Kimi Design API Reference

The public API of the TypeScript packages and the C++ renderer, generated from the source at e036c066cb. Entries marked Native link a TypeScript method to the C function it calls through Wasm; C functions link back with Called from TS and forward to the C++ code that implements them.

@taco-slides/model: 556 of 633 documented · @taco-slides/editor: 308 of 474 documented · @taco-slides/render-core: 196 of 270 documented · @taco-design/electron: 57 of 58 documented · C++ renderer: 157 of 310 documented

@taco-slides/model TypeScript

556 of 633 exports documented · outline

The model package is the framework-neutral source of truth for a Kimi Design document. It defines the document types, loads and saves them, validates them, and records every edit as a list of operations with undo history. It contains no editor, renderer, or network code.

A document is normalized into flat maps keyed by ID: pageOrder, pages, nodes, and assets, plus a theme. Each page points to one root frame or group that never clips, so the page is an unbounded canvas. Containers own an ordered children list. Parent pointers are not stored, so the hierarchy has one source of truth. assertDocument rejects missing nodes, cycles, shared children, unreachable nodes, broken references, and malformed geometry.

Values returned by this package are deeply frozen. To change a document, pass DocumentOperations to applyOperations, or dispatch them to a DocumentStore, which also keeps the commit history used for undo, redo and branches.

Current versions

The versions this package writes today, read from the source when the page is built.

Document schema0.27.0CURRENT_DOCUMENT_SCHEMA_VERSION; see the schema history
Stored history1CURRENT_STORE_SCHEMA_VERSION

Documents and pages

A DesignDocument holds every page, node, asset and theme resource. Pages are ordered by pageOrder, and each page names its root node.

type DesignDocument = DesignDocumentV1

source

A Kimi Design document. Values returned by this package are deeply frozen; change one with applyOperations or through a DocumentStore.

interface DesignDocumentV1

source

The stored shape of a document at CURRENT_DOCUMENT_SCHEMA_VERSION. Use the DesignDocument alias in code.

The data is normalized into flat maps keyed by ID. assertDocument checks that every page root exists, every node is reachable from exactly one page and has at most one parent, there are no cycles, and that style, variable, connector, media-node and font references resolve. (ImagePaint.assetId is not checked against the asset map.)

readonly schemaVersion: typeof CURRENT_DOCUMENT_SCHEMA_VERSIONsource
readonly id: DocumentIdsource
readonly title: stringsource
readonly pageOrder: readonly PageId[]source

Every page ID exactly once, in display order.

readonly pages: Readonly<Record<string, Page>>source
readonly nodes: Readonly<Record<string, SceneNode>>source

Every node on every page, keyed by NodeId.

readonly assets: Readonly<Record<string, Asset>>source

Images, videos and fonts, keyed by AssetId.

readonly theme: DocumentThemesource
readonly components?: Readonly<Record<string, ComponentDefinition>>since 0.23.0source

Reusable layer trees, keyed by ComponentId. Omitted when empty. Each component’s layers live in nodes under its rootId, on no page.

readonly metadata?: Readonly<Record<string, JsonValue>>source

Free-form JSON data kept with the document. The model does not read it.

interface Page

source

One page of the document. A page is an unbounded canvas: its root container never clips, so top-level nodes may lie anywhere, and the root’s frame only records the visible extent (see pageRootFrame).

readonly id: PageIdsource

Matches the page’s key in DesignDocument.pages.

readonly name: stringsource

Must not be empty.

readonly rootId: NodeIdsource

The page’s root node: a frame or group with no parent.

readonly guides?: readonly Guide[]since 0.11.0source

Ruler guides on the page’s canvas, in page coordinates. Omitted when empty.

readonly variableModes?: VariableModessince 0.26.0source

The page’s mode per variable collection. Layers set to Auto inherit it; a collection left out uses the collection’s activeModeId.

readonly metadata?: Readonly<Record<string, JsonValue>>source

interface Guide

since 0.11.0source

A ruler guide: an editor-only straight line used to position objects. The renderer never paints it, so it never appears in exports.

readonly axis: "x" | "y"source

“x” is a vertical line at x = offset; “y” is a horizontal line at y = offset.

readonly offset: numbersource

Position along the axis in document units, relative to whatever holds the guide: the page origin for a page guide, or the frame’s top-left corner for a frame guide.

type DocumentSchemaVersion = number | SemanticVersion

source

A document schema version. Current documents use a SemanticVersion; the number form is only read from legacy files saved before version 0.0.0.

type SemanticVersion = `${number}.${number}.${number}`

source

A major.minor.patch version string, such as "0.14.0".

const CURRENT_DOCUMENT_SCHEMA_VERSION: "0.27.0"

source

The schema version this package reads and writes. deserializeDocument migrates older documents to it, and assertDocument rejects any other version.

function pageRootFrame( document: DesignDocument, pageId: PageId ): Rect | undefined

source

A page root’s frame is the page’s visible extent: the area the editor fits on screen and the renderer sizes its scene to. Page roots never clip, so top-level nodes may lie outside it.

Scene nodes

SceneNode is a union told apart by type: frame, group, text, code, shape, image, video and connector. Shapes are further told apart by shape. Every node has the fields of NodeBase; every node but a path is also placed by a frame and rotation (FramedNodeBase).

type SceneNode = | FrameNode | GroupNode | TextNode | CodeNode | ShapeNode | ImageNode | VideoNode | ConnectorNode | InstanceNode

source

Any node in a page’s tree, told apart by its type field. Nodes live in the flat DesignDocument.nodes map; containers list their children by ID and parents are not stored.

interface NodeBase

source

Fields every SceneNode has.

readonly id: NodeIdsource

Matches the node’s key in DesignDocument.nodes.

readonly name: stringsource

The layer name. Must not be empty.

readonly visible: booleansource
readonly locked: booleansource

Protects the node from edits on the editor canvas.

readonly opacity: numbersource

From 0 (transparent) to 1 (opaque).

readonly blendMode?: BlendModesince 0.8.0source

How the node composites onto what lies behind it; normal when omitted.

readonly mask?: booleansince 0.8.0source

A mask hides its later siblings outside its own painted alpha and paints nothing itself. A hidden mask masks nothing.

readonly aspectRatioLocked?: booleansince 0.1.1source

Keeps width and height proportional when the node is resized.

readonly variableBindings?: VariableBindingssource

Properties whose values come from theme variables.

readonly exportSettings?: readonly ExportSetting[]since 0.14.0source

The node’s Export section rows, in order. Omitted when empty.

readonly expressions?: NodeExpressionssince 0.24.0source

Computed fields: expression source text keyed by field, for nodes under a component root only (see ComputableField and computableFieldsOf). The plain field keeps the base value, which the expression reads as stored and which is shown when the expression does not check. A field holds a variable binding or an expression, never both. Omitted when empty.

readonly condition?: stringsince 0.24.0source

A boolean expression; when it is false the layer and its sublayers are left out of every instance. Only on layers under a component root, never on the root itself. An expression that does not check counts as no condition.

readonly constraints?: ResizeConstraintssince 0.25.0source

How the layer follows its frame when the frame is resized (see ResizeConstraints). Omitted means Left and Top: the layer keeps its position and size. Used for children of frames without auto layout and for absolute children of auto layout frames; ignored elsewhere. Never on a group: constraints set on a group are set on its layers, as in Figma.

readonly layoutPositioning?: "absolute"since 0.25.0source

"absolute" takes a child of an auto layout frame out of the flow, like Figma’s “Ignore auto layout”: the layout neither moves it nor counts it when hugging, and it follows the frame’s size changes by its constraints. Ignored when the parent has no auto layout.

readonly variableModes?: VariableModessince 0.26.0source

The mode this layer and its sublayers use for each variable collection (see VariableModes). A collection left out is Auto: the layer inherits the mode from its parent. On an instance, the modes apply to the component’s layers, so two instances of one component can show two modes.

readonly metadata?: Readonly<Record<string, JsonValue>>source

Free-form JSON data kept with the node. The model does not read it.

interface FramedNodeBase extends NodeBase

source

Fields of every node placed by a rectangle: all kinds except PathNode, which uses a transform matrix instead.

readonly frame: Rectsource

Position and unrotated size, in the parent’s coordinate system (relative to the parent’s frame’s top-left corner).

readonly rotation: numbersource

Degrees, clockwise on screen, about the frame’s center.

readonly flipX?: booleansource

Mirrors the rendered node around its local horizontal center.

readonly flipY?: booleansource

Mirrors the rendered node around its local vertical center.

Together, frame, rotation and the flips place a local point p\mathbf{p} (origin at the frame’s top-left corner) in the parent as

pparent=[x+w/2y+h/2]+RθF(p−[w/2h/2]),Rθ=[cos⁡θ−sin⁡θsin⁡θcos⁡θ],F=[±100±1]\mathbf{p}_\text{parent} = \begin{bmatrix} x + w/2 \\ y + h/2 \end{bmatrix} + R_\theta F \left(\mathbf{p} - \begin{bmatrix} w/2 \\ h/2 \end{bmatrix}\right), \quad R_\theta = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix}, \quad F = \begin{bmatrix} \pm 1 & 0 \\ 0 & \pm 1 \end{bmatrix}

where θ\theta is rotation in radians and FF has −1-1 on the axis of each flip that is set. The flip happens first, then the rotation.

readonly resizing?: NodeResizingsince 0.15.0source

Fill sizing and size limits. Omitted when empty.

type ContainerNode = FrameNode | GroupNode

source

Nodes that have children. Test with isContainerNode.

const isContainerNode: (node: SceneNode) => node is ContainerNode

source

True for frames and groups, the nodes that have children.

interface FrameNode extends FramedNodeBase

source

A container with its own size, fills, strokes and effects, and optionally an auto layout. Each page’s root is a frame or group; a page root never clips.

readonly type: "frame"source
readonly children: readonly NodeId[]source

Child node IDs in paint order: the first child is at the back.

readonly layout: Layoutsource

How children are positioned: freely, or by a stack or grid auto layout.

readonly style?: VisualStylesource
readonly clipContent?: booleansource

Hides the parts of children that fall outside the frame.

readonly guides?: readonly Guide[]since 0.11.0source

Ruler guides that belong to this frame and move with it. The editor shows them only on top-level frames that are neither rotated nor flipped. Omitted when empty.

interface GroupNode extends FramedNodeBase

source

A container with no size of its own: its frame is always fitted to the union of its children’s bounds (see fitGroupToChildren). A group whose last child is removed by applyOperations is removed too.

readonly type: "group"source
readonly variableBindings?: GroupVariableBindingssource

Properties whose values come from theme variables. A group’s size is computed from its layers, so it cannot bind width or height.

readonly children: readonly NodeId[]source

Child node IDs in paint order: the first child is at the back.

readonly clipContent?: booleansource

interface TextNode extends FramedNodeBase

source

A text box made of styled paragraphs.

readonly type: "text"source
readonly paragraphs: readonly TextParagraph[]source
readonly verticalAlign: "top" | "middle" | "bottom"source

Where the text sits in the frame when the frame is taller than the text.

readonly overflow: "clip" | "visible"source

Whether text that does not fit the frame is cut off or still drawn.

readonly autoResize: "none" | "height" | "width-and-height"source

none keeps both frame dimensions fixed, height keeps the width and lets the height follow the text, and width-and-height lets both follow.

readonly wrap: "none" | "word" | "character"source

Where long lines may break: never, between words, or between any characters.

type TextualNode = TextNode

source

Nodes that hold editable rich text. Currently only TextNode.

const isTextualNode: (node: SceneNode) => node is TextualNode

source

True for nodes that hold rich text (TextNode).

interface CodeNode extends FramedNodeBase

source

A block of source code. Syntax highlighting is worked out when rendering; only the raw source is stored.

readonly type: "code"source
readonly source: stringsource
readonly language: stringsource

The language identifier used for highlighting. Must not be empty.

readonly fontFamily: stringsource

Applied uniformly to the full source rather than to individual text runs.

readonly fontSize: numbersource

Applied uniformly to the full source rather than to individual text runs. Positive.

readonly codeStyleId?: CodeStyleIdsource

The theme code style; the theme’s defaultCodeStyleId when omitted.

readonly codeStyleOverrides?: Partial< Pick< CodeStyle, "lineHeight" | "letterSpacing" | "background" | "foreground" | "syntax" > >source

Values that replace the code style’s for this node only.

readonly lineNumbers: boolean | numbersource

False hides line numbers, true starts at one, and a number sets the first line.

readonly autoResize: "none" | "height" | "width-and-height"since 0.1.3source

Same meaning as TextNode.autoResize.

readonly wordWrap: booleansource

Wraps long source lines to the frame width.

readonly stroke?: Strokesince 0.1.4source

A stroke drawn around the code block.

readonly decorations: readonly CodeDecoration[]source

interface CodeDecoration

source

A marked range of a CodeNode’s source.

readonly from: numbersource

Start offset into source, in UTF-16 code units.

readonly to: numbersource

End offset (exclusive), from from up to the source length.

readonly kind: "emphasis" | "addition" | "deletion" | "annotation"source
readonly label?: stringsource

type ShapeNode = | RectangleNode | EllipseNode | PolygonNode | StarNode | LineNode | PathNode

source

Every node with type: "shape", told apart by its shape field.

interface RectangleNode extends FramedNodeBase

source

A rectangle filling its frame; VisualStyle.cornerRadius rounds it.

readonly type: "shape"source
readonly shape: "rectangle"source
readonly style: VisualStylesource

interface EllipseNode extends FramedNodeBase

source

An ellipse filling its frame, or an arc, pie or ring of one.

readonly type: "shape"source
readonly shape: "ellipse"source
readonly style: VisualStylesource
readonly arc?: EllipseArcsince 0.12.0source

Omitted draws the whole ellipse.

interface EllipseArc

since 0.12.0source

Turns an ellipse into an arc, pie or ring, as Figma’s arc data does. Angles are degrees measured clockwise from the positive x axis on the ellipse’s parametric angle. The sweep runs from the starting to the ending angle and is at most one full turn in either direction.

readonly startingAngle: numbersource
readonly endingAngle: numbersource
readonly innerRadius: numbersource

Inner radius as a fraction of the outer radius, from 0 (a pie) to below 1 (a ring).

interface PolygonNode extends FramedNodeBase

since 0.12.0source

A regular polygon whose points are scaled to fill the frame, first point at the top center, as Figma draws it.

readonly type: "shape"source
readonly shape: "polygon"source
readonly pointCount: numbersource

Whole number of corners from 3 to 60.

readonly style: VisualStylesource

interface StarNode extends FramedNodeBase

since 0.12.0source

A star whose outer and inner points are scaled to fill the frame.

readonly type: "shape"source
readonly shape: "star"source
readonly pointCount: numbersource

Whole number of outer points from 3 to 60.

readonly innerRadius: numbersource

Inner point distance as a fraction of the outer radius, from 0 to 1.

readonly style: VisualStylesource

interface LineNode extends FramedNodeBase

since 0.12.0source

A single straight stroke. Like Figma, the frame has zero height: the line runs from the frame’s local (0, 0) to (width, 0), and rotation turns it about the frame center. An arrow is a line with an arrow cap.

readonly type: "shape"source
readonly shape: "line"source
readonly style: VisualStylesource
readonly startCap: StrokeCapsource
readonly endCap: StrokeCapsource

interface PathNode extends NodeBase

source

An editable vector shape. Unlike the other shapes it has no frame or rotation: transform places the network in the parent, and its bounds are derived from the transformed network (see getNodeBounds).

readonly type: "shape"source
readonly shape: "path"source
readonly vectorNetwork: VectorNetworksince 0.2.0source

The path’s geometry.

readonly transform: TransformMatrixsource

Maps network coordinates into the parent’s coordinate system.

readonly style: VisualStylesource
readonly startCap?: StrokeCapsince 0.22.0source

The end points of an open path whose segments form one chain, each in order from the end of the one before (see openPathChainEnds): the start cap sits on the first segment’s start and the end cap on the last segment’s end, the same styles a line has. Omitted is round, which every path drew before 0.22.0. Closed paths and paths with branches or several chains ignore them, and so does a path with an inside or outside stroke.

readonly endCap?: StrokeCapsince 0.22.0source

interface ImageNode extends FramedNodeBase

source

A placed image. The pixels live in an ImageAsset.

readonly type: "image"source
readonly assetId: AssetIdsource

Must name an asset of kind image.

readonly fit: MediaFitsource
readonly crop?: Rectsource

The part of the source image to show, in source pixels.

readonly alt: stringsource

Alternative text for accessibility; may be empty.

interface VideoNode extends FramedNodeBase

source

A placed video. The file lives in a VideoAsset.

readonly type: "video"source
readonly assetId: AssetIdsource

Must name an asset of kind video.

readonly fit: MediaFitsource
readonly crop?: Rectsource

The part of the video frame to show, in source pixels.

readonly alt: stringsource

Alternative text for accessibility; may be empty.

type MediaFit = "contain" | "cover" | "fill"

source

How an image or video fills its frame, as in CSS object-fit.

interface ConnectorNode extends FramedNodeBase

source

A stroked line or arrow between two points, each optionally attached to a node.

readonly type: "connector"source
readonly start: ConnectorEndpointsource
readonly end: ConnectorEndpointsource
readonly routing: "straight" | "elbow" | "curve"source
readonly stroke: Strokesource
readonly startCap: StrokeCapsince 0.20.0source

The end styles, the same set a line has (STROKE_CAPS). Before 0.20.0 a connector had only "none", "arrow" and "dot"; loading an older document turns "arrow" into "triangle-arrow" and "dot" into "circle-arrow".

readonly endCap: StrokeCapsource
readonly cornerRadius?: numbersince 0.21.0source

How far, in pixels, each bend of an elbow connector is rounded, as Figma’s ConnectorNode.cornerRadius. A bend never rounds past half of either part of the line it joins, so a short part keeps a smaller curve. The ends and end styles are not rounded. Straight and curved connectors have no bends and ignore it. Missing means 0: sharp bends. Not negative.

readonly cornerSmoothing?: numbersince 0.21.0source

How far each rounded bend of an elbow connector eases into the line, from 0 (a circular arc) to 1 (a continuous curve), the same fraction as a rectangle’s NodeVisualStyle.cornerSmoothing and drawn the same way. A smoothed bend takes up to (1 + cornerSmoothing) * cornerRadius of each part of the line it joins, capped at half of the shorter part, which also caps the smoothing. It has no effect without a corner radius, or on straight and curved connectors. Figma’s ConnectorNode has no smoothing; this extends it. Missing means 0.

interface ConnectorEndpoint

source

One end of a ConnectorNode.

An end with nodeId attaches to that layer, and the page compiler places it from the layer’s frame each time, so it follows the layer when the layer moves or resizes. Where on the layer it goes is set by at most one of:

  • side: the middle of that side, and an elbow connector leaves the layer at a right angle to it. Figma calls this a magnet (TOP, RIGHT, BOTTOM or LEFT in the plugin API’s ConnectorEndpointConnectedWithMagnet). On an ellipse, polygon or star the end is where the shape’s outline meets the line from its centre toward that side, so it sits on the drawn shape.
  • anchor: a corner or edge middle of a polygon, star or ellipse arc (ConnectorAnchor); the end leaves along the outline’s outward direction there.
  • point: a fixed point on the layer, as FigJam places an end dropped with Command or Ctrl held (ConnectorEndpointConnectedWithPosition). Figma stores it in the layer’s pixels; here it is a fraction of the layer’s width and height, so the point keeps its place when the layer resizes.

With none of them (Figma’s AUTO magnet), the end goes where the layer’s outline faces the other end. x and y are the end’s position in the connector’s frame; for an attached end they record where it was last placed and are replaced when the connector is drawn.

readonly nodeId?: NodeIdsource

The node this end attaches to. It must be on the same page as the connector.

readonly x: numbersource
readonly y: numbersource
readonly side?: ConnectorSidesince 0.20.0source

The side of the attached layer this end is pinned to. Needs nodeId, and cannot be set together with point or anchor.

readonly anchor?: ConnectorAnchorsince 0.20.0source

The key point on the attached shape’s outline this end is pinned to. Needs nodeId, and cannot be set together with side or point.

readonly point?: { readonly x: number; readonly y: number }since 0.20.0source

The point on the attached layer this end is fixed to, as fractions of the layer’s width (x) and height (y) from its top-left corner: { x: 1, y: 0.5 } is the middle of the right side. Each fraction is between 0 and 1. Needs nodeId, and cannot be set together with side or anchor.

type ConnectorSide = "top" | "right" | "bottom" | "left"

since 0.20.0source

A side of a layer that an attached connector end is pinned to.

interface ConnectorAnchor

since 0.20.0source

A key point on a shape’s outline that an attached connector end is pinned to, found from the shape’s geometry each time the connector is drawn, so the end follows the shape when it moves, resizes or changes its points.

  • vertex: corner index of the outline, counted as polygonPoints and starPoints list them (clockwise from the top point; a star’s outer tips are the even indices). On an ellipse arc, vertex 0 is where the arc starts and vertex 1 where it ends, on the outer rim.
  • edge: the middle of the outline’s edge from vertex index to the next.

An index the shape does not have (for example after a polygon loses points), or an anchor on a layer without such points, leaves the end automatic, as if it had no anchor.

readonly kind: "vertex" | "edge"source
readonly index: numbersource

A whole number from 0.

type BlendMode = (typeof BLEND_MODES)[number]

source

const BLEND_MODES: readonly [ "normal", "darken", "multiply", "color-burn", "lighten", "screen", "color-dodge", "overlay", "soft-light", "hard-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity" ]

since 0.8.0source

Separable and non-separable compositing modes, in CSS mix-blend-mode terms.

Frames can position their children with an auto layout instead of by hand. A stack lays its children out in a row or a column and can wrap them onto more lines; a grid places them in equal columns. The frame chooses Fixed or Hug on each axis, while each child chooses whether to fill the space its parent gives it, and the size limits that apply, in NodeResizing. These follow Figma’s auto layout, including its rule that a frame stops hugging an axis that one of its stack children fills.

type Layout = AbsoluteLayout | AutoLayout

source

interface AbsoluteLayout

source
readonly kind: "absolute"source

type AutoLayout = StackAutoLayout | GridAutoLayout

source

interface StackAutoLayout extends AutoLayoutConstraints

source

A row ("horizontal") or column ("vertical") auto layout. The main axis runs along the stack; the cross axis runs across it.

readonly kind: "vertical" | "horizontal"source
readonly gap: numbersource

Space between children on the main axis.

readonly padding: LayoutPaddingsource
readonly align: "start" | "center" | "end"source

Where children sit on the cross axis within their line. A child stretches across the line with a cross-axis Fill in FramedNodeBase.resizing. Schema 0.15.0 removed the frame-wide "stretch".

readonly justify: | "start" | "center" | "end" | "space-between" | "space-around" | "space-evenly"source

How children are spread on the main axis within each line. The space-* values replace gap with an automatic gap that is never negative: space-between puts all free space between children (a single child sits at the start), space-around puts half a gap before the first and after the last child, and space-evenly puts a whole gap there (both centre a single child). When children overflow, all three pack from the start. Schema 0.15.0 added "space-around" and "space-evenly".

readonly wrap?: booleansince 0.15.0source

Moves children that do not fit onto new lines: rows for a horizontal stack, columns for a vertical one. Omitted means a single line.

readonly wrapGap?: numbersince 0.15.0source

Space between wrapped lines. Omitted means the same as gap. Kept, but unused, while wrap is off.

readonly alignContent?: "auto" | "space-between"since 0.15.0source

How wrapped lines share the cross axis. "auto" (the default) packs the lines by align, or shares the free space equally between lines when every child fills the cross axis; "space-between" spreads the lines with an automatic gap that is never negative. Kept, but unused, while wrap is off.

interface GridAutoLayout extends AutoLayoutConstraints

source
readonly kind: "grid"source
readonly columns: numbersource
readonly columnGap: numbersource
readonly rowGap: numbersource
readonly padding: LayoutPaddingsource

interface AutoLayoutConstraints

source

The frame’s own sizing. Size limits moved to FramedNodeBase.resizing in 0.15.0.

readonly widthSizing: LayoutSizingsource

"hug" sizes the frame to its content on this axis. In a stack, a child that fills the same axis makes the frame keep its stored size instead, as Figma stops hugging in that case. A grid keeps hugging: its Fill children take their cell, whose size comes from the other children.

readonly heightSizing: LayoutSizingsource

interface LayoutPadding

source
readonly top: numbersource
readonly right: numbersource
readonly bottom: numbersource
readonly left: numbersource

type LayoutSizing = "fixed" | "hug"

source

How an auto layout frame sizes itself on one axis: keep its stored size, or hug its content. Filling the parent is set on the child instead, with FramedNodeBase.resizing.

interface NodeResizing

since 0.15.0source

How auto layout may size a node beyond its stored frame.

width or height set to "fill" makes a child of a stack or grid auto layout take the space its parent gives it on that axis: the Fill children of one line share the line’s free main-axis space equally, a cross-axis Fill takes the line’s cross size, and a grid child fills its cell. The flag has no effect outside an auto layout, on rotated nodes, on groups and on connectors. A node cannot fill an axis on which it hugs its own content (an auto layout frame’s "hug" sizing, or a text or code node’s autoResize).

The limits bound the size auto layout gives the node: a Fill child’s share, and an auto layout frame’s own Hug or Fixed size.

readonly width?: "fill"source
readonly height?: "fill"source
readonly minWidth?: numbersource
readonly maxWidth?: numbersource
readonly minHeight?: numbersource
readonly maxHeight?: numbersource

A few rules are shared with the editor’s drop slots so both break a wrapping row or column at the same children. stackLineBreaks splits the children into lines from the sizes stackLineBreakSize gives; fillsAxis and canFillParent say when a child takes a Fill size.

type LayoutAxis = "width" | "height"

source

A size axis of a node: its width or its height.

function nodeResizing(node: SceneNode): NodeResizing | undefined

source

The node’s Fill flags and size limits, or undefined for a path.

function canFillParent(node: SceneNode): boolean

source

Whether auto layout may give node a Fill size: its box must be its frame (no path, no rotation), and it must not be a group or connector, whose size comes from other nodes.

function fillsAxis(node: SceneNode, axis: LayoutAxis): boolean

source

Whether node fills its auto layout parent on axis.

function stackLineBreakSize(node: SceneNode, mainAxis: LayoutAxis): number

source

The main-axis size a stack child takes when lines are broken: its box size, or its minimum size when it fills the main axis.

function stackLineBreaks( layout: Pick<StackAutoLayout, "gap" | "justify" | "wrap">, availableMain: number, sizes: readonly number[] ): number[][]

source

Breaks a row or column into lines, as Figma’s wrap does: children stay in order and a child starts a new line when it would overflow availableMain (the frame’s main size without padding). An automatic gap (space between, around or evenly) counts as zero here. Without wrap there is one line.

sizes are the children’s main sizes from stackLineBreakSize. Returns the children’s indexes, grouped by line; no children gives no lines.

The layout itself runs whenever the editor edits the document: withAutoLayoutOperations adds the node replacements that keep every affected auto layout frame laid out. layoutFrameTree lays out one frame and the frames inside it, and applyFrameLayout places one frame’s children. createFrameLayout makes a new layout of a given kind.

function withAutoLayoutOperations( document: DesignDocument, operations: readonly DocumentOperation[] ): readonly DocumentOperation[]

source

operations followed by the node replacements that keep auto layout frames laid out after them: frames the operations touch are laid out again, deepest first, and nodes the operations leave filling a parent without auto layout become Fixed. In a document with components, a text whose content is computed is first sized for the text it shows (fitComputedTextContent), and the component cascade then runs (cascadeComponentChanges): instances of changed components are refitted to their hugging size, their dead override keys dropped, and their ancestors laid out again, nested components first. The operations come back unchanged when nothing needs it.

function layoutFrameTree( nodes: Record<string, SceneNode>, frameId: NodeId, changed: Set<NodeId> = new Set() ): Set<NodeId>

source

Lays out the auto layout frame frameId in nodes (changed in place), then lays out again each child frame whose size it changed, so Fill sizes reach nested auto layouts, and each auto layout frame that constraints resized. Returns the IDs of every node it changed. The caller lays out hugging descendants first, deepest first, so their sizes are known.

function applyFrameLayout( frame: FrameNode, childNodes: readonly SceneNode[], layout: Layout ): AppliedFrameLayout

source

Lays out one frame’s direct children with layout and returns the frame with its new size and the moved or resized children. Child frames are not laid out again here; layoutFrameTree does that.

interface AppliedFrameLayout

source

A frame and its children after applyFrameLayout has placed them.

readonly frame: FrameNodesource
readonly children: ReadonlyMap<NodeId, SceneNode>source

function createFrameLayout( kind: Layout["kind"], gap = DEFAULT_AUTO_LAYOUT_GAP ): Layout

source

A new layout of the given kind. Auto layouts start with a fixed width, a hugged height, no padding and gap between children (a grid uses it for both its column and row gaps).

function frameLayoutGap(layout: Layout): number

source

The gap a layout keeps between children: a stack’s gap or a grid’s column gap. Without an auto layout it is DEFAULT_AUTO_LAYOUT_GAP.

const DEFAULT_AUTO_LAYOUT_GAP: 24

source

The gap, in pixels, a new auto layout starts with.

Resize constraints say how a layer follows its frame when the frame is resized, with Figma’s five options on each axis: Left, Right, Left and right, Center and Scale horizontally, and their vertical counterparts. They apply to the children of frames without auto layout and to the absolute children of auto layout frames (layoutPositioning: "absolute", Figma’s “Ignore auto layout”), which the layout leaves out of its flow. constrainChildren places a frame’s children for a change of its size; the editor’s frame resize, the selection box and group resizing all use it, and so does layoutFrameTree when a layout changes a frame’s size. scaleLayerInParent scales one layer the way a resized box does.

interface ResizeConstraints

since 0.25.0source

A layer’s resize constraints, Figma’s five options on each axis. They act on the layer’s axis-aligned bounds in its frame (see constrainChildren).

readonly horizontal: HorizontalConstraintsource
readonly vertical: VerticalConstraintsource

type HorizontalConstraint = "left" | "right" | "left-right" | "center" | "scale"

since 0.25.0source

How a layer’s horizontal position and width follow its frame’s width. "left", "right" and "center" keep the width and the distance to the left edge, the right edge or the center line; "left-right" keeps both distances, so the width changes with the frame; "scale" keeps the layer’s position and width as shares of the frame’s width.

type VerticalConstraint = "top" | "bottom" | "top-bottom" | "center" | "scale"

since 0.25.0source

How a layer’s vertical position and height follow its frame’s height; the vertical counterpart of HorizontalConstraint.

const DEFAULT_RESIZE_CONSTRAINTS: ResizeConstraints

since 0.25.0source

The constraints a layer has when it stores none: Left and Top, Figma’s default, which keeps the layer’s position and size.

function nodeConstraints(node: SceneNode): ResizeConstraints

since 0.25.0source

A layer’s constraints, with DEFAULT_RESIZE_CONSTRAINTS when it stores none.

function followsConstraints(parent: FrameNode, child: SceneNode): boolean

since 0.25.0source

Whether a child of parent is placed by its constraints when parent resizes: every child of a frame without auto layout, and only the absolute children (layoutPositioning: "absolute") of an auto layout frame, whose other children the layout places.

function constrainChildren( nodes: NodeRecord, parentId: NodeId, from: FrameSize, to: FrameSize ): readonly SceneNode[]

since 0.25.0source

Places each child of the frame parentId for a resize of that frame from from to to, by the child’s constraints (NodeBase.constraints), as Figma does when a frame is resized. nodes holds the frame and its descendants before the change; the frame’s own record is only read for its children and layout, so it may hold either size.

  • Constraints act on each child’s axis-aligned bounds in the frame. A child whose bounds change size is resized like a layer in a resized selection box (scaleLayerInParent): a rotated or flipped layer keeps its rotation and flips, and a frame’s own children follow by their constraints in turn.
  • An auto layout frame’s flow children are left to its layout; only its absolute children move (followsConstraints). A child auto layout frame whose size changes is not laid out again here: the caller does that, as layoutFrameTree or the editor’s auto layout step do.
  • A group has no constraints of its own. The layers of an upright group (not rotated or flipped) are placed one by one by their own constraints relative to the frame, and the group is fitted to them again. A rotated or flipped group moves and resizes as one layer, by the option its layers share on each axis, or Left or Top when they differ.

Returns every changed node, children before descendants, each at most once. An unchanged size returns an empty list.

The instance resolver calls this to lay out a resized instance whose component root has no auto layout: constrainChildren(nodes, rootId, componentRoot.frame, instance.frame), writing the result into its node record before it lays out auto layout frames with layoutFrameTree.

interface FrameSize

since 0.25.0source

A frame’s size before or after a resize. Constraints only need sizes: children are placed relative to their frame’s top-left corner, so where the frame moves on the page does not matter.

readonly width: numbersource
readonly height: numbersource

function scaleLayerInParent( nodes: NodeRecord, node: SceneNode, scaleX: number, scaleY: number ): readonly SceneNode[]

since 0.25.0source

Scales a layer’s position and size by (scaleX, scaleY) about its parent’s origin, in the parent’s coordinates, the way resizing a group or a selection box moves the layers inside it. Positions, sizes and vector geometry scale; stroke widths, corner radii, effects and font sizes do not.

  • A rotated layer keeps its rotation and takes the size whose bounds are its old bounds scaled, when the rotation allows it (see the source of rotatedSize for the fallback near 45 degrees).
  • A group’s descendants scale with it.
  • A frame’s box scales and its children follow by their constraints (constrainChildren); an auto layout places its other children later.
  • Text or code that grew with its content gets a fixed size.

Returns the layer followed by every changed descendant. nodes must hold the layer’s descendants.

The sizing helpers read and change a node’s Fixed, Hug or Fill sizing on each axis, keeping the rules of Figma’s resizing menu: only auto layout frames, instances of a component with auto layout, text and code can hug, and only children of an auto layout frame can fill. nodeAxisSizing is the one reader and withAxisSizing the one writer of Fixed and Hug: an instance’s sizing is its override of its component root’s sizing, so both take the document. withManualSize is the one place a size changed by hand (a resize, the Scale tool, a size variable) gets the sizing that keeps it: commands and drag previews pass their node through it.

type AxisSizing = "fixed" | "hug" | "fill"

source

How a node is sized on one axis, as the width and height menus show it (Figma’s resizing menu): a stored size, its content’s size, or the space its auto layout parent gives it.

type SizingAxis = "width" | "height"

source

The axis a sizing applies to.

function nodeAxisSizing( document: DesignDocument, node: SceneNode, axis: SizingAxis ): AxisSizing

source

The node’s sizing on an axis: the one reader of it. Nodes that cannot hug or fill are Fixed. An auto layout frame’s is its layout’s; text and code read their automatic sizing. An instance has no layout of its own: its sizing is its override of its component root’s sizing (LayerSizingOverride, stored under the root’s path), and the root’s own sizing where it has none, which is what the instance resolver lays it out with. Fill is the node’s own resizing for every kind of node.

function canHugAxis(document: DesignDocument, node: SceneNode): boolean

source

Auto layout frames hug their children, text and code blocks hug their content, and an instance hugs as its component root does: only when the root has auto layout.

function canFillAxis( document: DesignDocument, node: SceneNode, axis: SizingAxis, parent: SceneNode | undefined = parentNodeOf(document, node.id) ): boolean

source

Whether the node can fill its parent on an axis: it must be a child of an auto layout frame, and not a path, group or connector, a rotated node, or (for the height) a line, whose height is always zero. Figma offers Fill container only on children of auto layout frames.

function withoutFill(node: SceneNode, axis: SizingAxis): SceneNode

source

The node without a Fill flag on the axis.

function withoutAnyFill(node: SceneNode): SceneNode

source

The node without any Fill flag, keeping its size limits.

function withAxisSizing( document: DesignDocument, node: SceneNode, axis: SizingAxis, sizing: "fixed" | "hug" ): SceneNode

source

The node with its sizing on axis set to Fixed or Hug: the one writer of it, the counterpart of nodeAxisSizing. An auto layout frame stores it in its layout; an instance stores it as its override of its component root’s sizing. A node that has neither (a shape, a frame without auto layout, an instance of a component without auto layout) is returned as it is, and so is text or code, whose automatic sizing has rules of its own (sizingReplacements). Fill is not touched: see withoutFill.

type ManualSizeChange = "resize" | "scale" | "variable"

source

How a layer’s size was changed by hand, as opposed to by a layout, a constraint or its content:

  • "resize": a resize handle, a typed W or H, the box around a selection, a moved end of a line.
  • "scale": the Scale tool, on one layer or on the box around a selection.
  • "variable": a size variable bound to W or H.

function withManualSize( document: DesignDocument, before: SceneNode, after: SceneNode, change: ManualSizeChange, axes: readonly SizingAxis[] = changedSizeAxes(before, after) ): SceneNode

source

after, the node before with a size changed by hand, with the sizing that change leaves: the one place the rule “a size set by hand is kept” is written. Every command that changes a size by hand, and every preview of one, passes its node through here, so the release commits what the drag drew and the next layout does not undo it.

  • A resize, on each axis whose size changed: Fill and Hug become Fixed (an instance’s Hug too) and a size variable on the axis is dropped.
  • The Scale tool, on each axis whose size changed: an instance’s Hug becomes Fixed, since its layers are its component’s and do not scale. A frame keeps Hug: its children scaled with it, so it hugs the scaled size. Fill and a size variable are left as they are.
  • A size variable: Fill and Hug become Fixed on axes, which the caller names because the variable’s value may equal the size the node has.

A size that a layout, a constraint or the node’s content gives is not a change by hand and does not come here: the sizing stays.

function withManualSizes( document: DesignDocument, nodes: readonly SceneNode[], change: ManualSizeChange, only?: ReadonlySet<NodeId> ): readonly SceneNode[]

source

nodes, the output of a resize or a scale of layers of document, each passed through withManualSize against the node it replaces. only limits the rule to the layers the user changed, where the others were only carried along (the descendants of a resized group or selection).

function withoutStretchWhereHugging( document: DesignDocument, node: SceneNode ): SceneNode

source

The node without a stretching constraint on an axis it hugs: what the rule “no node hugs and stretches on the same axis” does to one node. Commands do not call it; stretchWhereHuggingReplacements applies the rule to every edit, on the axes the edit brings to break it.

function stretchWhereHuggingReplacements( previous: DesignDocument, next: DesignDocument, changedNodeIds: ReadonlySet<NodeId> ): readonly SceneNode[]

source

The one place the rule “no node has a stretching constraint (Left and right, Top and bottom, Scale) on an axis whose sizing is Hug” is enforced. applyOperations calls it for every batch of operations, so every command, preview, paste, undo-able edit and replayed commit passes through it and no command applies the rule itself.

next is previous after the batch and changedNodeIds the nodes the batch wrote. The nodes looked at are the ones whose sizing or constraints can have changed:

  • every changed node that still exists (its own layout, automatic text sizing, overrides, component or constraints may be new);
  • every instance, at any depth and on any page or inside any component, of a component whose root or root sizing changed. An instance with a sizing override on the axis keeps its own sizing, so only the ones that follow the component can lose a constraint.

Only a break of the rule that the batch brings is removed: an axis of a node that hugs and stretches in next and did not in previous (the node is new, or it kept the rule on that axis there). A node that already hugged and stretched on an axis in previous, as a document stored before the rule existed can hold, is left on that axis exactly as the batch leaves it, so a batch that does not touch the break, or keeps it, can be reversed exactly.

Returned are those nodes, each with the stretching constraint turned into Left or Top on the axes that newly break the rule, in a stable order (the batch’s own, then the document’s). The sizing is never changed here, so one pass is enough. The cost is two sizing reads per changed node, two more for one that breaks the rule, and one comparison per component; the walk over every node of the document happens only for a batch that changed a component root’s sizing.

function sizingReplacements( document: DesignDocument, nodeIds: readonly NodeId[], axis: SizingAxis, sizing: AxisSizing ): ReadonlyMap<NodeId, SceneNode>

source

The nodes that change when nodeIds are set to sizing on axis, by id. Nodes that cannot take that sizing are left out.

An instance follows every rule here as an auto layout frame does, through nodeAxisSizing and withAxisSizing, except the last part of the Hug rule: its layers are its component’s, so their Fill is not changed.

  • Fill turns the node’s own Hug on the axis into Fixed (text and code stop sizing that axis to their content), drops a size variable on the axis, and turns a hugging stack parent Fixed on the axis, as Figma does: “If any child objects within an auto layout frame are set to Fill container, the parent frame will no longer hug contents and become Fixed for the axis.”
  • Hug on an auto layout frame turns its stack children that fill the axis into Fixed. A grid keeps its Fill children: they take their cell.
  • Fixed and Hug drop the node’s own Fill on the axis.

Hug leaves a stretching constraint on the axis for applyOperations to turn into Left or Top, as it does after every edit (stretchWhereHuggingReplacements).

function resizingReplacements( document: DesignDocument, nodeId: NodeId, resizing: NodeResizing | undefined ): ReadonlyMap<NodeId, SceneNode>

source

The nodes that change when a node’s Fill flags and size limits are replaced with resizing (undefined removes them all). A newly set Fill follows the same rules as choosing Fill container in the sizing menu.

function isAutoLayoutFrame(node: SceneNode | undefined): node is AutoLayoutFrame

source

Whether the node is a frame with a stack or grid auto layout.

function parentNodeOf( document: DesignDocument, nodeId: NodeId ): SceneNode | undefined

source

The container that holds the node, or undefined for a page’s top level node.

function documentWithNodes( document: DesignDocument, nodes: ReadonlyMap<NodeId, SceneNode> ): DesignDocument

source

The document with nodes replacing their stored versions.

Paints, strokes and styles

Fills and strokes take a Paint: a solid color, a gradient, or an image. Every paint can be hidden (visible: false) and can blend with the paints below it on the same layer (blendMode), as in Figma; see PaintVisibility. VisualStyle collects a node’s fills, strokes, corners and effects. Rectangles and frames can round each corner on its own with CornerRadii; styleCornerRadii reads the four radii a style paints with. A stroke can likewise give each side its own weight with StrokeSides; strokeSideWeights reads the four weights a stroke paints with.

interface VisualStyle

source
readonly styleId?: NodeStyleIdsource
readonly fills?: readonly Paint[]source
readonly strokes?: readonly Stroke[]since 0.5.0source

Ordered strokes painted after the fills, first entry at the bottom.

readonly cornerRadius?: numbersource

One radius for every corner. Rectangles and frames use cornerRadii instead when it is present; polygons, stars and paths round every vertex with it.

readonly cornerRadii?: CornerRadiisince 0.16.0source

Independent corner radii for rectangles and frames. When present it replaces cornerRadius on those nodes; other shapes ignore it. The editor clears cornerRadius when it writes this field, so one of the two describes the corners.

readonly cornerSmoothing?: numbersince 0.9.0source

Corner smoothing from 0 (circular corners) to 1 (fully continuous curvature), as in Figma. It only changes corners that have a radius, and each smoothed corner consumes up to (1 + smoothing) * radius of edge.

For a rectangle, the renderer first limits the radius rr and smoothing ss to the budget B=12min⁡(w,h)B = \tfrac12 \min(w, h), then works out how much of each edge the corner uses:

r′=min⁡(r,B),s′=min⁡(s,max⁡(0,B/r′−1)),p=min⁡((1+s′) r′, B)r' = \min(r, B), \qquad s' = \min\bigl(s, \max(0, B / r' - 1)\bigr), \qquad p = \min\bigl((1 + s')\,r',\ B\bigr)

Walking into the corner, a cubic curve eases off the edge, a circular arc of radius r′r' turns through 90(1−s′)90(1 - s') degrees, and a mirrored cubic eases onto the next edge. With β=45s′\beta = 45 s' degrees, the arc covers ℓ\ell along each edge and the cubic control distances are

ℓ=2 r′sin⁡ ⁣(90(1−s′)2),c=r′tan⁡ ⁣(β2)cos⁡β,d=ctan⁡β,b=p−ℓ−c−d3,a=2b\ell = \sqrt{2}\, r' \sin\!\Bigl(\frac{90(1 - s')}{2}\Bigr), \quad c = r' \tan\!\Bigl(\frac{\beta}{2}\Bigr) \cos\beta, \quad d = c \tan\beta, \quad b = \frac{p - \ell - c - d}{3}, \quad a = 2b

so a+b+c+d+ℓ=pa + b + c + d + \ell = p. At s′=0s' = 0 this is a plain quarter circle.

readonly shadows?: readonly Shadow[]source
readonly layerBlur?: numbersince 0.7.0source

Gaussian blur radius applied to the node and its descendants as one layer.

readonly backgroundBlur?: numbersince 0.7.0source

Gaussian blur radius applied to whatever lies behind the node’s shape.

interface CornerRadii

since 0.16.0source

Separate radii for the four corners of a rectangle or frame, as in Figma’s topLeftRadius, topRightRadius, bottomRightRadius and bottomLeftRadius. Each is a non-negative number of units; the renderer limits each one to half the shorter side, the same limit as a uniform radius, so neighbouring corners never overlap.

readonly topLeft: numbersource
readonly topRight: numbersource
readonly bottomRight: numbersource
readonly bottomLeft: numbersource

const CORNER_KEYS: readonly ["topLeft", "topRight", "bottomRight", "bottomLeft"]

source

The four corners in Figma’s panel order: top left, top right, bottom right, bottom left.

type CornerKey = (typeof CORNER_KEYS)[number]

source

One corner of a rectangle or frame, as named in CornerRadii.

function styleCornerRadii( style: | Pick<VisualStyle, "cornerRadius" | "cornerRadii"> | undefined ): CornerRadii

source

The radii a rectangle or frame paints with: cornerRadii when present, otherwise the uniform cornerRadius (zero when absent) on every corner.

function uniformCornerRadii(radius: number): CornerRadii

source

Four equal radii.

function cornerRadiiAreUniform(radii: CornerRadii): boolean

source

True when all four corners have the same radius.

function cornerRadiiAreRounded(radii: CornerRadii): boolean

source

True when any corner is rounded.

function scaleCornerRadii(radii: CornerRadii, factor: number): CornerRadii

source

Multiplies every radius by factor, as the scale tool does.

type Paint = SolidPaint | GradientPaint | ImagePaint

source

interface PaintVisibility

since 0.18.0source

Fields every paint shares, as in Figma’s Paint: whether it shows and how it blends with the paints below it on the same layer.

readonly visible?: booleansince 0.18.0source

False hides the paint without removing it, like the eye button in Figma’s fill and stroke lists. Defaults to true.

readonly blendMode?: BlendModesince 0.18.0source

How the paint blends with the paints below it on the same layer: the fills under a fill, or the fills and strokes under a stroke. Uses the same modes as a node’s blendMode. Defaults to "normal".

interface SolidPaint extends PaintVisibility

source
readonly kind: "solid"source
readonly color: stringsource
readonly opacity?: numbersource

type GradientPaint = | LinearGradientPaint | RadialGradientPaint | AngularGradientPaint

source

interface LinearGradientPaint extends PaintVisibility

source
readonly kind: "linear-gradient"source
readonly angle: numbersource
readonly stops: readonly GradientStop[]source
readonly opacity?: numbersource

interface RadialGradientPaint extends PaintVisibility

since 0.6.0source

A gradient that spreads outward from a center point.

readonly kind: "radial-gradient"source
readonly center?: PaintAnchorsource

Defaults to the center of the bounds.

readonly radius?: PaintAnchorsource

Ellipse radii as fractions of the bounds; defaults to half the width and height.

readonly stops: readonly GradientStop[]source
readonly opacity?: numbersource

interface AngularGradientPaint extends PaintVisibility

since 0.6.0source

A gradient that sweeps around a center point.

readonly kind: "angular-gradient"source
readonly center?: PaintAnchorsource

Defaults to the center of the bounds.

readonly angle?: numbersource

Start angle in degrees, clockwise from the positive x axis; defaults to 0.

readonly stops: readonly GradientStop[]source
readonly opacity?: numbersource

interface GradientStop

source
readonly offset: numbersource
readonly color: stringsource

interface PaintAnchor

since 0.6.0source

A point in the painted shape’s bounds, as fractions of its width and height.

readonly x: numbersource
readonly y: numbersource

interface ImagePaint extends PaintVisibility

since 0.6.0source

Paints an image asset.

readonly kind: "image"source
readonly assetId: AssetIdsource
readonly fit?: ImagePaintFitsource

Defaults to cover.

readonly opacity?: numbersource

type ImagePaintFit = "contain" | "cover" | "fill"

since 0.6.0source

How an image paint is scaled into the painted shape’s bounds.

interface Stroke

source
readonly paint: Paintsource
readonly width: numbersource

The weight of every side. With sides, the editor keeps it at the largest side weight.

readonly dash?: readonly number[]source
readonly position?: StrokePositionsince 0.0.1source

Defaults to center when omitted by an older operation or document.

readonly sides?: StrokeSidessince 0.17.0source

Separate weights for the top, right, bottom and left sides, replacing width on rectangles and frames. Other shapes, connectors and code blocks ignore it. A side of weight 0 paints nothing, and the corners between sides of different weights follow the shape’s corners.

readonly join?: StrokeJoinsince 0.22.0source

How the stroke turns a corner where two segments meet, as Figma’s strokeJoin. Omitted keeps what the layer drew before 0.22.0: round on paths, ellipses, polygons, stars, lines and connectors, and on rectangles drawn as paths (rounded or with gradient, image or several fills); miter on frames, other rectangles and code blocks. See defaultStrokeJoin. Rectangles and frames with side weights ignore it.

readonly miterAngle?: numbersince 0.22.0source

The miter angle in degrees, as Figma’s “Miter angle” (the REST API’s strokeMiterAngle): a miter join whose corner is sharper than this is drawn beveled. Only a miter join uses it. It is more than 0 and at most 180; omitted is DEFAULT_STROKE_MITER_ANGLE, Figma’s default of about 28.96 degrees, which is a miter limit of 4 (limit = 1 / sin(angle / 2)).

readonly dashCap?: DashCapsince 0.22.0source

The ends of every dash of a dashed stroke, as Figma’s “Dash cap”. None stops a dash flat; round and square extend it by half the stroke width. Omitted keeps what the layer drew before 0.22.0: round on the layers whose join defaults to round, none on the others (see defaultDashCap). A solid stroke ignores it.

type StrokePosition = "center" | "inside" | "outside"

since 0.0.1source

Which side of the outline a stroke is drawn on.

interface StrokeSides

since 0.17.0source

Stroke weights for the four sides of a rectangle or frame. Unlike Figma, which keeps one set of side weights per node, each stroke in VisualStyle.strokes carries its own.

readonly top: numbersource
readonly right: numbersource
readonly bottom: numbersource
readonly left: numbersource

const STROKE_SIDE_KEYS: readonly ["top", "right", "bottom", "left"]

source

The four stroke sides in the order the wire and the panel use.

type StrokeSideKey = (typeof STROKE_SIDE_KEYS)[number]

source

One side of a stroke, as named in StrokeSides.

function strokeSideWeights(stroke: Pick<Stroke, "width" | "sides">): StrokeSides

source

The weight of each side a stroke paints with: sides, or width on all four.

function widestStrokeSide(sides: StrokeSides): number

source

The largest of the four side weights.

type StrokeCap = (typeof STROKE_CAPS)[number]

source

const STROKE_CAPS: readonly [ "none", "round", "square", "line-arrow", "triangle-arrow", "reversed-triangle", "circle-arrow", "diamond-arrow" ]

since 0.12.0source

Stroke end caps as Figma names them. None ends the stroke flat at the endpoint; round and square extend it by half the stroke width; the arrow caps draw a head in the stroke’s paint whose size follows the stroke width. Lines and connectors share these end styles and draw them the same way. Heads are always solid, even on a dashed stroke.

A stroke also sets how its corners join, how each dash of a dashed stroke ends, and the miter angle below which a miter join is beveled, as in Figma’s Stroke settings. A stroke that sets none of these draws as layers did before schema 0.22.0: defaultStrokeJoin and defaultDashCap give those defaults. Figma sets a miter angle where Skia takes a miter limit; miterLimitFromAngle and miterAngleFromLimit convert between them. Only an open path with one unbranched chain of segments takes end points (openPathChain).

type StrokeJoin = (typeof STROKE_JOINS)[number]

source

How a stroke’s corners join: sharp (miter), cut off (bevel) or round.

const STROKE_JOINS: readonly ["miter", "bevel", "round"]

since 0.22.0source

The stroke joins, as Figma names them (MITER, BEVEL, ROUND).

type DashCap = (typeof DASH_CAPS)[number]

source

How each dash of a dashed stroke ends: flat at the dash (none), round or square.

const DASH_CAPS: readonly ["none", "round", "square"]

since 0.22.0source

The ends of each dash of a dashed stroke, as in Figma’s Dash cap menu.

const DEFAULT_STROKE_MITER_ANGLE: number

since 0.22.0source

Figma’s default miter angle in degrees, 2 * asin(1 / 4): the angle at which a miter limit of 4 bevels.

function strokeMiterAngle(stroke: Pick<Stroke, "miterAngle">): number

source

A stroke’s miter angle, or Figma’s default when it has none.

function miterLimitFromAngle(angle: number): number

since 0.22.0source

The miter limit a miter angle gives, as SVG and Skia use it: the longest a miter may be, as a multiple of half the stroke width, before the join is beveled. A corner sharper than angle degrees has a longer miter.

function miterAngleFromLimit(limit: number): number

source

The miter angle, in degrees, that a miter limit of at least 1 gives.

function defaultStrokeJoin( node: SceneNode, style: VisualStyle | undefined ): StrokeJoin

since 0.22.0source

The join a stroke of node draws with when it names none: what the layer drew before schema 0.22.0, round on layers stroked along a path and miter on layers stroked around a box. style is the layer’s resolved style.

function defaultDashCap( node: SceneNode, style: VisualStyle | undefined ): DashCap

since 0.22.0source

The dash ends a dashed stroke of node draws with when it names none: round on layers stroked along a path and none on layers stroked around a box, as before schema 0.22.0.

const DEFAULT_PATH_STROKE_CAP: StrokeCap

source

The end style an open path draws when it names none: round, as before 0.22.0.

function openPathChain(network: VectorNetwork): readonly VectorSegment[] | null

since 0.22.0source

The segments of network when they form one open chain in order: each segment starts where the one before it ends, and the first segment’s start and the last segment’s end are each touched by that segment only. Only such a path takes end points (PathNode.startCap and endCap). Returns null for a closed, branched or empty network.

interface Shadow

source
readonly kind?: ShadowKindsince 0.7.0source

Defaults to a drop shadow when omitted by an older document.

readonly x: numbersource
readonly y: numbersource
readonly blur: numbersource
readonly spread: numbersource
readonly color: stringsource

type ShadowKind = "drop" | "inner"

since 0.7.0source

A drop shadow paints behind the node; an inner shadow paints inside it.

Vector networks

A path stores an editable vector network: vertices, segments between them, optional tangent constraints, and filled regions made of directed loops. SVG path data is only an interchange format.

interface VectorNetwork

since 0.2.0source

The vertices, segments and filled regions that make up a path’s shape.

readonly vertices: readonly VectorVertex[]source
readonly segments: readonly VectorSegment[]source
readonly constraints?: readonly VectorTangentConstraint[]source

Omitted is equivalent to an empty constraint list.

readonly regions: readonly VectorRegion[]source

Ordered from back to front for painting.

interface VectorVertex

since 0.2.0source

A point in a VectorNetwork, in network coordinates.

readonly id: VectorVertexIdsource
readonly x: numbersource
readonly y: numbersource
readonly cornerRadius?: numbersince 0.10.0source

Corner radius at this vertex, overriding the node style’s corner radius. Only a vertex joining exactly two segments rounds; the renderer clamps the radius to half of the shorter adjacent segment.

type VectorSegment = | VectorLineSegment | VectorCubicSegment | VectorEllipticalArcSegment

since 0.2.0source

An edge between two vertices of a VectorNetwork.

interface VectorLineSegment extends VectorSegmentBase

source
readonly kind: "line"source

interface VectorCubicSegment extends VectorSegmentBase

source
readonly kind: "cubic"source
readonly startHandle: VectorHandlesource
readonly endHandle: VectorHandlesource

interface VectorEllipticalArcSegment extends VectorSegmentBase

source

Compatibility geometry for exact migration and SVG interchange. The editor initially creates and directly edits line and cubic segments only.

readonly kind: "elliptical-arc"source
readonly radiusX: numbersource
readonly radiusY: numbersource
readonly xAxisRotation: numbersource
readonly largeArc: booleansource
readonly sweep: booleansource

interface VectorHandle

source
readonly id: VectorHandleIdsource
readonly dx: numbersource

Horizontal control-point offset relative to the segment endpoint.

readonly dy: numbersource

Vertical control-point offset relative to the segment endpoint.

interface VectorTangentConstraint

source

Couples two endpoint handles that meet at one vertex. Pairwise constraints let branched networks describe independent smooth continuations without assigning one ambiguous smoothing mode to the whole vertex.

readonly vertexId: VectorVertexIdsource
readonly handleIds: readonly [VectorHandleId, VectorHandleId]source
readonly mode: "angle" | "angle-and-length"source

Keep only the angle mirrored, or mirror both angle and handle length.

interface VectorRegion

since 0.2.0source

A filled area of a VectorNetwork, bounded by loops of segments.

readonly id: VectorRegionIdsource
readonly windingRule: "nonzero" | "evenodd"source
readonly loops: readonly VectorRegionLoop[]source
readonly fills?: readonly Paint[]source

Omitted inherits the path’s resolved fills; an empty array disables fill.

interface VectorRegionLoop

source
readonly segments: readonly VectorSegmentReference[]source
readonly implicitClose?: truesource

Closes an open chain for filling without adding or stroking another edge, matching SVG’s implicit fill closure for open subpaths.

interface VectorSegmentReference

source
readonly segmentId: VectorSegmentIdsource
readonly direction: "forward" | "reverse"source

type VectorWindingRule = VectorRegion["windingRule"]

source

function vectorNetworkFromSvgPathData( pathData: string, windingRule: VectorWindingRule = "nonzero" ): VectorNetwork

source

Converts an SVG path into deterministic, node-local vector-network topology. Relative, shorthand, horizontal, vertical, and quadratic commands are normalized. Elliptical arcs remain exact compatibility segments.

function vectorRegionToSvgPathData( network: VectorNetwork, region: VectorRegion ): string

source

Projects one fillable region to SVG path data.

function vectorRegionPathDataById( network: VectorNetwork, regionId: string ): string | undefined

source

Projects the first matching region, useful for callers that retain a region ID.

function vectorNetworkToStrokePathData(network: VectorNetwork): string

source

Projects every network edge once for SVG-compatible stroking and bounds. Region loops preserve authored chains where possible; remaining graph edges become individual subpaths because SVG cannot encode a branched join.

function transformVectorPoint( x: number, y: number, transform: TransformMatrix ): { readonly x: number; readonly y: number }

source

Applies an SVG-compatible affine matrix to a point.

function scaleVectorNetwork( network: VectorNetwork, scale: { readonly x: number; readonly y: number }, anchor: { readonly x: number; readonly y: number } ): VectorNetwork

source

Scales a vector network in its own coordinate space around one anchor. An elliptical arc keeps its exact shape: see scaleEllipticalArc.

function assertValidPathData(pathData: string): void

source

function isClosedPathData(pathData: string): boolean

source

Returns true when every subpath closes back to its starting point.

Text

Text nodes hold paragraphs of styled runs. A run names a theme TextStyle and may carry direct overrides; paragraphs add alignment, spacing, indents and list markers.

interface TextParagraph

source

One paragraph of a TextNode: its runs plus paragraph-level layout.

readonly runs: readonly TextRun[]source
readonly direction?: "auto" | "ltr" | "rtl"source

Auto resolves direction from paragraph content when omitted or set explicitly.

readonly align?: "left" | "center" | "right" | "justify"source
readonly spacingBefore?: numbersource
readonly spacingAfter?: numbersource
readonly indent?: { readonly left: number; readonly right: number; readonly firstLine: number; }source
readonly list?: { readonly kind: "ordered" | "unordered"; readonly level: number; readonly start?: number; readonly marker?: TextListMarker; }source

Makes the paragraph a list item.

interface TextRun

source

A span of text sharing one style.

readonly text: stringsource
readonly styleId?: TextStyleIdsource

The theme text style; the theme’s defaultTextStyleId when omitted.

readonly overrides?: TextStyleOverridessource

Direct formatting on top of the style.

interface TextListMarker

source

The bullet or number shown before a list paragraph, and how it is styled.

readonly text?: stringsource

Custom marker text; must not be empty when given.

readonly styleId?: TextStyleIdsource
readonly overrides?: TextStyleOverridessource
readonly gap?: numbersource

Space between the marker and the paragraph text. Non-negative.

interface TextStyle

source
readonly name: stringsource
readonly fontFamily: stringsource
readonly fontAssetId?: AssetIdsource
readonly fontFallbackAssetIds?: readonly AssetId[]source

Ordered embedded font assets tried after fontAssetId.

readonly language?: stringsource

Canonically-cased BCP 47 language tag used for shaping and line breaking.

readonly fontSize: numbersource
readonly fontWeight: numbersource
readonly lineHeight: numbersource
readonly letterSpacing?: numbersource
readonly color: stringsource
readonly italic?: booleansource
readonly underline?: booleansource
readonly strikethrough?: booleansince 0.2.0source

Draws a line through the text.

readonly openTypeFeatures?: Readonly<Record<string, boolean | number>>since 0.2.0source

Explicit deviations from a font’s OpenType feature defaults.

readonly fontVariations?: Readonly<Record<string, number>>since 0.2.0source

Explicit variable-font axis coordinates keyed by four-byte OpenType tag.

type TextStyleOverrides = & Partial<Omit<TextStyle, "name" | "fontAssetId" | "fontFallbackAssetIds">> & { readonly fontAssetId?: AssetId | null; readonly fontFallbackAssetIds?: readonly AssetId[]; }

since 0.2.1source

Direct text formatting authored on one run or list marker.

Font resources need explicit clearing semantics because omitting an override inherits the selected theme style. A null primary clears an inherited fontAssetId, while an empty fallback list clears inherited fallbacks.

function assertTextStyleOverrides( value: unknown ): asserts value is TextStyleOverrides

source

Validates a standalone authored run or list-marker text-style override.

const MAX_TEXT_FONT_FALLBACK_ASSETS: 4095

source

Canonical upper bound; retained text also carries one primary font asset.

type OpenTypeTag = Brand<string, "OpenTypeTag">

since 0.2.0source

A four-character OpenType feature or axis tag. Make one with openTypeTag.

function openTypeTag(value: string): OpenTypeTag

source

OpenType feature and variation tags are exactly four printable ASCII bytes.

Assets and fonts

Images, videos and fonts live in the document’s asset map and are referenced by AssetId. A font comes from Google Fonts (GoogleFontsSource) or from the fonts installed on the computer (LocalFontSource); a local font is named but its file is never stored in the document.

type Asset = ImageAsset | VideoAsset | FontAsset

source

A file stored in DesignDocument.assets, told apart by its kind field.

interface ImageAsset

source

An image file used by ImageNode and ImagePaint.

readonly id: AssetIdsource

Matches the asset’s key in DesignDocument.assets.

readonly kind: "image"source
readonly mimeType: stringsource
readonly source: AssetSourcesource
readonly width?: numbersource

Intrinsic width in pixels, when known.

readonly height?: numbersource

Intrinsic height in pixels, when known.

interface VideoAsset

source

A video file used by VideoNode.

readonly id: AssetIdsource

Matches the asset’s key in DesignDocument.assets.

readonly kind: "video"source
readonly mimeType: stringsource
readonly source: AssetSourcesource
readonly width?: numbersource

Intrinsic width in pixels, when known.

readonly height?: numbersource

Intrinsic height in pixels, when known.

interface FontAsset

source

One font face, referenced by TextStyle.fontAssetId, TextStyle.fontFallbackAssetIds and CodeStyle.fontAssetId.

readonly id: AssetIdsource

Matches the asset’s key in DesignDocument.assets.

readonly kind: "font"source
readonly mimeType: stringsource

Must be font/woff2 when the source is Google Fonts, and font/ttf or font/otf when the source is a local font.

readonly source: FontAssetSourcesource
readonly family: stringsource
readonly weight: numbersource

From 1 to 1000, as in CSS font-weight.

readonly style: "normal" | "italic" | "oblique"source

type AssetSource = | { readonly kind: "url"; readonly url: string } | { readonly kind: "data"; readonly data: string }

source

Where an asset’s bytes come from: a URL, or data stored in the document.

type FontAssetSource = AssetSource | GoogleFontsSource | LocalFontSource

since 0.13.0source

Where a font’s files come from.

interface GoogleFontsSource

since 0.13.0source

A font family served by Google Fonts as many small WOFF2 files, each covering part of Unicode (declared by unicode-range in the Google Fonts CSS). The app keeps the list of files for each built-in family and loads only the files that contain characters the document uses.

readonly kind: "google-fonts"source
readonly family: stringsource

The Google Fonts family name, such as “Noto Sans SC”.

interface LocalFontSource

since 0.19.0source

A font installed on the computer of the person who chose it, read through the browser’s Local Font Access API (window.queryLocalFonts()). The document names the font but does not carry its file, so the list of fonts installed on a computer never enters a document; only the fonts that its text uses are named. On a computer where the font is not installed, the app shows the text as using a missing font.

readonly kind: "local"source
readonly family: stringsource

The installed family name, such as “Helvetica Neue”.

readonly postScriptName: stringsource

The face’s PostScript name, which identifies it among installed fonts.

readonly styleName: stringsource

The installed style name, such as “Bold Italic”.

readonly sha256: stringsource

SHA-256 of the face’s font file, as 64 lowercase hexadecimal digits. An installed face with the same PostScript name but other bytes is a different version of the font, which can lay text out differently.

Theme, styles and variables

The theme holds shared node, text and code styles, named colors, and variable collections. A node may bind a supported property to a variable; the bound value is also written on the node, so detaching keeps the look.

interface DocumentTheme

source

The document’s shared styles, named colors and variables. Nodes refer to them by ID and may keep direct overrides on top.

readonly defaultTextStyleId: TextStyleIdsource

The text style used by runs that name none.

readonly defaultCodeStyleId: CodeStyleIdsource

The code style used by code nodes that name none.

readonly colors: Readonly<Record<string, string>>source

Named colors, keyed by name.

readonly nodeStyles: Readonly<Record<string, NodeStyle>>source
readonly textStyles: Readonly<Record<string, TextStyle>>source
readonly codeStyles: Readonly<Record<string, CodeStyle>>source
readonly variableCollections?: Readonly<Record<string, VariableCollection>>source

Variable collections stored in this document. Libraries shared between documents are intentionally out of scope.

interface NodeStyle extends Omit<VisualStyle, "styleId">

source
readonly name: stringsource

interface CodeStyle

source

Provides palette colors and the typography defaults used for new code nodes.

readonly name: stringsource
readonly fontFamily: stringsource
readonly fontAssetId?: AssetIdsource
readonly fontSize: numbersource
readonly lineHeight: numbersource
readonly letterSpacing?: numbersince 0.1.2source

Extra space added between characters.

readonly background: stringsource
readonly foreground: stringsource
readonly syntax: Readonly<Record<string, string>>source

interface VariableCollection

source
readonly name: stringsource
readonly defaultModeId: VariableModeIdsource
readonly activeModeId?: VariableModeIdsource

The mode this document shows where no page or layer chooses one (see VariableModes). It defaults to defaultModeId when omitted.

readonly modeOrder: readonly VariableModeId[]source
readonly modes: Readonly<Record<string, VariableMode>>source
readonly variables: Readonly<Record<string, VariableDefinition>>source

interface VariableMode

source
readonly name: stringsource

type VariableDefinition = | VariableDefinitionBase<"color", string> | VariableDefinitionBase<"number", number> | VariableDefinitionBase<"string", string> | VariableDefinitionBase<"boolean", boolean>

source

type VariableType = "color" | "number" | "string" | "boolean"

source

type VariableValue = string | number | boolean

source

type VariableProperty = | "visibility" | "opacity" | "width" | "height" | "cornerRadius" | "fillColor" | "strokeColor" | "strokeWidth" | "gap" | "wrapGap" | "columnGap" | "rowGap" | "paddingTop" | "paddingRight" | "paddingBottom" | "paddingLeft" | "fontFamily" | "fontSize" | "fontWeight" | "lineHeight" | "letterSpacing" | "textColor"

source

type VariableBindings = Readonly<Partial<Record<VariableProperty, VariableId>>>

source

const VARIABLE_PROPERTY_TYPES: Readonly<Record<VariableProperty, VariableType>>

source

interface LocatedVariable

source
readonly collectionId: stringsource
readonly collection: VariableCollectionsource
readonly variableId: VariableIdsource
readonly variable: VariableDefinitionsource

function findVariable( theme: DocumentTheme, variableId: VariableId | string ): LocatedVariable | undefined

source

function resolveVariableValue( theme: DocumentTheme, variableId: VariableId | string, modes?: VariableModes ): VariableValue | undefined

source

The variable’s value in the mode its collection shows under modes (the modes in effect at a layer, see variableModesAt): the inherited choice, else the collection’s active mode, else its default mode. A mode without a value reads the default mode’s value.

function listVariablesForProperty( theme: DocumentTheme, property: VariableProperty ): readonly LocatedVariable[]

source

function variableSupportsProperty( variable: VariableDefinition, property: VariableProperty ): boolean

source

function nodeSupportsVariableProperty( node: SceneNode, property: VariableProperty ): boolean

source

### Variable modes

A page, frame, group, component or instance can choose a mode per variable collection in variableModes. A collection it leaves out is Auto: the layer inherits the mode from its parent, up to the page, and a page without one uses the collection’s activeModeId. An instance’s modes apply to its component’s layers. Stored bound values are written for the active modes; the resolver and the page compiler write them again for layers under other modes.

type VariableModes = Readonly<Record<string, VariableModeId>>

since 0.26.0source

A variable mode per collection, keyed by collection ID. A collection left out is Auto: the layer takes the mode of its parent, up to the page, and a page without one uses the collection’s activeModeId (else its defaultModeId). Omitted when empty.

function inheritVariableModes( parent: VariableModes | undefined, own: VariableModes | undefined ): VariableModes | undefined

since 0.26.0source

The modes a layer uses: its parent’s modes with the layer’s own choices on top. A collection the layer leaves out (Auto) keeps the parent’s mode. Returns parent itself when own chooses nothing.

function variableModeFor( collectionId: string, collection: VariableCollection, modes?: VariableModes ): VariableModeId

since 0.26.0source

The mode a collection shows under modes: the inherited choice, else the collection’s activeModeId, else its defaultModeId.

function variableModesAt( document: DesignDocument, nodeId: NodeId ): VariableModes | undefined

since 0.26.0source

The modes in effect at a stored node: the page’s modes, then each ancestor’s, then the node’s own. A layer of a component (outside any page) starts from the document’s active modes at the component root; an instance applies its own modes on top when it is resolved. Undefined means every collection shows its active mode.

function inheritedVariableModes( document: DesignDocument, nodeId: NodeId ): VariableModes | undefined

since 0.26.0source

The modes a stored node inherits: variableModesAt its parent, or the page’s modes for a page root. The node’s own choices are not included.

function locatedVariableValue( located: LocatedVariable, modes?: VariableModes ): VariableValue | undefined

since 0.26.0source

resolveVariableValue for a variable already found with findVariable.

function applyVariableBindings( document: DesignDocument, node: SceneNode, modes: VariableModes | undefined ): SceneNode

since 0.26.0source

Writes each of the node’s variable bindings in the modes modes into the bound field (see withVariableValue). A binding to a missing variable, or one the node cannot take, is skipped. A text layer that sizes itself is measured again. Returns node itself when it has no bindings.

function withVariableValue( theme: DocumentTheme, node: SceneNode, property: VariableProperty, value: VariableValue ): SceneNode

since 0.26.0source

Writes one variable value into the node field it is bound to, the way the editor does when a variable is applied: a color drives the bottom fill or the first stroke, a radius sets every corner, a text property restyles every run. Sizes and opacity are clamped. A value of the wrong type changes nothing.

function bindingsDependOnModes( theme: DocumentTheme, node: SceneNode, modes: VariableModes | undefined ): boolean

since 0.26.0source

Whether a node’s variable bindings read any collection in a mode other than its active mode under modes. Stored values are written for the active modes, so only such a node needs applyVariableBindings.

function variableModesKey( theme: DocumentTheme, modes: VariableModes | undefined ): string

since 0.26.0source

A stable text form of modes for cache keys: the collections in sorted order. Modes that match a collection’s active mode are left out, so Auto and an explicit choice of the active mode give the same key.

IDs

IDs are branded strings: plain strings at run time, but the type system keeps one kind from being used as another. Each has a constructor that rejects an empty string.

type DocumentId = Brand<string, "DocumentId">

source

Branded string IDs. Each is a plain string at run time, but the brand keeps one kind of ID from being passed where another is expected. Make one with the matching constructor, such as nodeId, which rejects an empty string.

const documentId: (value: string) => DocumentId

source

Brands a string as a DocumentId. Throws a TypeError for an empty string.

type PageId = Brand<string, "PageId">

source

The ID of a Page.

const pageId: (value: string) => PageId

source

Brands a string as a PageId. Throws a TypeError for an empty string.

type NodeId = Brand<string, "NodeId">

source

The ID of a SceneNode.

const nodeId: (value: string) => NodeId

source

Brands a string as a NodeId. Throws a TypeError for an empty string.

type AssetId = Brand<string, "AssetId">

source

The ID of an Asset.

const assetId: (value: string) => AssetId

source

Brands a string as an AssetId. Throws a TypeError for an empty string.

type NodeStyleId = Brand<string, "NodeStyleId">

source

The ID of a NodeStyle in DocumentTheme.nodeStyles.

const nodeStyleId: (value: string) => NodeStyleId

source

Brands a string as a NodeStyleId. Throws a TypeError for an empty string.

type TextStyleId = Brand<string, "TextStyleId">

source

The ID of a TextStyle in DocumentTheme.textStyles.

const textStyleId: (value: string) => TextStyleId

source

Brands a string as a TextStyleId. Throws a TypeError for an empty string.

type CodeStyleId = Brand<string, "CodeStyleId">

source

The ID of a CodeStyle in DocumentTheme.codeStyles.

const codeStyleId: (value: string) => CodeStyleId

source

Brands a string as a CodeStyleId. Throws a TypeError for an empty string.

type VariableCollectionId = Brand<string, "VariableCollectionId">

source

The ID of a VariableCollection.

const variableCollectionId: (value: string) => VariableCollectionId

source

Brands a string as a VariableCollectionId. Throws a TypeError for an empty string.

type VariableModeId = Brand<string, "VariableModeId">

source

The ID of a VariableMode within its collection.

const variableModeId: (value: string) => VariableModeId

source

Brands a string as a VariableModeId. Throws a TypeError for an empty string.

type VariableId = Brand<string, "VariableId">

source

The ID of a VariableDefinition.

const variableId: (value: string) => VariableId

source

Brands a string as a VariableId. Throws a TypeError for an empty string.

type CommitId = Brand<string, "CommitId">

source

The ID of a CommitRecord in a DocumentStore.

const commitId: (value: string) => CommitId

source

Brands a string as a CommitId. Throws a TypeError for an empty string.

type VectorVertexId = Brand<string, "VectorVertexId">

since 0.2.0source

The ID of a VectorVertex.

const vectorVertexId: (value: string) => VectorVertexId

source

Brands a string as a VectorVertexId. Throws a TypeError for an empty string.

type VectorSegmentId = Brand<string, "VectorSegmentId">

since 0.2.0source

The ID of a VectorSegment.

const vectorSegmentId: (value: string) => VectorSegmentId

source

Brands a string as a VectorSegmentId. Throws a TypeError for an empty string.

type VectorHandleId = Brand<string, "VectorHandleId">

since 0.2.0source

The ID of a VectorHandle.

const vectorHandleId: (value: string) => VectorHandleId

source

Brands a string as a VectorHandleId. Throws a TypeError for an empty string.

type VectorRegionId = Brand<string, "VectorRegionId">

since 0.2.0source

The ID of a VectorRegion.

const vectorRegionId: (value: string) => VectorRegionId

source

Brands a string as a VectorRegionId. Throws a TypeError for an empty string.

Loading and saving

deserializeDocument migrates, validates and freezes a document; serializeDocument writes it back as JSON with sorted keys. Every failure is a ModelError with a code and a JSON path.

function deserializeDocument( input: string | unknown, migrations: MigrationRegistry = defaultMigrations ): DesignDocument

source

Loads a document from a JSON string or an already parsed value.

Runs migrations up to CURRENT_DOCUMENT_SCHEMA_VERSION, validates the result with assertDocument, refits every group to its children (which repairs files saved with a stale group size), and returns a deeply frozen copy. The input is not changed.

Throws a ModelError: INVALID_JSON for a string that does not parse, a migration error code (see MigrationRegistry.migrate), or a validation error.

function serializeDocument(document: DesignDocument, indentation = 0): string

source

Validates document with assertDocument and writes it as JSON with sorted keys, so the same document always gives the same text. indentation is passed to JSON.stringify; 0 writes one line.

function snapshotDocument(input: DesignDocument | unknown): DesignDocument

source

Returns a validated, deeply frozen copy of a document. The same as deserializeDocument with the default migrations.

function assertDocument(value: unknown): asserts value is DesignDocument

source

Checks that value is a valid DesignDocument at CURRENT_DOCUMENT_SCHEMA_VERSION: every entity’s shape, the node tree (each node reachable from exactly one page, no cycles or shared children), page order, and style, variable, connector, media-node and font references. It does not migrate; use deserializeDocument for older data.

Throws a ModelError naming the first problem found.

function assertSceneNode(value: unknown): asserts value is SceneNode

source

Checks the shape of one node on its own. References to other nodes, styles and assets are not checked. Throws a ModelError on failure.

function assertDocumentTheme(value: unknown): asserts value is DocumentTheme

source

Checks the shape of a theme on its own. Throws a ModelError on failure.

function assertAsset(value: unknown): asserts value is Asset

source

Checks the shape of one asset on its own. Throws a ModelError on failure.

class ModelError extends Error

source

The error this package throws for invalid data and rejected edits. The message ends with at <path>.

new ( readonly code: ModelErrorCode, message: string, readonly path = "$", options?: ErrorOptions )source

type ModelErrorCode = | "INVALID_JSON" | "INVALID_SCHEMA" | "INVALID_TREE" | "UNSUPPORTED_VERSION" | "MISSING_MIGRATION" | "MIGRATION_FAILED" | "NOT_IMPLEMENTED"

source

What kind of problem a ModelError reports:

  • INVALID_JSON: the input string is not JSON, or a value is not plain JSON (it holds undefined, a non-finite number, a cycle, or a class instance).
  • INVALID_SCHEMA: a value has the wrong shape, an unknown property, or an out-of-range number. Rejected operations and store calls also use it.
  • INVALID_TREE: the value is well formed but its references do not fit together: a missing or shared child, a cycle, an unreachable node, or a missing style, variable, asset or commit.
  • UNSUPPORTED_VERSION: the data is newer than this package supports.
  • MISSING_MIGRATION: no migration step starts at the data’s version.
  • MIGRATION_FAILED: a migration step threw or produced the wrong version.

Older documents are upgraded by a chain of migration steps before they are validated.

class MigrationRegistry

source

An ordered chain of DocumentMigration steps. deserializeDocument and deserializeStore use defaultMigrations unless given another registry.

new (migrations: readonly DocumentMigration[] = [])source
register(migration: DocumentMigration): thissource

Adds a step and returns the registry. Throws a ModelError with code INVALID_SCHEMA if the step does not move forward as DocumentMigration.to describes, or if a step from the same version is already registered.

migrateOperation( operation: unknown, from: DocumentSchemaVersion, target: DocumentSchemaVersion = CURRENT_DOCUMENT_SCHEMA_VERSION ): unknownsource

Carries one stored operation from the version it was recorded under to the current one, using the same ordered steps as documents. Steps without migrateOperation pass the operation through. Returns null when a step returns null.

migrate( input: unknown, target: DocumentSchemaVersion = CURRENT_DOCUMENT_SCHEMA_VERSION ): unknownsource

Runs the steps from the input’s schemaVersion up to target and returns the result as plain JSON. It does not validate the result.

Throws a ModelError: UNSUPPORTED_VERSION when the input is newer than target, MISSING_MIGRATION when no step starts at a version on the way, and MIGRATION_FAILED when a step throws or writes the wrong version.

interface DocumentMigration

source

One step that upgrades a document from one schema version to the next. Register steps in a MigrationRegistry.

readonly from: DocumentSchemaVersionsource

The version this step reads. Only one step may start at a version.

readonly to: DocumentSchemaVersionsource

The version this step writes: from + 1 for a legacy number, "0.0.0" from legacy version 1, or a later semantic version.

migrate(input: unknown): unknownsource

Returns the upgraded document as plain JSON, with schemaVersion set to to. The input is a private copy, so changing it in place is fine.

migrateOperation?(operation: unknown): unknownsource

Carries one stored operation across the same version step.

Stored history keeps whole nodes, pages, themes, and assets inside its operations, so a change to any of those shapes leaves the recorded operations behind. Provide this whenever migrate rewrites something an operation can carry, and leave it out when the change is only about a document-level field that no operation contains.

The operation is given as plain JSON. Returning it unchanged is fine, and returning something that no longer changes the document is fine too: the commit is dropped while its children keep their place in history.

const defaultMigrations: MigrationRegistry

source

The migrations this package ships: one step per schema version, from legacy version 1 through 0.0.0 and every later version up to CURRENT_DOCUMENT_SCHEMA_VERSION. Data saved at a version with no step fails with MISSING_MIGRATION.

function readDocumentSchemaVersion( value: unknown, path: string ): DocumentSchemaVersion

source

Checks that value is a document schema version: a non-negative whole number (legacy) or a major.minor.patch string. Returns it unchanged. Throws a ModelError with code INVALID_SCHEMA at path otherwise.

function compareDocumentSchemaVersions( left: DocumentSchemaVersion, right: DocumentSchemaVersion ): number

source

Orders two schema versions, returning -1, 0 or 1. Semantic versions compare part by part. Legacy numbers 0 and 1 sort before every semantic version, and higher numbers after them. Throws a TypeError for a malformed version string.

These helpers work on any plain JSON value.

type JsonValue = | JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue }

source

Any JSON value. Used for the free-form metadata fields.

type JsonPrimitive = string | number | boolean | null

source

A JSON scalar: string, finite number, boolean, or null.

function canonicalJson(value: unknown, indentation = 0): string

source

Serializes a JSON value with object keys sorted, so equal values give equal text. Throws a ModelError if value is not plain JSON (for example undefined, NaN, or a class instance).

function cloneJson<Value>(value: Value): Value

source

Deep-copies a plain JSON value. Throws a ModelError if it is not plain JSON.

function deepFreeze<Value>(value: Value): Readonly<Value>

source

Freezes a value and everything it contains.

A frozen object is treated as already frozen all the way down, because this package only ever freezes values through this function. Without that rule, re-freezing a document would walk every node on every edit.

Schema history

Every change to the stored document shape raises the schema version and adds one migration step. The table below is built from those steps, newest first: the summary is the note above each step in codec.ts, and the commit is the one that introduced the version string. Types and fields tagged with the version they were added in are listed under their row, and carry a “since” badge where they are documented.

VersionWhat changedIntroduced in
0.27.0Two rules that earlier versions did not check: a group never binds its width or height to a variable, and a text size or line height is greater than zero.1aaacdd 2026-10-04
0.26.0Variable modes on pages and layers (variableModes).c3ea40f 2026-09-29
0.25.0Resize constraints (constraints) and absolute children of auto layout (layoutPositioning).be76658 2026-09-29
0.24.0Component properties (ComponentDefinition.properties), instance and override propertyValues, and node expressions and condition.
Added: changeComponentPropertyOperations, COMPONENT_NUMBER_UNITS, COMPONENT_PROPERTY_KINDS, ComponentDefinition.properties, componentExpressionEnvironment, ComponentExpressionSite, componentExpressionSites, componentLayerIds, ComponentNumberUnit, ComponentPropertyBase, ComponentPropertyDefinition, componentPropertyExpressionType, componentPropertyExpressionValue, componentPropertyId, ComponentPropertyId, ComponentPropertyKind, ComponentPropertyOption, componentPropertyUsage, ComponentPropertyValue, ComponentPropertyValues, componentValueEnvironment, COMPUTABLE_FIELD_VARIABLE_PROPERTIES, COMPUTABLE_FIELDS, ComputableField, computableFieldsOf, computableFieldType, deleteComponentPropertyOperations, deleteComponentPropertyOptionOperations, effectivePropertyValue, ExpressionEditResult, ExpressionField, expressionFieldContext, ExpressionFieldContext, expressionFieldValueEnvironment, findComponentProperty, findComponentPropertyByName, InstanceNode.propertyValues, isComputableField, isExpressionIdentifier, isLayoutFieldInUse, LayerOverride.propertyValues, LAYOUT_ALIGN_TYPE, LAYOUT_DIRECTION_TYPE, LAYOUT_JUSTIFY_TYPE, LENGTH_VARIABLE_PROPERTIES, NamedComputableField, nestedPropertyEnumName, NodeBase.expressions, NodeBase.condition, NodeExpressions, PROPERTY_VALUE_FIELD_PREFIX, propertyButtonSample, propertySampleIds, propertyValueFieldId, propertyValueFits, renameComponentPropertyOperations, renameComponentPropertyOptionOperations, renameVariableCollectionOperations, renameVariableOperations, storedFieldValue, variableExpressionName, variableExpressionType, variableExpressionValue, variablePropertiesOfField, variablesByExpressionName
aa54f10 2026-09-29
0.23.0Components (ComponentDefinition, DesignDocument.components), instances (InstanceNode) and their overrides.686162a 2026-09-29
0.22.0Stroke join, miterAngle and dashCap, and end points on open paths.dad417a #88 2026-09-28
0.21.0ConnectorNode.cornerRadius, which rounds the bends of an elbow connector, and ConnectorNode.cornerSmoothing, which smooths them.60f0497 #87 2026-09-28
0.20.0Connector end side, point and anchor, which pin an attached end on its layer.f6199e2 #73 2026-09-28
0.19.0LocalFontSource font source that names a font installed on a computer.2474003 #63 2026-09-26
0.18.0Paint visible and paint blendMode on every fill and stroke paint.c753479 #59 2026-09-26
0.17.0Per-side stroke weights (Stroke.sides) on rectangles and frames.7382f68 #55 2026-09-26
0.16.0Independent cornerRadii on rectangles and frames.e6739c6 #54 2026-09-26
0.15.0Stack wrap, Around/Evenly spacing and per-node Fill sizing (resizing).761dddf #48 2026-09-26
0.14.0Per-node exportSettings list for layer export.cba94d9 #30 2026-09-23
0.13.0GoogleFontsSource font source that names a Google Fonts family.b0bab12 #15 2026-09-23
0.12.0Line (with arrow caps), polygon and star shapes, and the optional ellipse arc.4c61909 #20 2026-09-23
0.11.0Ruler guides on pages and frames.0a7ec97 #16 2026-09-23
0.10.0Per-vertex cornerRadius on vector vertices.837342a 2026-09-18
0.9.0cornerSmoothing for continuous-curvature corners.a9795ca 2026-09-17
0.8.0Node blendMode and the mask flag for sibling masks.b90c0f5 2026-09-17
0.7.0Inner shadows (Shadow.kind), layerBlur and backgroundBlur.ebc9460 2026-09-17
0.6.0Radial-gradient, angular-gradient and image paints.142ea63 2026-09-17
0.5.0Multiple strokes: the strokes list replaces the single stroke.d037d4d 2026-09-17
0.4.0Slides become pages on an unbounded canvas, each slide root wrapped in a page frame.40b14c0 2026-09-16
0.3.0Removes slide sections, presenter notes and build steps; slide numbers become text.36e77a7 2026-09-16
0.2.1TextStyleOverrides can clear inherited font assets with null or an empty list.e5b85b5 2026-08-10
0.2.0vectorNetwork replaces path data; text strikethrough, OpenType features, font axes.a392f5a 2026-08-10
0.1.4Optional stroke on code nodes.a2b4483 2026-07-30
0.1.3Code node autoResize; older code nodes get "none".9216adf 2026-07-30
0.1.2letterSpacing on code styles and in code node style overrides.bfbe3b5 2026-07-30
0.1.1aspectRatioLocked on nodes.29eb87f 2026-07-30
0.1.0Generated slide-number nodes and slideNumberSettings; removed again in 0.3.0.38e3cd2 2026-07-28
0.0.2Slide sections (slideSections) that group slides; removed again in 0.3.0.63f6094 2026-07-28
0.0.1Stroke.position: strokes drawn centered, inside or outside the outline.56029ef 2026-07-28
0.0.0Adopts semantic versions; the document itself is unchanged.1985db2 2026-07-28

One row per step in defaultMigrations; the codec migrates documents from version 1 onward. Summaries are the notes above each step in codec.ts; commits come from git log -S on the version string.

Operations

Every edit is a list of DocumentOperations. applyOperations applies them all or none, and returns the new document, the operations that undo it, and a ChangeSet naming what changed.

function applyOperations( input: DesignDocument, operations: readonly DocumentOperation[] ): ApplyOperationsResult

source

Applies operations in order and returns the new document, the inverse operations, and a ChangeSet. It never changes the input.

After the operations run, every group that contains a changed node, at any depth, is refitted to its children, and a group left with no children is removed; the inverse operations undo that too. A connector end attached to a node that is gone after that (a removed node, anything inside it, or such a group) becomes a free end at the x and y it has, so removing a connector’s target is never refused; the inverse operations attach the end again. Then every changed node, and every instance of a component whose root’s sizing changed, loses a stretching constraint (Left and right, Top and bottom, Scale) on an axis it hugs: it becomes Left or Top, and the inverse operations put it back. That is done only where the operations bring it: an axis of a node that already hugged and stretched in the input document (one stored before this rule) is left as the operations write it. The result is then validated. A document that is not frozen is first validated and copied with snapshotDocument.

All or nothing: if any operation does not fit the document, or the result is invalid, it throws a ModelError and returns nothing.

interface ApplyOperationsResult

source

What applyOperations returns.

readonly document: DesignDocumentsource

The new, deeply frozen document; the input document when nothing changed.

readonly inverseOperations: readonly DocumentOperation[]source

Operations that turn document back into the input, already in the order to apply them. Empty when nothing changed.

One exception: when the input held a node with a stretching constraint on an axis it hugs (a document stored before applyOperations kept that rule) and the operations themselves repaired that axis or removed the node, these operations bring it back with Left or Top on that axis.

readonly changes: ChangeSetsource

type DocumentOperation = | { readonly type: "document.set-title"; readonly title: string } | { readonly type: "document.set-metadata"; readonly metadata: Readonly<Record<string, JsonValue>> | null; } | { readonly type: "node.insert"; readonly parentId: NodeId; readonly index: number; readonly subtree: SerializedSubtree; } | { readonly type: "node.remove"; readonly nodeId: NodeId } | { readonly type: "node.move"; readonly nodeId: NodeId; readonly newParentId: NodeId; readonly index: number; } | { readonly type: "node.replace"; readonly node: SceneNode } | { readonly type: "page.insert"; readonly index: number; readonly page: Page; readonly subtree: SerializedSubtree; } | { readonly type: "page.remove"; readonly pageId: PageId } | { readonly type: "page.move"; readonly pageId: PageId; readonly index: number; } | { readonly type: "page.replace"; readonly page: Page } | { readonly type: "theme.replace"; readonly theme: DocumentTheme } | { readonly type: "asset.put"; readonly asset: Asset } | { readonly type: "asset.remove"; readonly assetId: AssetId } | { readonly type: "component.insert"; readonly component: ComponentDefinition; readonly subtree: SerializedSubtree; } | { readonly type: "component.remove"; readonly componentId: ComponentId } | { readonly type: "component.replace"; readonly component: ComponentDefinition; }

source

One edit to a document. Every change goes through these operations, so each can be recorded, inverted for undo, and sent over the wire as JSON.

  • document.set-title, document.set-metadata: change document fields; null metadata removes it.
  • node.insert: adds subtree as a child of parentId at index (0 to the child count). None of its node IDs may exist yet.
  • node.remove: removes a node and its descendants. Not for a page root. A connector end attached to a removed node becomes a free end.
  • node.move: moves a node to newParentId at index, counted after the node is taken out of its old parent. Not for a page root, and not into the node’s own subtree.
  • node.replace: replaces a node’s data by ID. It cannot change the node’s type or a container’s children; use insert, remove and move.
  • page.insert, page.remove, page.move: add, delete or reorder pages. An inserted page’s rootId must match its subtree’s root. Removing a page removes its nodes.
  • page.replace: replaces a page’s fields; it cannot change rootId.
  • theme.replace: replaces the whole theme.
  • asset.put: adds an asset or replaces the one with the same ID.
  • asset.remove: deletes an asset. It fails validation while an image or video node or a text or code style still refers to it.

interface SerializedSubtree

source

A node and all its descendants, as carried by insert operations.

readonly rootId: NodeIdsource
readonly nodes: Readonly<Record<string, SceneNode>>source

The root and every descendant, keyed by NodeId.

function serializeSubtree( document: DesignDocument, rootId: NodeId ): SerializedSubtree

source

Copies a node and its descendants into a SerializedSubtree, ready for a node.insert or page.insert operation. Throws a ModelError when the node does not exist.

function getSubtreeNodeIds( document: DesignDocument, rootId: NodeId ): ReadonlySet<NodeId>

source

The IDs of a node and all its descendants. Empty when the node does not exist.

function nodeIdsRemovedBy( input: DesignDocument, operations: readonly DocumentOperation[] ): readonly NodeId[]

source

The IDs of the nodes that are in input and gone once operations have run, in the order input.nodes lists them. That is every node the operations remove themselves (node.remove, page.remove and component.remove, each with everything inside) and every group removed because the edit left it with no children. A node that is removed and put back by the same operations is not listed.

It runs the same steps as applyOperations but does not validate the result and returns no document. Use it to learn what an edit removes before applying it, for example to let go of the connector ends attached to those nodes, without which applyOperations would refuse the edit.

It throws a ModelError when an operation does not fit the document.

interface ChangeSet

source

What an edit changed, used to decide what to redraw and which subscribers to notify. Every ID list is sorted. When nothing changed the value is EMPTY_CHANGE_SET.

readonly documentChanged: booleansource

False only when the document is unchanged; then every list is empty.

readonly themeChanged: booleansource
readonly changedNodeIds: readonly NodeId[]source

Nodes whose data or position changed, or that may look different. It includes inserted and removed nodes, parents whose children changed, every node in a moved subtree, nodes that use a changed asset, and every node when the theme changed.

readonly insertedNodeIds: readonly NodeId[]source

Nodes that did not exist before. A node removed and inserted again in the same edit is in neither this list nor removedNodeIds.

readonly removedNodeIds: readonly NodeId[]source

Nodes that no longer exist.

readonly changedPageIds: readonly PageId[]source

Pages added, removed, changed or moved in pageOrder.

readonly changedAssetIds: readonly AssetId[]source
readonly changedComponentIds: readonly ComponentId[]since 0.23.0source

Components added, removed or replaced, and components any of whose layers changed. Their instances, and the instances of every component that nests them, may look different (see componentsNesting).

const EMPTY_CHANGE_SET: ChangeSet

source

The frozen ChangeSet returned whenever nothing changed.

function diffDocuments( previous: DesignDocument, current: DesignDocument ): ChangeSet

source

Works out the ChangeSet between two valid documents, for changes that did not come from applyOperations (such as undo or checkout in DocumentStore). Parts shared by identity are skipped without reading.

function serializeOperations( operations: readonly DocumentOperation[], indentation = 0 ): string

source

Checks each operation with assertOperation and writes the list as JSON with sorted keys. indentation is passed to JSON.stringify.

function deserializeOperations( input: string | unknown ): readonly DocumentOperation[]

source

Reads a list of operations from a JSON string or a parsed value, checks each with assertOperation, and returns a deeply frozen copy. Throws a ModelError (INVALID_JSON for a string that does not parse).

function assertOperation(value: unknown): asserts value is DocumentOperation

source

Checks the shape of one operation: its type, its exact keys, and any node, page, theme or asset it carries. Whether it fits a particular document is only checked by applyOperations. Throws a ModelError.

function assertChangedDocument( value: unknown, changed: ChangedDocumentEntities ): asserts value is DesignDocument

source

Validates a document produced by editing an already valid one. The shape of the changed entities is checked, and every rule that spans entities — the node tree, page order, sections, and references — is checked in full.

interface ChangedDocumentEntities

source

Names the parts a transaction touched. Everything the document carries over unchanged was validated when it first entered the document, so it is checked for shape again only when it appears here.

A change set that omits something the transaction wrote would let an invalid value through, so only produce one alongside the edit that made it.

readonly nodeIds: ReadonlySet<string>source
readonly pageIds: ReadonlySet<string>source
readonly assetIds: ReadonlySet<string>source
readonly componentIds: ReadonlySet<string>since 0.23.0source

Components whose definition was written.

readonly themeChanged: booleansource

Store and history

DocumentStore keeps the current document and a tree of commits. It supports undo, redo, grouping many dispatches into one undo step, named branches, and subscriptions to parts of the document.

class DocumentStore

source

Holds a document and its edit history. Every change is a commit made of DocumentOperations; the head commit’s document is the current one.

History is a tree of commits rather than a line: undoing and then dispatching starts a new child, and older children stay reachable through DocumentStore.checkout or DocumentStore.redo with an ID. Named branches point at commits and follow the head while checked out. A new store starts on the main branch with one root commit.

Listeners run synchronously after each change. A listener may read the store but must not change it: dispatch, undo, redo, checkouts and branch calls made during notification throw a ModelError.

new (initialDocument: DesignDocument | unknown, options: StoreOptions = {})source

Validates and freezes initialDocument with snapshotDocument and makes it the root commit.

getSnapshot(): DesignDocumentsource

The head commit’s document.

getHeadCommitId(): CommitIdsource
getRootCommitId(): CommitIdsource
getCurrentBranch(): string | nullsource

The checked-out branch, or null when the head is detached.

getBranches(): Readonly<Record<string, CommitId>>source

Every branch name and the commit it points to.

getCommit(id: CommitId): CommitRecord | undefinedsource
getHistoryGroupId(id: CommitId = this.#headCommitId): string | undefinedsource

The history group a commit belongs to, if it was dispatched with one. A later dispatch with the same group amends that commit instead of adding an undo entry.

getDocumentAt(id: CommitId): DesignDocument | undefinedsource

The document as of a commit, or undefined for an unknown ID.

listCommits(): readonly CommitRecord[]source

Every commit, ordered by CommitRecord.sequence.

listChildren(id: CommitId = this.#headCommitId): readonly CommitId[]source

The commits made directly on top of id (the head by default).

preview( operations: readonly DocumentOperation[] ): ReturnType<typeof applyOperations>source

Applies operations to the head document without committing them.

dispatch(input: TransactionInput): CommitResult | nullsource

Applies input.operations to the head document and records the result. Usually this adds a commit on top of the head, moves the current branch to it, and clears the redo entries. With amendHead, or a historyGroupId equal to the head’s, the operations are folded into the head commit instead; if the folded commit then changes nothing, it is removed and the head moves back to its parent.

Returns the new head, or null when nothing changed (or an amended commit was removed). Throws a ModelError when baseCommitId is not the head, when an operation does not fit, or when the result is invalid; the store is then unchanged.

setFrozen(frozen: boolean): voidsince 0.27.0source

Freezes or unfreezes the document. While frozen, dispatch, undo, redo, squashHistory, checkout and checkoutBranch change nothing and return null, the same answer as for a change that changes nothing, and no listener is called. Reading still works. A caller that hands the document to someone else (before the page that shows it is loaded again, for example) freezes it first, so that what it handed over stays the latest document.

isFrozen(): booleansource

True between setFrozen(true) and setFrozen(false).

canUndo(): booleansource

True unless the head is the root commit.

canRedo(): booleansource

True when redo with no argument has a commit to move to.

undo(): CommitResult | nullsource

Moves the head (and the current branch) to the head’s parent and remembers the old head for redo. Returns null at the root commit.

redo(id?: CommitId): CommitResult | nullsource

Moves the head to a child commit. Without id it picks the most recently undone child, or the only child when there is exactly one, and returns null when there is none. Throws a ModelError when id is not a child of the head.

getNextSequence(): numbersource

The sequence number the next new commit gets.

squashHistory( fromSequence: number, options: { readonly onlyCommits?: ReadonlySet<CommitId>; } = {} ): CommitResult | nullsource

Folds the commits made since fromSequence (see getNextSequence) on the way to the head into one commit, so one undo takes them all back. The walk from the head stops at the first commit made before fromSequence, which becomes the folded commit’s parent. Commits that hang off the folded ones (typing undone and then replaced) are removed with them; the head’s own children stay. The folded commit keeps the head’s ID and document, so the document does not change; when the folded commits change nothing in total, they are all removed and the head moves to the parent.

With onlyCommits, nothing changes and null is returned when a commit it would fold or remove is not in that set, so commits made by someone else (another tab, an outside change) are never folded into or removed with the caller’s.

Returns the folded commit, or null when there were fewer than two commits to fold, they were removed, or a commit outside onlyCommits was found. Throws a ModelError when a branch other than the checked-out one points at a commit it would remove.

checkout(id: CommitId): CommitResult | nullsource

Moves the head to any commit and detaches it from its branch, so later commits move no branch. Clears the redo entries. Returns null when id is already the head. Throws a ModelError for an unknown commit.

createBranch(name: string, from: CommitId = this.#headCommitId): voidsource

Adds a branch pointing at from (the head by default) without checking it out. Throws a ModelError if the name is empty, holds a null character, or is taken, or if the commit is unknown.

checkoutBranch(name: string): CommitResult | nullsource

Makes name the current branch and moves the head to its commit. Clears the redo entries. Returns null when the head does not move. Throws a ModelError for an unknown branch.

renameBranch(previousName: string, nextName: string): voidsource

Renames a branch. Throws a ModelError if the old name is unknown or the new one is invalid or taken.

deleteBranch(name: string): voidsource

Deletes a branch name; its commits stay. Deleting the current branch detaches the head. Throws a ModelError for an unknown branch.

subscribe(target: SubscriptionTarget, listener: StoreListener): Unsubscribesource

Calls listener after each head move whose changes touch target (see SubscriptionTarget). Returns a function that removes the listener.

subscribeState(listener: StoreStateListener): Unsubscribesource

Calls listener, with no arguments, after any change to the store’s state: the head, the branches, the current branch or the redo entries. Suits UI that shows undo and redo buttons or a branch list.

toJSON(): PersistedStoresource

The saved form of the whole store, as a frozen plain object.

serialize(indentation = 0): stringsource

toJSON written as JSON with sorted keys. Same as serializeStore.

static fromJSON( input: string | unknown, options: StoreOptions = {}, storeMigrations: StoreMigrationRegistry = defaultStoreMigrations, documentMigrations: MigrationRegistry = defaultMigrations ): DocumentStoresource

Loads a store saved by serialize. Same as deserializeStore.

interface StoreOptions

source

Options for DocumentStore and deserializeStore.

readonly rootCommitId?: CommitIdsource

The root commit’s ID. Ignored when loading, which keeps the saved one.

readonly idFactory?: () => CommitIdsource

Makes new commit IDs. It must not repeat an ID in the store, or dispatch throws. The default combines the time, a counter and random characters.

readonly now?: () => numbersource

The clock for CommitRecord.timestamp. Defaults to Date.now.

readonly onListenerError?: (error: unknown) => voidsource

Receives errors thrown by listeners, so one failing listener does not stop the others. Errors are dropped when omitted.

interface TransactionInput

source

The argument to DocumentStore.dispatch.

readonly operations: readonly DocumentOperation[]source
readonly baseCommitId?: CommitIdsource

The head commit the caller based the operations on. When given and the head has moved since, dispatch throws instead of applying them.

readonly historyGroupId?: stringsource

Groups a run of dispatches, such as the steps of one drag, into one undo step. When the head commit was dispatched with the same group ID, the operations are folded into it instead of adding a commit. Must be a non-empty string without null characters.

readonly message?: stringsource
readonly actor?: CommitActorsource
readonly amendHead?: booleansource

Folds the operations into the head commit instead of adding a commit, so they add no undo step; the head keeps its message unless one is given. For changes derived from the head commit, such as the size a renderer measures for text the commit edited. The root commit cannot be amended, and amending discards the redo entries, so callers check canRedo first.

interface CommitResult

source

The commit the head now points to, and its document.

readonly commit: CommitRecordsource
readonly document: DesignDocumentsource

interface CommitRecord

source

One entry in a DocumentStore’s history. Commits form a tree.

readonly id: CommitIdsource
readonly parentId: CommitId | nullsource

The commit this one was made on top of; null only for the root commit.

readonly sequence: numbersource

Creation order within the store: 0 for the root, then counting up. It also orders DocumentStore.listCommits.

readonly timestamp: numbersource

When the commit was made or last amended, from StoreOptions.now.

readonly message?: stringsource
readonly actor?: CommitActorsource
readonly operations: readonly DocumentOperation[]source

The operations that turn the parent’s document into this commit’s.

readonly inverseOperations: readonly DocumentOperation[]source

The operations that turn this commit’s document back into the parent’s.

readonly changes: ChangeSetsource

What operations changed relative to the parent.

interface CommitActor

source

Who made a commit. The root commit’s actor is { kind: "system", id: "taco-slides" }.

readonly kind: "human" | "assistant" | "system"source
readonly id: stringsource

An app-defined identifier for the person or agent.

interface StoreChangeEvent

source

Passed to DocumentStore.subscribe listeners after the head moves. The event is deeply frozen.

readonly reason: TransitionReasonsource
readonly previousCommitId: CommitIdsource

The head before the change. Equal to currentCommitId when a dispatch amended the head commit in place.

readonly currentCommitId: CommitIdsource
readonly previous: DesignDocumentsource
readonly current: DesignDocumentsource
readonly operations: readonly DocumentOperation[]source

Operations that transform previous into current for this transition.

readonly changes: ChangeSetsource

type TransitionReason = "dispatch" | "undo" | "redo" | "checkout"

source

Which DocumentStore call moved the head. checkout covers checkoutBranch too.

type SubscriptionTarget = | { readonly kind: "document" } | { readonly kind: "node"; readonly nodeId: NodeId } | { readonly kind: "page"; readonly pageId: PageId } | { readonly kind: "asset"; readonly assetId: AssetId }

source

What a DocumentStore.subscribe listener watches:

  • document: every change.
  • node: a change to the node or any of its descendants, before or after the change.
  • page: the page is listed in ChangeSet.changedPageIds, or any node in its tree changed.
  • asset: a change to that asset.

type StoreListener = (event: StoreChangeEvent) => void

source

A DocumentStore.subscribe callback.

type StoreStateListener = () => void

source

A DocumentStore.subscribeState callback.

type Unsubscribe = () => void

source

Removes the listener it was returned for.

The whole history can be saved and loaded. Saved history has its own integer version and migrations; the documents and operations inside it go through the document migrations.

function serializeStore(store: DocumentStore, indentation = 0): string

source

Writes a store’s whole history as JSON with sorted keys: the root document and every commit’s operations, plus the head, branches and redo entries.

function deserializeStore( input: string | unknown, options: StoreOptions = {}, storeMigrations: StoreMigrationRegistry = defaultStoreMigrations, documentMigrations: MigrationRegistry = defaultMigrations ): DocumentStore

source

Loads a store from a JSON string or parsed value written by serializeStore.

It first runs storeMigrations, then carries the root document and every stored operation to the current document schema with documentMigrations (see DocumentMigration.migrateOperation), and replays every commit to rebuild the documents, inverse operations and change sets. A commit that migration leaves with nothing to change is dropped and its children take its place. Without migration, such a commit is an error.

Throws a ModelError for invalid or inconsistent data.

type PersistedStore = PersistedStoreV1

source

The current saved-history format.

interface PersistedStoreV1

source

The saved form of a DocumentStore: the root document plus every commit’s operations, the head, branches and redo stack. Produced by DocumentStore.toJSON.

readonly storeVersion: typeof CURRENT_STORE_SCHEMA_VERSIONsource
readonly documentSchemaVersion: typeof CURRENT_DOCUMENT_SCHEMA_VERSIONsource
readonly rootCommitId: CommitIdsource
readonly rootDocument: DesignDocumentsource
readonly commits: readonly PersistedCommitV1[]source
readonly headCommitId: CommitIdsource
readonly branches: Readonly<Record<string, CommitId>>source
readonly currentBranch: string | nullsource
readonly redoStack: readonly CommitId[]source

interface PersistedCommitV1

source

A commit as saved. Inverse operations and changes are not saved; loading works them out again by replaying the operations.

readonly id: CommitIdsource
readonly parentId: CommitId | nullsource
readonly sequence: numbersource
readonly timestamp: numbersource
readonly message?: stringsource
readonly actor?: CommitActorsource
readonly operations: readonly DocumentOperation[]source

const CURRENT_STORE_SCHEMA_VERSION: 1

source

The version of the saved-history format written by serializeStore. It is separate from CURRENT_DOCUMENT_SCHEMA_VERSION and counts up by whole numbers.

class StoreMigrationRegistry

source

An ordered chain of StoreMigration steps, used by deserializeStore.

new (migrations: readonly StoreMigration[] = [])source
register(migration: StoreMigration): thissource

Adds a step and returns the registry. Throws a ModelError with code INVALID_SCHEMA if the step does not advance exactly one version from a non-negative one, or if a step from the same version exists.

migrate(input: unknown): unknownsource

Runs the steps from the input’s storeVersion up to CURRENT_STORE_SCHEMA_VERSION. Throws a ModelError with code UNSUPPORTED_VERSION, MISSING_MIGRATION or MIGRATION_FAILED.

interface StoreMigration

source

One step that upgrades saved history by one storeVersion. Document data inside it is upgraded separately by the document migrations.

readonly from: numbersource
readonly to: numbersource

Must be from + 1.

migrate(input: unknown): unknownsource

Returns the upgraded store as plain JSON with storeVersion set to to.

const defaultStoreMigrations: StoreMigrationRegistry

source

The store migrations this package ships. Empty while only version 1 exists.

Geometry and shapes

Helpers for node bounds, rotated frames, groups, and the points of the built-in shapes.

interface Rect

source

An axis-aligned rectangle; x and y are its top-left corner.

readonly x: numbersource
readonly y: numbersource
readonly width: numbersource
readonly height: numbersource

type TransformMatrix = readonly [ a: number, b: number, c: number, d: number, e: number, f: number ]

source

An SVG-style affine matrix [a, b, c, d, e, f]. It maps a point (x, y) to (a*x + c*y + e, b*x + d*y + f), which is the 3×3 matrix product

[x′y′1]=[acebdf001][xy1]\begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}

const IDENTITY_TRANSFORM: TransformMatrix

source

function getNodeBounds(node: SceneNode): Rect

source

Returns layout bounds in the coordinate system of the node’s parent.

function getRotatedRectBounds(frame: Rect, rotation: number): Rect

source

Returns the parent-space axis-aligned bounds of a rotated frame.

function resizeRotatedRect( frame: Rect, rotation: number, size: { readonly width: number; readonly height: number }, anchor: { readonly x: number; readonly y: number } = { x: 0, y: 0 } ): Rect

source

Changes a frame’s size while keeping one of its local points fixed in the parent coordinate system. The default anchor is the local top-left corner. anchor is a fraction of the size, so (α,β)=(0.5,0.5)(\alpha, \beta) = (0.5, 0.5) is the center. With RθR_\theta the rotation by rotation degrees (clockwise in y-down space), the anchor sits at this offset from the frame’s x, y:

o(w,h)=[w/2h/2]+Rθ[(α−12) w(β−12) h],Rθ=[cos⁡θ−sin⁡θsin⁡θcos⁡θ]\mathbf{o}(w, h) = \begin{bmatrix} w/2 \\ h/2 \end{bmatrix} + R_\theta \begin{bmatrix} (\alpha - \tfrac12)\,w \\ (\beta - \tfrac12)\,h \end{bmatrix}, \qquad R_\theta = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix}

and the new position is (x′,y′)=(x,y)+o(wold,hold)−o(wnew,hnew)(x', y') = (x, y) + \mathbf{o}(w_\text{old}, h_\text{old}) - \mathbf{o}(w_\text{new}, h_\text{new}).

function getPathBounds( pathData: string, transform: TransformMatrix = IDENTITY_TRANSFORM ): Rect | null

source

Returns tight fill bounds after applying an SVG-compatible affine transform.

function getVectorNetworkBounds( vectorNetwork: VectorNetwork, transform: TransformMatrix = IDENTITY_TRANSFORM ): Rect | null

source

Returns tight network-edge bounds after applying an affine transform.

function getGroupChildrenBounds( nodes: NodeRecord, group: GroupNode ): Rect | null

source

Returns the union of a group’s children bounds in the group’s own coordinate system, or null for a group without children. Rotated children contribute their rotated bounding box and nested groups contribute their own (already fitted) frame.

function fitGroupToChildren( nodes: NodeRecord, group: GroupNode ): FittedGroup | null

source

Fits a group’s frame to its children without moving anything on the page. Returns null when the frame already matches. An empty group keeps its position and collapses to zero size.

interface FittedGroup

source
readonly group: GroupNodesource
readonly children: readonly SceneNode[]source

Children whose local position changed because the group origin moved.

function fitAllGroups(nodes: NodeRecord): NodeRecord

source

Fits every group in a node record, deepest groups first so a parent group sees its nested groups’ final frames. Used when a document is loaded, which repairs files saved with a stale group size.

function buildParentMap(nodes: NodeRecord): Map<NodeId, NodeId>

source

function nodeDepth(parents: ReadonlyMap<NodeId, NodeId>, nodeId: NodeId): number

source

interface ShapePoint

source

Pure geometry for the parametric shapes: polygons, stars, ellipse arcs and lines. The renderer compiler, the editor and the tests share these helpers so every consumer agrees on the same vertices.

readonly x: numbersource
readonly y: numbersource

const MIN_SHAPE_POINT_COUNT: 3

since 0.12.0source

Figma’s polygon and star point counts.

const MAX_SHAPE_POINT_COUNT: 60

source

const DEFAULT_POLYGON_POINT_COUNT: 3

source

Figma’s defaults for newly drawn shapes.

const DEFAULT_STAR_POINT_COUNT: 5

source

const DEFAULT_STAR_INNER_RADIUS: 0.382

source

function isValidShapePointCount(value: number): boolean

source

function polygonPoints( pointCount: number, width: number, height: number ): ShapePoint[]

source

The corners of a regular polygon in frame-local coordinates, clockwise from the top center. The polygon is stretched so its bounds equal the frame. Corner kk of nn starts on the unit circle (y points down)

θk=−π2+2πkn,(uk,vk)=(cos⁡θk,sin⁡θk)\theta_k = -\frac{\pi}{2} + \frac{2\pi k}{n}, \qquad (u_k, v_k) = (\cos\theta_k, \sin\theta_k)

and is then scaled into the frame by its bounding box: xk=w⋅(uk−umin)/(umax−umin)x_k = w \cdot (u_k - u_\text{min}) / (u_\text{max} - u_\text{min}), and likewise yky_k with the height (0 when the span is 0).

function starPoints( pointCount: number, innerRadius: number, width: number, height: number ): ShapePoint[]

source

The alternating outer and inner points of a star in frame-local coordinates, clockwise from the top outer point. innerRadius is the inner point distance as a fraction of the outer radius. The star is stretched so the bounds of all its points equal the frame. Before stretching, point kk of 2n2n is

θk=−π2+πkn,rk={1k evenρk odd,(uk,vk)=(rkcos⁡θk, rksin⁡θk)\theta_k = -\frac{\pi}{2} + \frac{\pi k}{n}, \qquad r_k = \begin{cases} 1 & k \text{ even} \\ \rho & k \text{ odd} \end{cases}, \qquad (u_k, v_k) = (r_k\cos\theta_k,\ r_k\sin\theta_k)

with ρ\rho = innerRadius; the stretch is the same as polygonPoints.

function closedPolygonVectorNetwork( points: readonly ShapePoint[], idPrefix = "shape" ): VectorNetwork

source

A closed straight-edged network through the given points, one region.

function validateEllipseArc(arc: EllipseArc): string | null

source

function ellipseArcSweep(arc: EllipseArc): number

source

The sweep in degrees, positive clockwise; at most one full turn: sweep=endingAngle−startingAngle\text{sweep} = \text{endingAngle} - \text{startingAngle}.

function isWholeEllipseArc(arc: EllipseArc | undefined): boolean

source

True when the arc draws the plain whole ellipse.

function ellipseVectorNetwork( width: number, height: number, arc?: EllipseArc, idPrefix = "shape" ): VectorNetwork

source

The network for an ellipse filling a width by height frame, cut by an optional arc: a whole ellipse, a ring (whole sweep with an inner radius), a pie (partial sweep without one), or a ring sector. A zero sweep draws nothing and returns an empty network. An angle ϕ\phi in degrees (0 points right, positive turns clockwise because y points down) lands at

(w2+w2 tcos⁡ϕ, h2+h2 tsin⁡ϕ)\left(\frac{w}{2} + \frac{w}{2}\,t\cos\phi,\ \frac{h}{2} + \frac{h}{2}\,t\sin\phi\right)

where t=1t = 1 on the outer edge and tt = innerRadius on the inner edge.

function lineEndpoints( line: Pick< LineNode, | "frame" | "rotation" | "flipX" | "flipY" > ): { readonly start: ShapePoint; readonly end: ShapePoint }

source

The start and end points of a line in its parent’s coordinates. With frame center (cx,cy)(c_x, c_y), width ww, rotation θ\theta (degrees converted to radians) and s=−1s = -1 when flipX is set, else s=1s = 1:

start,end=(cx,cy)∓sw2(cos⁡θ,sin⁡θ)\text{start}, \text{end} = (c_x, c_y) \mp \frac{s w}{2}(\cos\theta, \sin\theta)

function lineFrameFromEndpoints( start: ShapePoint, end: ShapePoint ): { readonly frame: Rect; readonly rotation: number }

source

The zero-height frame and rotation of a line between two parent-space points. The rotation is in degrees within (-180, 180].

function constrainLineEndpoint( anchor: ShapePoint, point: ShapePoint ): ShapePoint

source

Moves point so the segment from anchor keeps its length but turns to the nearest multiple of 45 degrees, as Shift does while drawing a line.

Connectors

Where connector ends go on a layer’s outline. The Connector tool offers a layer’s key points as dots (connectorKeyPoints); an end pinned to one of them stores a side or an anchor (ConnectorKeyPlace), and connectorPinnedPlace finds where it sits on the layer. An automatic end sits where the outline meets the line toward the other end (connectorOutlinePointToward). The page compiler and the editor share these, so the dot a connector is dropped on is where its end is drawn. Positions are in the layer’s own coordinates.

function connectorKeyPoints(node: SceneNode): readonly ConnectorKeyPoint[]

source

The key points of a layer, in the order the Connector tool shows them:

  • A rectangle, frame, text, image or any other box: the middle of each side, as FigJam’s TOP, RIGHT, BOTTOM and LEFT magnets.
  • An ellipse: its top, right, bottom and left points (on the curve, which touches its box there), keeping only those an arc draws, and the two ends of an arc that stops short of a full turn.
  • A polygon: every corner, and the middle of every edge when it has at most MAX_CONNECTOR_EDGE_KEY_POINT_COUNT points.
  • A star: its outer tips. Its inner corners point into the star, so a connector there would cross it.

interface ConnectorKeyPoint extends ShapePoint

source

A point on a layer’s outline that a connector end can be pinned to.

readonly place: ConnectorKeyPlacesource
readonly normal: ShapePointsource

The outline’s outward direction at the point.

type ConnectorKeyPlace = | { readonly side: ConnectorSide; readonly anchor?: undefined } | { readonly anchor: ConnectorAnchor; readonly side?: undefined }

source

How an end is pinned to a key point: by a side (Figma’s magnet) or an anchor.

function sameConnectorKeyPlace( a: ConnectorKeyPlace, b: ConnectorKeyPlace ): boolean

source

Two places are the same pin: the same side, or the same kind and index of anchor.

function connectorPinnedPlace( node: SceneNode, endpoint: ConnectorEndpoint ): ConnectorOutlinePlace | undefined

source

The place of an attached end pinned by side, point or anchor on node, or undefined for an automatic end (including an anchor the layer does not have). A side on a box or ellipse is the middle of that side of the box; on a polygon or star it is where the outline meets the line from the centre (the average of its corners) straight toward that side. A side or anchor has the outward normal; a fixed point has none.

function connectorAnchorPoint( node: SceneNode, anchor: ConnectorAnchor ): ConnectorOutlinePlace | undefined

source

The place of an anchor on a layer, or undefined when the layer has no such point: an index past its corners, or an anchor on a box or whole ellipse.

function connectorOutlinePointToward( node: SceneNode, toward: ShapePoint ): ShapePoint

source

Where on its outline a layer meets the line from its centre toward toward: the box’s edge, the ellipse’s curve (a whole ellipse, even for an arc) or the polygon’s or star’s outermost crossing. The layer’s centre when toward is the centre.

interface ConnectorOutlinePlace extends ShapePoint

source

A place on a layer an end is drawn at, with the outward direction when it has one.

readonly normal?: ShapePointsource

const CONNECTOR_SIDE_NORMALS: Readonly<Record<ConnectorSide, ShapePoint>>

source

The outward direction of each side of a layer’s box.

const MAX_CONNECTOR_EDGE_KEY_POINT_COUNT: 8

source

A polygon above this many points shows its corners only: with more, the dots in the middle of its edges crowd the corners.

Components and instances

A component is a reusable layer tree stored once per document, never on a page: its layers live in nodes under ComponentDefinition.rootId, listed in DesignDocument.components. An InstanceNode shows it on a page. It stores a reference and LayerOverrides keyed by layer path, and its layers are derived by resolveInstance with virtual IDs <instance>;<path> (parseVirtualNodeId). editTargetFor decides whether an edit to a selected ID goes to a stored node or to an instance override, and setOverrideOperations writes one. After an edit, withAutoLayoutOperations runs the component cascade (cascadeComponentChanges): instances of changed components are refitted to their hugging size, their override keys that name deleted layers are dropped (pruneOverrides), and their auto layout ancestors are laid out again, nested components first. remapOverrides carries overrides over to another component tree by layer name and position, for swap, redefine and paste. The nesting graph (componentNestingGraph) says which components contain which, for the cycle rule and for invalidation. The sample documents are what the compiler, editor and web tests build on. See docs/components/design.md.

interface ComponentDefinition

since 0.23.0source

A reusable layer tree, stored once per document and shown in the Assets panel, never on a page. Its layers live in DesignDocument.nodes under rootId, so every node operation, undo and validation works on them unchanged. Instances (InstanceNode) show it on pages.

readonly id: ComponentIdsource

Matches the key in DesignDocument.components.

readonly key: stringsource

A stable identity separate from id, a UUID string, unique in the document. Paste between documents and, later, libraries match components by key, so a component edited after a paste is still recognised as the same component. It never changes.

readonly name: stringsource

Shown in the Assets panel. Must not be empty.

readonly description?: stringsource

Optional free text shown in the Assets panel.

readonly rootId: NodeIdsource

The root of the component’s layer tree: always a frame with no parent that no page uses. Its frame.x and frame.y are 0; its width and height are the natural size, which is the size shown in the component view. Its layout sizing (Hug or Fixed) is the size behaviour every instance starts with; see LayerOverride.sizing.

readonly properties?: readonly ComponentPropertyDefinition[]since 0.24.0source

The component’s properties, in the order the instance section lists them. IDs and names are unique within the component. Omitted when empty.

readonly metadata?: Readonly<Record<string, JsonValue>>source

Free-form JSON data kept with the component. The model does not read it.

type ComponentId = Brand<string, "ComponentId">

since 0.23.0source

The ID of a ComponentDefinition, its key in DesignDocument.components.

const componentId: (value: string) => ComponentId

since 0.23.0source

Brands a string as a ComponentId. Throws a TypeError for an empty string.

interface InstanceNode extends FramedNodeBase

since 0.23.0source

One use of a component on a page, or inside another component. It stores a reference and its overrides; its layers are derived when the page is compiled (see resolveInstance) and get virtual IDs <instance id>;<layer path> (see parseVirtualNodeId).

The instance has no children; isContainerNode is false for it. Its NodeBase fields apply to the whole instance, like a frame’s, and its frame, rotation, flips and resizing place it like any framed node. frame holds the resolved size, so geometry, selection and snapping code works without the resolver; applyOperations keeps hugging axes equal to the resolved size (from step 1.2). The instance’s own paints come from the component root and are overridden under the component root’s path.

readonly type: "instance"source
readonly componentId: ComponentIdsource
readonly overrides?: Readonly<Record<string, LayerOverride>>source

Changes to the component’s layers, keyed by layer path: the component node IDs from the component root down to the layer, joined by ;, without the instance ID. Omitted when empty. A key naming a node the component no longer has is allowed and has no effect.

readonly propertyValues?: ComponentPropertyValuessince 0.24.0source

The instance’s values for the component’s properties, keyed by property ID. Missing means the default; a value equal to the default is still kept, so Reset is what makes the instance follow the default again. Omitted when empty.

interface LayerOverride

since 0.23.0source

The changes one instance makes to one layer of its component, stored in InstanceNode.overrides under the layer’s path. Every field replaces the layer’s resolved value as a whole; a field that is absent follows the component. Structure (layers, layout kind and spacing, geometry of inner layers, masks, blend modes, names) is not overridable: that needs an edit to the component, or Detach.

Precedence for one field of one layer, decided per field:

  1. A variable ID in variableBindings for the field’s property: the variable’s value wins.
  2. Otherwise a plain value in this override for the field (or null in variableBindings for its property): the override value wins, and the component layer’s own binding for that property is ignored in this instance. Overriding a field therefore never gets repainted by the component’s binding.
  3. Otherwise the component layer’s own binding, then its stored value.

When instances nest, the override stored on the outer instance wins over the one stored on the nested instance node inside the component, which wins over the component’s value, again field by field.

readonly visible?: booleansource
readonly opacity?: numbersource

From 0 (transparent) to 1 (opaque).

readonly characters?: stringsource

Text layers: the whole text as plain characters, paragraphs separated by "\n". Every character takes the style of the component’s first run, so a component font change still reaches an instance that only changed its label. Not allowed together with paragraphs.

readonly paragraphs?: readonly TextParagraph[]source

Text layers: the whole content with styled runs, stored only when the user styles part of the text differently from the rest. Not allowed together with characters.

readonly textStyleId?: TextStyleId | nullsource

Text layers: the theme text style every run takes instead of its own; null clears the runs’ style IDs so they use the theme default.

readonly textStyle?: TextStyleOverridessource

Text layers: direct formatting (font, size, weight, line height, letter spacing, colour and the rest) applied on top of every run’s style.

readonly styleId?: NodeStyleId | nullsource

Frames and shapes: the node style to use; null clears the layer’s.

readonly fills?: readonly Paint[]source
readonly strokes?: readonly Stroke[]source
readonly shadows?: readonly Shadow[]source
readonly layerBlur?: numbersource
readonly backgroundBlur?: numbersource
readonly imageAssetId?: AssetIdsource

Image layers: the image asset to show instead.

readonly componentId?: ComponentIdsource

Nested instance layers: the component to show instead (a swap).

readonly sizing?: LayerSizingOverridesource

Frame layers, and the component root for the instance’s own size.

readonly variableBindings?: LayerOverrideVariableBindingssource
readonly propertyValues?: ComponentPropertyValuessince 0.24.0source

Nested instance layers: property values keyed by property ID, each replacing the value the nested instance has in the component (stored or computed). Overrides are constants, never expressions.

interface LayerSizingOverride

since 0.23.0source

Which axes of a layer inside an instance hug their content or keep a fixed size, replacing the component frame’s layout.widthSizing and layout.heightSizing. Under the component root’s path it is the instance’s own size behaviour: resizing an instance on the canvas stores "fixed" on that axis, and Reset size removes the entry, so the instance follows the component root again. Fill is not stored here: an instance fills its parent through its own resizing, like any framed node.

readonly width?: "hug" | "fixed"source
readonly height?: "hug" | "fixed"source

type LayerOverrideVariableBindings = Readonly< Partial<Record<VariableProperty, VariableId | null>> >

since 0.23.0source

Variable bindings an instance stores for one layer, keyed by property. A variable ID binds the property in that instance; null removes the component’s binding for that property in that instance and leaves the plain value (the layer’s, or the override’s) in force.

const LAYER_PATH_SEPARATOR: ";"

since 0.23.0source

The character that joins the IDs in a layer path and a virtual node ID. Stored node IDs never contain it.

interface VirtualNodeId

since 0.23.0source

The parts of a virtual node ID: the stored instance the layer belongs to and the layer path inside it.

readonly instanceId: NodeIdsource

The stored InstanceNode on a page or inside a component.

readonly path: readonly NodeId[]source

The component node IDs from the instance down to the layer, in order. Component roots that are passed through are left out; a component root is only named when it is the addressed layer itself (for the instance’s own paints and size). Never empty.

const isVirtualNodeId: (id: string) => boolean

source

True when id names a layer inside an instance rather than a stored node.

function parseVirtualNodeId(id: string): VirtualNodeId | null

since 0.23.0source

Splits a virtual node ID <instance id>;<layer path> into its instance and layer path. Returns null for a stored node ID (no separator) and for a malformed ID with an empty segment. This is the only place that splits virtual IDs.

function virtualNodeId(instanceId: NodeId, path: readonly NodeId[]): NodeId

source

Builds the virtual node ID of the layer at path inside instanceId.

function layerPathKey(path: readonly NodeId[]): string

source

The key of InstanceNode.overrides for the layer at path.

function parseLayerPath(key: string): readonly NodeId[] | null

since 0.23.0source

Splits an override key into its component node IDs, or returns null when the key is malformed (empty, or with an empty segment). Whether the named nodes still exist is not checked: such a key is allowed and has no effect.

function storedNodeIdOf(id: NodeId): NodeId

source

The stored node an ID refers to: the instance for a virtual ID, the ID itself otherwise. Selection code uses it to find the node that moves, deletes or reorders when a layer inside an instance is selected.

function componentRootIds(document: DesignDocument): readonly NodeId[]

source

The root node IDs of every component, in components order.

function findComponentByRoot( document: DesignDocument, rootId: NodeId ): ComponentDefinition | undefined

source

The component whose root is rootId, if any.

function componentOwningNode( document: DesignDocument, nodeId: NodeId, parents: ReadonlyMap< NodeId, NodeId > = buildParentMap(document.nodes) ): ComponentDefinition | undefined

source

The component whose layer tree contains the stored node nodeId (the root included), or undefined when the node is on a page or does not exist. Pass parents (from buildParentMap) when calling this for many nodes.

function isComponentLayer(document: DesignDocument, nodeId: NodeId): boolean

source

True when the stored node is a component layer (its root included) rather than a page node.

function instanceNodeOf( document: DesignDocument, instanceId: NodeId ): InstanceNode | undefined

source

The node an instance ID names, checked to be an InstanceNode.

function componentReferencesOf(node: SceneNode): readonly ComponentId[]

source

The components a node refers to: an instance’s componentId, and every component its overrides swap a nested instance to. Empty for other nodes.

function instancesOfComponent( document: DesignDocument, componentId: ComponentId ): readonly NodeId[]

source

The stored instance nodes of componentId, by ID, sorted. Swaps in overrides do not count.

interface ComponentNestingGraph

since 0.23.0source

Which components contain which. nests.get(a) holds every b such that a node under a’s root is an instance of b, or an override under a’s root swaps a nested instance to b. nestedBy is the reverse. Every component appears as a key of both maps, with an empty set when it has no edges. Instances on pages add no edges.

readonly nests: ReadonlyMap<ComponentId, ReadonlySet<ComponentId>>source
readonly nestedBy: ReadonlyMap<ComponentId, ReadonlySet<ComponentId>>source

function componentNestingGraph(document: DesignDocument): ComponentNestingGraph

source

Builds the ComponentNestingGraph of a document. Missing component references are kept as edges.

function findComponentCycle( graph: ComponentNestingGraph ): readonly ComponentId[] | null

since 0.23.0source

Finds a cycle in the nesting graph: a list of component IDs where each nests the next and the last equals the first. Returns null when the graph has no cycle.

function componentsNesting( graph: ComponentNestingGraph, componentId: ComponentId ): ReadonlySet<ComponentId>

source

Every component whose tree contains componentId, directly or through nesting. A change to componentId also changes the instances of these.

function componentsNestedIn( graph: ComponentNestingGraph, componentId: ComponentId ): ReadonlySet<ComponentId>

source

Every component that componentId’s tree contains, directly or through nesting.

function wouldNestCycle( document: DesignDocument, hostId: ComponentId, placedId: ComponentId ): boolean

since 0.23.0source

Reports whether placing an instance of placedId inside hostId’s tree, or swapping a nested instance there to placedId, would make a component contain itself. The editor disables such choices; assertDocument rejects the result.

interface ResolvedLayer

since 0.23.0source

One derived layer of a resolved instance. node is the component node with the instance’s overrides applied (by the precedence on LayerOverride) and its frame laid out for the instance’s size, so the compiler compiles it exactly as it compiles a stored node, with virtualId as the render node ID.

  • node.id equals virtualId, and a container’s children list holds the virtual IDs of children, in paint order.
  • path names the nested instance nodes passed through and then the layer itself; frames passed through inside one component are left out, as component roots are, since node IDs are unique in the document.
  • The root layer stands for the instance itself: its virtualId is the instance’s own ID, its path is [component root ID] (the override key for the instance’s paints and size), and its node is a FrameNode carrying the instance’s NodeBase fields, frame, rotation, flips and resizing, with the component root’s layout, style and clipContent (overrides applied). instance names the stored instance. A nested instance layer’s path is the path to the nested instance node, which is also its override key on the outer instance for its paints and sizing, as editTargetFor writes it.
  • A nested instance inside the component is expanded the same way: its node is a FrameNode built like the root layer (the nested instance node’s placement and NodeBase fields, after the outer overrides, with the nested component root’s layout, style and clipContent), instance gives the nested instance and the component it shows after any swap, and children are the nested component’s layers, whose paths continue through it.
readonly virtualId: NodeIdsource
readonly sourceNodeId: NodeIdsource

The component node this layer is derived from.

readonly path: readonly NodeId[]source

The layer path inside the outermost instance; see VirtualNodeId.path.

readonly node: SceneNodesource
readonly children: readonly ResolvedLayer[]source
readonly instance?: ResolvedLayerInstancesource

Present on the root layer and on nested instance layers.

readonly inheritedVariableModes?: VariableModessince 0.26.0source

The variable modes the layer inherits from its parent layer, as inheritedVariableModes gives them for a stored node; the layer’s own choices (an instance’s, for the root and nested instance layers) are not included. Present on the layers of a resolved instance.

readonly variableModes?: VariableModessince 0.26.0source

The variable modes in effect at the layer, which its bound fields read. Present on the layers of a resolved instance.

interface ResolvedLayerInstance

since 0.23.0source

The stored instance a resolved layer stands for, set on the root layer and on every nested instance layer.

readonly instanceId: NodeIdsource

The stored InstanceNode: the outer instance, or a nested one inside a component.

readonly componentId: ComponentIdsource

The component shown, after any swap by an outer override.

readonly node: InstanceNodesince 0.26.0source

The instance node as resolved: swapped, with outer overrides and the enclosing component’s computed fields (its own propertyValues included) applied. The component view draws a nested instance from it.

readonly propertyValues?: ComponentPropertyValuessince 0.26.0source

The property values the component is resolved with: the stored instance’s own, or for a nested instance its outer override, else its expression over the outer properties, else its stored value. Absent when none is set (every property at its default).

interface ResolvedInstance

since 0.23.0source

What resolveInstance returns: the instance’s derived layer tree.

readonly instanceId: NodeIdsource
readonly componentId: ComponentIdsource
readonly root: ResolvedLayersource

The layer standing for the instance itself; see ResolvedLayer.

readonly layers: ReadonlyMap<NodeId, ResolvedLayer>source

Every resolved layer by virtual ID, the root under the instance ID.

readonly componentIds: readonly ComponentId[]source

The components this instance draws, its own first, then every nested one after swaps, without repeats. A change to any of them changes the result.

readonly expressionErrors: readonly ComponentExpressionError[]since 0.25.0source

Computed values this instance’s property values make invalid, such as a font family with no font in the document, for every component it draws; each such field shows its stored value.

interface InstanceResolveCache

since 0.23.0source

Resolved trees kept between calls so a component is resolved once for all its instances (design 4.3). One cache belongs to one document line; call clear when the document is replaced.

A component’s version rises when any node in its own layer tree is a new object; an entry is reused only while every component it draws keeps its version and the theme and assets are the same objects. An instance entry also needs the instance node itself to be the same object.

clear(): voidsource
readonly stats: { readonly hits: number; readonly misses: number; readonly evaluations: number; }source

How many resolveInstance and resolveComponent calls were served from the cache, and how many times a component’s conditions and computed fields were evaluated (evaluations, since 0.25.0): once per component, component version, variables and property values with defaults filled in, however many instances share them.

function createInstanceResolveCache(): InstanceResolveCache

source

Makes an InstanceResolveCache to pass to resolveInstance.

interface ResolveInstanceOptions

since 0.23.0source
readonly cache?: InstanceResolveCachesource
readonly variableModes?: VariableModessince 0.26.0source

The variable modes the instance inherits from its parent, as inheritedVariableModes returns them. Read from the document when omitted; the page compiler passes them as it walks the page.

function resolveInstance( document: DesignDocument, instanceId: NodeId, options?: ResolveInstanceOptions ): ResolvedInstance

since 0.23.0source

Derives the layers an instance shows: virtual IDs, overrides applied by precedence, nested instances expanded, and the component root’s auto layout run for the instance’s size and overridden text. A hugging axis takes its natural size, whatever the stored frame says; a fixed axis and an axis the instance fills keep the stored size.

With component properties (design 8.6, since 0.25.0), each component in the instance is resolved in this order: conditions leave layers out (with their sublayers, taking no space in auto layout), computed fields are evaluated for the instance’s property values and written into the model fields, overrides go on top (an override wins over a computed value), and then layout runs. A nested instance’s property values are its outer override, else its expression over the outer properties, else its stored value, else the nested component’s default. An expression that does not check is skipped (see componentExpressionErrors). A root without auto layout lays out a resized instance by its children’s constraints (constrainChildren).

Throws a ModelError with code INVALID_TREE when instanceId is not an instance node or its component is missing.

function resolveComponent( document: DesignDocument, componentId: ComponentId, options?: ResolveInstanceOptions ): ResolvedLayer

since 0.23.0source

Derives the component view: the component’s own layer tree at its natural size with no overrides, as ResolvedLayers whose virtualIds are the stored node IDs. Isolation mode draws it; a nested instance inside it is expanded as in resolveInstance, under its own stored ID. A component with properties or expressions shows its default values (design 8.9): conditions and computed fields are evaluated with the defaults, and its auto layout frames are laid out again.

function huggedInstanceSize( document: DesignDocument, instanceId: NodeId, options?: ResolveInstanceOptions ): { readonly width?: number; readonly height?: number }

since 0.23.0source

The size an instance takes on each axis it hugs, from its resolved root. undefined on an axis the instance keeps as stored (Fixed, Fill, or a component root without auto layout).

function cascadeComponentChanges( document: DesignDocument, nodes: Record<string, SceneNode>, changedNodeIds: ReadonlySet<NodeId>, changedComponentIds: readonly ComponentId[], changed: Set<NodeId> ): void

source

The component cascade of one edit (design question D): after the ordinary layout pass, refits every instance whose component changed (or that nests a changed component, or that the edit touched itself) to its hugging size, drops its override keys that name deleted layers, and lays out its auto layout ancestors again, on pages and inside components. Nested components go first, so a component that nests a changed one is laid out with the new nested sizes before its own instances are refitted.

nodes is the working copy after the operations and the layout pass; it is changed in place, and every node it changes is added to changed. changedNodeIds are the nodes the operations and the layout pass changed.

interface OverrideRemapTree

since 0.23.0source

One component’s layer tree as a remap or prune reads it: the document that holds the nodes and the component’s root. Swap passes one document with two roots; redefine and paste pass the old and the new document.

readonly document: DesignDocumentsource
readonly rootId: NodeIdsource
readonly shownComponentAt?: (path: readonly NodeId[]) => ComponentId | undefinedsince 0.26.0source

The component the nested instance at a layer path (inside the instance whose overrides are remapped) shows, for when its component is computed from a property value. Without it, or when it returns undefined, the nested instance node’s stored component counts. An override swap at the path still wins.

function remapOverrides( overrides: | Readonly<Record<string, LayerOverride>> | undefined, from: OverrideRemapTree, to: OverrideRemapTree, mapComponent: ( id: ComponentId ) => ComponentId = (id) => id ): Record<string, LayerOverride>

since 0.23.0source

Carries an instance’s overrides over to another component tree by layer name and position (design 3.5): each key’s layers are matched from the root down by name at the same index among their siblings, else by the first sibling with that name; a nested instance segment continues into the component it shows. Keys with no match are dropped, and a key that is remapped onto another key’s target is dropped as well (the earlier key wins). Property values on a nested instance’s entry are carried to the component that layer shows on the other side (carryPropertyValues, with mapComponent for instance swap values). Swap, redefine and paste use this, then store the result.

function overridePathResolves( document: DesignDocument, componentId: ComponentId, path: readonly NodeId[], overrides: Readonly<Record<string, LayerOverride>> = {}, shownComponentAt?: (path: readonly NodeId[]) => | ComponentId | undefined ): boolean

since 0.23.0source

Whether an override key names a layer the component still has: every segment but the last must be a nested instance node in the tree entered so far (after any swap in overrides, else the component shownComponentAt gives), and the last a node in that tree.

function pruneOverrides( document: DesignDocument, instance: InstanceNode ): InstanceNode

since 0.23.0source

The instance without the override keys that name layers its component no longer has (design 3.5); the same node when every key still resolves.

type OverrideField = keyof LayerOverride

since 0.23.0source

The fields an instance can override; the keys of LayerOverride.

const OVERRIDE_FIELDS: readonly OverrideField[]

source

Every OverrideField, in LayerOverride order.

type EditTarget = | { readonly kind: "node"; readonly nodeId: NodeId; readonly node: SceneNode } | { readonly kind: "override"; readonly instanceId: NodeId; readonly instance: InstanceNode; readonly key: string; readonly path: readonly NodeId[]; readonly sourceNodeId: NodeId; readonly field: OverrideField; } | { readonly kind: "unsupported"; readonly reason: string }

since 0.23.0source

Where an edit to one field of one selected ID goes.

  • node: a stored node on a page or inside a component (editing in isolation changes the component). Write it with node.replace.
  • override: a layer inside an instance, or an instance’s own paints and size. Write it with setOverrideOperations (a node.replace of the instance with the changed overrides[key]); sourceNodeId is the component node the layer is derived from, for reading the base value.
  • unsupported: the edit cannot be made here; reason says why, for the user (a structural field on a layer inside an instance, or a layer the component no longer has).

function editTargetFor( document: DesignDocument, id: string, field: string ): EditTarget

since 0.23.0source

Decides where an edit goes. id is a stored node ID or a virtual ID (<instance>;<path>); field is the layer field being edited: an OverrideField, a node field name with the same meaning (assetId for imageAssetId), or variableBindings.<property> for one binding.

A virtual ID always targets an override on its outermost instance, so an edit to a layer of a nested instance is stored on the instance the user selected through. Fields that are not in LayerOverride (position, size, rotation, name, layout, masks, blend mode) are unsupported on a virtual ID: they need an edit to the component, or Detach.

type LayerOverridePatch = { readonly [Field in OverrideField]?: LayerOverride[Field] | undefined }

since 0.23.0source

A change to one override entry: each present key replaces that field, and a key set to undefined removes it (Reset for that field).

function withOverride( instance: InstanceNode, key: string, patch: LayerOverridePatch ): InstanceNode

since 0.23.0source

Returns the instance with patch merged into overrides[key]. Setting characters removes paragraphs and the other way round, so the two never coexist. An entry left with no fields is removed, and an empty overrides map is dropped. Pure: the input is not changed.

function withoutOverride( instance: InstanceNode, key?: string, field?: OverrideField ): InstanceNode

since 0.23.0source

Returns the instance without an override: one field of one layer, one whole layer (field omitted), or every override (key omitted). Pure.

function setOverrideOperations( document: DesignDocument, instanceId: NodeId, key: string, patch: LayerOverridePatch ): readonly DocumentOperation[]

since 0.23.0source

The operations that write patch into overrides[key] of the instance: one node.replace, or none when the instance is not an instance node. applyOperations drops a replace that changes nothing.

function resetOverrideOperations( document: DesignDocument, instanceId: NodeId, key?: string, field?: OverrideField ): readonly DocumentOperation[]

since 0.23.0source

The operations that reset overrides of the instance: one field of one layer, one layer, or all (see withoutOverride). An axis that hugs again loses its stretching constraint when the operations are applied, as after every edit (applyOperations).

const componentSampleIds: { readonly document: DocumentId; readonly page: PageId; readonly pageRoot: NodeId; readonly textStyle: TextStyleId; readonly button: ComponentId; readonly buttonRoot: NodeId; readonly buttonLabel: NodeId; readonly buttonIcon: NodeId; readonly card: ComponentId; readonly cardRoot: NodeId; readonly cardTitle: NodeId; readonly cardButton: NodeId; readonly iconButton: ComponentId; readonly iconButtonRoot: NodeId; readonly iconButtonIcon: NodeId; readonly buttonInstance: NodeId; readonly secondButtonInstance: NodeId; readonly cardInstance: NodeId; readonly colors: VariableCollectionId; readonly lightMode: VariableModeId; readonly primary: VariableId; readonly danger: VariableId; }

since 0.23.0source

The IDs the component sample documents use, so tests can refer to them by name. Layer paths for overrides are built from the node IDs, for example layerPathKey([componentSampleIds.cardButton, componentSampleIds.buttonLabel]).

const componentSampleDocuments: { readonly plain: () => DesignDocument; readonly nested: () => DesignDocument; readonly textOverride: () => DesignDocument; readonly variableBoundOverride: () => DesignDocument; readonly swappedNested: () => DesignDocument; }

since 0.23.0source

Every component sample by name, for tests that run over all of them.

function plainInstanceSample(): DesignDocument

since 0.23.0source

One Button component and one instance of it on the page, with no overrides. The instance is at the component’s natural size.

function nestedInstanceSample(): DesignDocument

since 0.23.0source

A Card component whose tree holds an instance of Button, and one Card instance on the page. The nesting graph has the edge Card → Button.

function textOverrideSample(): DesignDocument

since 0.23.0source

Two Button instances with text overrides: button-1 changes the label’s characters only, and button-2 stores styled paragraphs because part of its label is bold. Both keep the component’s font.

function variableBoundOverrideSample(): DesignDocument

since 0.23.0source

The Button label’s colour is bound to the primary variable in the component. The instance rebinds it to danger for the label, and for the root it overrides the fills with a plain colour while removing the component’s fillColor binding, so the plain override cannot be repainted by the variable.

function swappedNestedInstanceSample(): DesignDocument

since 0.23.0source

The nested sample plus an Icon button component. The Card instance swaps its nested Button to Icon button, and keeps a characters override under the old label’s path, which Icon button does not have: the entry is allowed and has no effect.

### Isolation mode

Editing a component in isolation (design 5.5) keeps a stack of IsolationLevels, one per opened component. isolationEditRoute applies the edit rules: in the instance view an edit changes the override that wins at that place when the opened instance side already overrides the field, and the component otherwise; in the component view it always changes the component. Its reach says whether the edit changes only this instance or a component and how many instances that has. isolationViews is what the canvas draws besides the instance in place: the component views, laid out as a grid inside one set outline when there are several, never saved or exported. The editor draws them in the split’s component pane.

interface IsolationLevel

since 0.23.0source

One level of isolation mode (design 5.5): the component being edited and the instance it was opened from.

Levels form a stack. The first level opens a component from an instance on a page (or from the Assets panel, with no instance); each deeper level opens the component of a nested instance seen in the level above.

readonly componentId: ComponentIdsource

The component edited at this level. For a swapped nested instance, the component it is swapped to.

readonly instanceId: NodeId | nullsource

The instance the level was opened from, drawn as the instance view, or null when the level was opened from the Assets panel (only the component view is shown). It is a stored instance ID (an instance on a page, or a nested instance node inside the component view of the level above) or a virtual ID (<instance>;<path>, a nested instance as it appears inside the instance view of the level above).

type IsolationView = "instance" | "component"

since 0.23.0source

Which of the two isolation views an edit is made in (design 5.5): the instance view shows the opened instance with its overrides and uses virtual IDs; the component view shows the component’s own nodes by their stored IDs.

type EditReach = | { readonly kind: "this-instance"; readonly instanceId: NodeId } | { readonly kind: "component"; readonly componentId: ComponentId; readonly instanceCount: number; }

since 0.23.0source

How far an edit reaches (question F in the components plan): only the opened instance, or a component and so every instance of it. The editor shows “Only this instance” or “Changes the component (N instances)”.

type IsolationEditRoute = | { readonly kind: "edit"; readonly target: WritableTarget; readonly overridden: boolean; readonly reach: EditReach; } | { readonly kind: "unsupported"; readonly reason: string }

since 0.23.0source

Where an edit made in isolation goes (design 5.5, Edit rules).

  • edit: write target as editTargetFor describes it. overridden is true when the edit changes an override that wins at that place on the instance side of the level (the first row of the design’s table); the editor marks such fields “Overridden”. reach is the question F signal.
  • unsupported: the edit cannot be made in this view; reason is for the user.

function isolationEditRoute( document: DesignDocument, level: IsolationLevel, view: IsolationView, id: NodeId, field: string ): IsolationEditRoute

since 0.23.0source

Where an edit to field of the layer id, made in view at isolation level level, goes (design 5.5, Edit rules and nesting). field is as for editTargetFor: an override field, a node field name, variableBindings.<property>, or textStyle.<member> for one text style member (routed by that member and its own binding only).

Component view: id is a stored node of the level’s component, or a virtual ID of a layer inside a nested instance node of it. The edit always changes the component; this is editTargetFor’s answer, checked to stay inside the component.

Instance view: id is the level’s instance ID (its root) or a virtual ID under it. The overrides that can apply at that place are those stored on the level’s instance and on every instance it is nested in (the instance side). When the override whose value shows there (by the resolver’s precedence: a binding beats a plain value, then outermost first) is one of them, the edit changes that override: only this instance when that instance is on a page, or the component that holds it when it is a nested instance node inside a component. Otherwise the edit changes the level’s component, as the same edit in the component view would. Isolation never creates an override (design 5.5). Geometry, structure and other fields that cannot be overridden always go to the component.

function componentOfInstance( document: DesignDocument, instanceId: NodeId ): ComponentId | undefined

since 0.23.0source

The component an instance shows: a stored instance’s componentId, or for a virtual ID naming a nested instance, the component it is swapped to by the override that wins at that place (outer instance first), else the nested instance node’s own componentId.

A nested instance whose componentId is computed from the properties of the component that holds it (an instance swap property) shows the computed component, unless an override swaps it. That one is read from the tree the instance resolver draws, so it is the property values of the instance on the page, or in a component’s own view the property defaults. A nested instance that is not drawn (a condition leaves it out) has its stored component.

undefined when the ID does not name an instance or the component is missing.

function componentInstanceCount( document: DesignDocument, componentId: ComponentId ): number

since 0.23.0source

How many instances an edit to the component reaches, for “Editing affects N instances”: every stored instance node of the component in the document, on pages and inside other components. A nested instance inside a component counts once, however many instances of the outer component show it, and swaps in overrides do not count (as instancesOfComponent).

interface IsolationBreadcrumb

since 0.23.0source

One crumb of the isolation bar: the page, then one per level.

readonly label: stringsource
readonly depth: numbersource

How many levels stay open when the crumb is chosen: 0 for the page.

function isolationBreadcrumbs( document: DesignDocument, pageName: string, levels: readonly IsolationLevel[] ): readonly IsolationBreadcrumb[]

since 0.23.0source

The isolation bar’s breadcrumbs: page › component › nested component.

interface ComponentView

since 0.23.0source

One component view: the component drawn by its stored node IDs at its natural size (the root’s frame size), with no overrides, under the label “Component”. “Show all variants” adds one view per combination of property values, laid out beside it inside one set outline (design 8.2).

readonly componentId: ComponentIdsource
readonly rootId: NodeIdsource

The component root; the view draws the component’s layer tree from it.

readonly label: stringsource
readonly bounds: Rectsource

The view’s place at the natural size, in the coordinates of the set of views: the first view (the defaults) is at 0,0.

readonly variant?: { readonly values: ComponentPropertyValues; readonly key: string; }since 0.26.0source

A variant view’s property values (the ones that differ from the defaults) and its combination key; absent for the view at the defaults, which draws the stored nodes.

interface IsolationViewsOptions

source

What isolationViews draws besides the component view.

readonly vary?: readonly ComponentPropertyId[]since 0.26.0source

“Show all variants”: the enum and boolean properties to vary. One view per combination is drawn when there are at most VARIANT_VIEW_CAP; otherwise only the component view.

readonly also?: ComponentPropertyValuessince 0.27.0source

Property values whose view is drawn as well, whatever vary holds: the opened instance’s, so the variant it shows can be edited beside the defaults. Adds nothing when they are the defaults or already drawn.

interface IsolationViews

since 0.23.0source

What the canvas draws for one isolation level. The views are not part of the document; they are never saved or exported.

readonly componentId: ComponentIdsource
readonly instanceId: NodeId | nullsource

The instance view: the opened instance, drawn in place with its overrides; null from the Assets panel.

readonly componentViews: readonly ComponentView[]source

The component views, the defaults first, in reading order (row by row, left to right).

readonly setBounds?: Rectsince 0.27.0source

The outline around the views when more than one is drawn, in the same coordinates as their bounds: the views with COMPONENT_SET_PADDING around them, as Figma outlines a component set. Absent with one view.

const COMPONENT_VIEW_GAP: 40

source

The gap between two views, in canvas units.

const COMPONENT_SET_PADDING: 40

since 0.27.0source

The space between the set outline and the views inside it, in canvas units.

function isolationViewCombinations( component: ComponentDefinition, options?: IsolationViewsOptions ): readonly VariantCombination[]

since 0.27.0source

The combinations isolationViews draws for options, in drawing order: the defaults alone with no options; every combination of vary; and the combination of also after them when it is not one of those already.

function isolationViews( document: DesignDocument, level: IsolationLevel, options?: IsolationViewsOptions ): IsolationViews | undefined

since 0.23.0source

The views to draw for level, laid out as a grid inside one set outline: with one varied property (or none) the views are one row; with more, the last varied property’s values are the columns and every combination of the others is a row. A column is as wide as its widest view and a row as tall as its tallest, so the views line up. A view added by options.also goes at the end of the first row.

Export settings

Each node can carry rows for its Export section. These helpers check the rows, pick defaults, and build file names.

interface ExportSetting

since 0.14.0source

One row of a layer’s Export section. Every field is always stored so a row keeps its values when its format changes; a field that does not apply to the current format is ignored when exporting.

readonly format: ExportFormatsource
readonly constraint: ExportConstraintsource

PNG and JPEG only; SVG and PDF always export at 1x.

readonly suffix: stringsource

Added to the file name after the layer name; may be empty.

readonly quality: ExportQualitysource

JPEG and PDF only.

readonly resampling: ExportResamplingsource

PNG, JPEG and PDF only.

readonly ignoreOverlappingLayers: booleansource

PNG, JPEG and SVG only. When true only the layer and its children are drawn; when false every visible layer inside the layer’s bounds is drawn.

readonly includeIdAttribute: booleansource

SVG only: gives drawn elements “id” attributes made from layer names.

type ExportFormat = (typeof EXPORT_FORMATS)[number]

source

const EXPORT_FORMATS: readonly ["png", "jpeg", "svg", "pdf"]

since 0.14.0source

File formats a layer can export to.

type ExportConstraint = | { readonly type: "scale"; readonly value: number } | { readonly type: "width"; readonly value: number } | { readonly type: "height"; readonly value: number }

since 0.14.0source

The output size of a raster export: a multiplier of the layer’s size, or a fixed pixel width or height with the other side kept in proportion.

type ExportQuality = (typeof EXPORT_QUALITIES)[number]

source

const EXPORT_QUALITIES: readonly ["low", "medium", "high"]

since 0.14.0source

JPEG compression level, or the raster resolution of PDF effects.

type ExportResampling = (typeof EXPORT_RESAMPLINGS)[number]

source

const EXPORT_RESAMPLINGS: readonly ["detailed", "basic"]

since 0.14.0source

“detailed” samples images bicubically; “basic” uses the nearest pixel.

const MAX_EXPORT_SCALE: 100

source

Largest multiplier a scale constraint may hold.

const MAX_EXPORT_DIMENSION: 16384

source

Largest pixel width or height a size constraint may hold; the renderer’s own limit.

const MAX_EXPORT_SUFFIX_LENGTH: 100

source

Longest suffix, in UTF-16 code units.

const MAX_EXPORT_SETTINGS: 64

source

Most rows one node may hold.

function defaultExportQuality(format: ExportFormat): ExportQuality

source

The quality a row starts with when it switches to format.

function isVectorExportFormat(format: ExportFormat): format is "svg" | "pdf"

source

True when the format ignores the size constraint and exports at 1x.

function nextExportSetting(existing: readonly ExportSetting[]): ExportSetting

source

The row the Export section’s + button adds. The first row is PNG at 1x with no suffix. Each later row copies the last row’s format and options and takes the next whole scale, with a matching “@Nx” suffix, as Figma does.

function exportConstraintProblem(constraint: ExportConstraint): string | null

source

The reason a constraint is invalid, or null when it is valid.

function exportSuffixProblem(suffix: string): string | null

source

The reason a suffix is invalid, or null when it is valid.

function formatExportConstraint(constraint: ExportConstraint): string

source

A constraint as the scale field shows it: “2x”, “512w” or “512h”.

function parseExportConstraint(text: string): ExportConstraint | null

source

Reads what a person typed in the scale field. “2”, “2x” and “2X” are multipliers, “512w” a fixed width and “512h” a fixed height. Returns null when the text is not a valid constraint.

function exportFileExtension(format: ExportFormat): string

source

The file extension for a format; JPEG files use “.jpg”, as Figma’s do.

function exportFileStem(name: string): string

source

The part of a layer name that is safe in a file name on every common system. Path separators and reserved characters become “-“.

function exportFileName( name: string, setting: Pick<ExportSetting, "format" | "suffix"> ): string

source

<layer name><suffix>.<extension>, as Figma names each exported file.

function uniqueExportFileNames(names: readonly string[]): readonly string[]

source

Makes every file name in one export unique. A repeated name gets “ 2″, “ 3″ and so on before its extension, so no download replaces another.

function uniqueSvgId(name: string, used: Set<string>): string

source

A valid XML id made from a layer name: spaces become “_”, other characters outside letters, digits, “_”, “-” and “.” are dropped, and a name that does not start with a letter or “_” gets a leading “_”. used holds ids already in the file; a repeated id gets “_2”, “_3” and so on. The result is added to used.

Component expressions

The component expression language (docs/components/design.md 8.4 and 8.5) lets a layer field inside a component be computed from the component’s properties and the document’s variables. This part of the package parses source text into an Expression tree with spans, type-checks it against an ExpressionEnvironment (the properties, the variables and what stored reads) and the field’s ExpressionType, and rewrites the source when a property, an enum option or a variable is renamed. EXPRESSION_LIBRARY holds the signatures of every constructor and standard library function. evaluateExpression runs a checked expression against an ExpressionValueEnvironment and always gives an ExpressionValue, which wraps the model’s own types where one exists (design.md 8.4, “Values as the model stores them”); ExpressionCache keeps parsed and checked forms per source text and environment version.

function parseExpression(source: string): ExpressionParseResult

source

Parses source text into an expression tree, or reports the first syntax error.

type ExpressionParseResult = | { readonly ok: true; readonly expression: Expression } | { readonly ok: false; readonly error: ExpressionError }

source

What parseExpression gives back: a tree, or the first error.

const EXPRESSION_SOURCE_LIMIT: 4000

source

The longest expression source the parser accepts, in UTF-16 code units.

const EXPRESSION_DEPTH_LIMIT: 64

source

The deepest nesting of sub-expressions the parser accepts.

type Expression = | { readonly kind: "boolean"; readonly value: boolean; readonly span: ExpressionSpan; } | { readonly kind: "number"; readonly value: number; readonly unit?: ExpressionUnit; readonly span: ExpressionSpan; } | { readonly kind: "string"; readonly value: string; readonly span: ExpressionSpan; } | { readonly kind: "color"; readonly value: ExpressionColor; readonly span: ExpressionSpan; } | { readonly kind: "option"; readonly name: string; readonly span: ExpressionSpan; } | { readonly kind: "name"; readonly name: string; readonly span: ExpressionSpan; } | { readonly kind: "stored"; readonly span: ExpressionSpan } | { readonly kind: "variable"; readonly name: string; readonly nameSpan: ExpressionSpan; readonly span: ExpressionSpan; } | { readonly kind: "call"; readonly callee: string; readonly calleeSpan: ExpressionSpan; readonly args: readonly ExpressionArgument[]; readonly span: ExpressionSpan; } | { readonly kind: "list"; readonly items: readonly Expression[]; readonly span: ExpressionSpan; } | { readonly kind: "field"; readonly target: Expression; readonly name: string; readonly nameSpan: ExpressionSpan; readonly span: ExpressionSpan; } | { readonly kind: "with"; readonly target: Expression; readonly updates: readonly ExpressionFieldUpdate[]; readonly span: ExpressionSpan; } | { readonly kind: "unary"; readonly operator: "-" | "not"; readonly operand: Expression; readonly span: ExpressionSpan; } | { readonly kind: "binary"; readonly operator: ExpressionBinaryOperator; readonly operatorSpan: ExpressionSpan; readonly left: Expression; readonly right: Expression; readonly span: ExpressionSpan; } | { readonly kind: "if"; readonly condition: Expression; readonly then: Expression; readonly else: Expression; readonly span: ExpressionSpan; } | { readonly kind: "let"; readonly name: string; readonly nameSpan: ExpressionSpan; readonly value: Expression; readonly body: Expression; readonly span: ExpressionSpan; } | { readonly kind: "match"; readonly subject: Expression; readonly arms: readonly ExpressionMatchArm[]; readonly otherwise?: Expression; readonly span: ExpressionSpan; }

source

A node of the expression syntax tree. kind tells the construct.

interface ExpressionSpan

source

A half-open range of UTF-16 offsets in an expression’s source text.

readonly start: numbersource
readonly end: numbersource

interface ExpressionError

source

A parse or type error, with the text it points at and an optional fix.

readonly message: stringsource
readonly span: ExpressionSpansource
readonly hint?: stringsource

A short suggested fix, for example “write 8px”.

type ExpressionUnit = "px" | "%" | "deg"

source

The unit suffix of a number literal: 8px, 50%, 45deg.

type ExpressionBinaryOperator = | "or" | "and" | "==" | "!=" | "<" | "<=" | ">" | ">=" | "++" | "+" | "-" | "*" | "/"

source

The two-operand operators, from or (lowest precedence) to / (highest).

interface ExpressionColor

source

An sRGB colour with every channel from 0 to 1, as a #rrggbb[aa] literal parses to.

readonly red: numbersource
readonly green: numbersource
readonly blue: numbersource
readonly alpha: numbersource

interface ExpressionArgument

source

One argument of a call: positional, or named with name: value.

readonly name?: stringsource
readonly nameSpan?: ExpressionSpansource
readonly value: Expressionsource

interface ExpressionFieldUpdate

source

One name: value pair inside with { ... }.

readonly name: stringsource
readonly nameSpan: ExpressionSpansource
readonly value: Expressionsource

type ExpressionPattern = | { readonly kind: "option"; readonly name: string; readonly span: ExpressionSpan; } | { readonly kind: "boolean"; readonly value: boolean; readonly span: ExpressionSpan; }

source

A match arm pattern: an enum option (.small) or a boolean (true).

interface ExpressionMatchArm

source

One arm of a match: one or more patterns and the value they give.

readonly patterns: readonly ExpressionPattern[]source
readonly body: Expressionsource

const EXPRESSION_KEYWORDS: readonly [ "if", "then", "else", "let", "in", "match", "with", "or", "and", "not", "true", "false", "stored", "var" ]

source

The words the language reserves; they cannot name a property or a let.

type ExpressionKeyword = (typeof EXPRESSION_KEYWORDS)[number]

source

function parseColorLiteral(text: string): ExpressionColor | undefined

source

Reads a #rgb, #rgba, #rrggbb or #rrggbbaa literal into channels from 0 to 1.

type ExpressionType = | { readonly kind: "boolean" } | { readonly kind: "number" } | { readonly kind: ExpressionUnitTypeKind } | { readonly kind: "string" } | { readonly kind: "color" } | { readonly kind: "component" } | ExpressionEnumType | { readonly kind: "point" } | { readonly kind: "size" } | { readonly kind: "insets" } | { readonly kind: "corners" } | { readonly kind: "paint" } | { readonly kind: "stroke" } | { readonly kind: "effect" } | { readonly kind: "text_style" } | { readonly kind: "list"; readonly element: ExpressionType }

source

A type of the expression language. Every value has exactly one.

type ExpressionUnitTypeKind = "length" | "percent" | "angle"

source

The unit types: numbers that carry a unit and never mix with each other.

interface ExpressionEnumType

source

An enum type: a named set of options. Two enum types are the same when their names match.

readonly kind: "enum"source
readonly name: stringsource

Unique within one checking environment. The property model gives each enum property its own name; layer field enums use the field’s name.

readonly options: readonly string[]source

Option names in display order, as written after the dot: .large.

const EXPRESSION_TYPES: { readonly boolean: { readonly kind: "boolean" }; readonly number: { readonly kind: "number" }; readonly length: { readonly kind: "length" }; readonly percent: { readonly kind: "percent" }; readonly angle: { readonly kind: "angle" }; readonly string: { readonly kind: "string" }; readonly color: { readonly kind: "color" }; readonly component: { readonly kind: "component" }; readonly point: { readonly kind: "point" }; readonly size: { readonly kind: "size" }; readonly insets: { readonly kind: "insets" }; readonly corners: { readonly kind: "corners" }; readonly paint: { readonly kind: "paint" }; readonly stroke: { readonly kind: "stroke" }; readonly effect: { readonly kind: "effect" }; readonly text_style: { readonly kind: "text_style" }; }

source

The types with a single kind and nothing else, ready to use.

const STROKE_ALIGN_TYPE: ExpressionEnumType

source

The enum type of a stroke’s align argument and field.

function expressionListType(element: ExpressionType): ExpressionType

source

list(element).

function expressionEnumType( name: string, options: readonly string[] ): ExpressionEnumType

source

An enum type with the given name and options.

function expressionTypesEqual(a: ExpressionType, b: ExpressionType): boolean

source

True when a value of type a is also a value of type b. Enums match by name.

function isExpressionUnitType( type: ExpressionType ): type is { readonly kind: ExpressionUnitTypeKind }

source

True for length, percent and angle.

function isExpressionNumeric(type: ExpressionType): boolean

source

True for number and the unit types: what arithmetic, comparison and min accept.

function formatExpressionType(type: ExpressionType): string

source

The type as the language writes it: length, list(paint), Size (.small, .large).

function expressionRecordFields( type: ExpressionType ): Readonly<Record<string, ExpressionType>> | undefined

source

The fields a record type exposes to value.field and value with { field: ... }, or undefined for a type that is not a record. Field names follow the language’s snake_case (top_left, line_height).

const EXPRESSION_LIBRARY: Readonly<Record<string, ExpressionFunction>>

source

Every constructor and standard library function, by name. A null prototype keeps toString and the like out of lookups.

interface ExpressionFunction

source

A constructor or standard library function with its overloads, tried in order.

readonly name: stringsource
readonly group: "numbers" | "colours" | "strings" | "constructors"source
readonly summary: stringsource
readonly overloads: readonly ExpressionSignature[]source

interface ExpressionSignature

source

One way to call a function: its parameters, in order, and its result type.

readonly parameters: readonly ExpressionParameter[]source
readonly rest?: ExpressionTypesource

When set, any number of extra positional arguments of this type may follow the parameters.

readonly result: ExpressionTypesource

interface ExpressionParameter

source

One parameter of a signature. A parameter with a default may be left out.

readonly name: stringsource
readonly type: ExpressionTypesource
readonly defaultSource?: stringsource

The default, as expression source text (0px, .center); absent for a required parameter.

function formatExpressionSignature( name: string, signature: ExpressionSignature ): string

source

A signature as the language writes it: min(a: number, b: number, ...) -> number.

function checkExpression( expression: Expression, expected: ExpressionType, environment: ExpressionEnvironment ): ExpressionCheckResult

source

Type-checks an expression against the type its field expects (boolean for a condition), the component’s properties and the document’s variables.

function checkExpressionSource( source: string, expected: ExpressionType, environment: ExpressionEnvironment ): ExpressionCheckResult

source

Parses and checks source text in one step; a syntax error is the only error reported.

interface ExpressionEnvironment

source

What an expression may read, as the checker sees it. Step 3.2 builds one from a component’s properties and the document’s variables.

propertyType(name: string): ExpressionType | undefinedsource

The type of the component property with this name, or undefined when there is none.

variableType(name: string): ExpressionType | undefinedsource

The type of the document variable written "Collection/Name", or undefined when there is none.

readonly storedType?: ExpressionTypesource

The type of the component’s base value of the field being computed, which is what stored reads. Leave it out for a condition, where stored has no meaning.

interface ExpressionCheckResult

source

The outcome of checking one expression.

readonly ok: booleansource

True when there are no errors.

readonly type?: ExpressionTypesource

The type of the whole expression, when it could be told.

readonly errors: readonly ExpressionError[]source
readonly references: readonly ExpressionReference[]source

Every property, option and variable the expression names, in source order.

type ExpressionReference = | { readonly kind: "property"; readonly name: string; readonly span: ExpressionSpan; } | { readonly kind: "option"; readonly enum: string; readonly name: string; readonly span: ExpressionSpan; } | { readonly kind: "variable"; readonly name: string; readonly span: ExpressionSpan; }

source

A place in the source that names something the editor can rename.

function renameInExpression( source: string, expected: ExpressionType, environment: ExpressionEnvironment, renames: | ExpressionRename | readonly ExpressionRename[] ): ExpressionRenameResult

source

Rewrites every reference to the renamed properties, options or variables in the source text. The environment and expected type describe the field as it is before the rename, so .large can be told apart between enums. Several renames are found in one check of the original source, so a reference half-way through a batch never meets names the environment does not know yet. Source that does not parse is returned unchanged; type errors elsewhere in the expression do not stop the references that do resolve from being rewritten. A let name that shadows a property is not a reference to it.

type ExpressionRename = | { readonly kind: "property"; readonly from: string; readonly to: string } | { readonly kind: "option"; readonly enum: string; readonly from: string; readonly to: string; } | { readonly kind: "variable"; readonly from: string; readonly to: string }

source

What to rename. Names are the ones in force before the rename.

interface ExpressionRenameResult

source

The rewritten source and how many references changed.

readonly source: stringsource
readonly count: numbersource

function expressionSignatureAccepts( signature: ExpressionSignature, positionalCount: number, named: ReadonlySet<string> ): boolean

source

True when this many positional arguments plus these named ones fill the signature without gaps or extras: every parameter is given once, by position, by name or by its default. The checker and the evaluator pick an overload with it.

type ExpressionValue = | { readonly kind: "boolean"; readonly value: boolean } | { readonly kind: "number"; readonly value: number } | { readonly kind: ExpressionUnitTypeKind; readonly value: number } | { readonly kind: "string"; readonly value: string } | { readonly kind: "color"; readonly value: ExpressionColor } | { readonly kind: "component"; readonly id: string } | { readonly kind: "enum"; readonly option: string } | { readonly kind: "point"; readonly x: number; readonly y: number } | { readonly kind: "size"; readonly width: number; readonly height: number } | { readonly kind: "insets"; readonly value: LayoutPadding } | { readonly kind: "corners"; readonly value: CornerRadii } | { readonly kind: "paint"; readonly value: Paint } | { readonly kind: "stroke"; readonly value: Stroke } | { readonly kind: "effect"; readonly value: ExpressionEffect } | { readonly kind: "text_style"; readonly value: ExpressionTextStyle } | { readonly kind: "list"; readonly items: readonly ExpressionValue[] }

source

A value of the expression language. kind matches the type’s kind.

type ExpressionEffect = | { readonly kind: "shadow"; readonly shadow: Shadow } | { readonly kind: "layer_blur"; readonly radius: number } | { readonly kind: "background_blur"; readonly radius: number }

source

One effect. The model keeps shadows in shadows and the two blurs as plain radii, so this is the union step 3.3 sorts into those fields.

type ExpressionTextStyle = & Pick<TextStyle, "fontFamily" | "fontWeight" | "fontSize"> & { readonly lineHeightPixels: number; readonly letterSpacing: number }

source

The parts of a text style an expression can build; the name and colour come from elsewhere. lineHeightPixels is the language’s line_height, a length in pixels. The model’s TextStyle.lineHeight is a multiple of the font size, so whatever applies this value must divide by fontSize first.

function expressionColorToHex(color: ExpressionColor): string

source

The colour as the model stores it: #rrggbb, or #rrggbbaa when the alpha is below 1.

function expressionColorFromHex(text: string): ExpressionColor

source

Reads a model colour string; a string that is not a hex colour reads as opaque black.

function zeroExpressionValue(type: ExpressionType): ExpressionValue

source

The value 0 of a type: false, 0, “”, transparent black, the first option, empty records and lists. It is what a division by zero gives and what a missing value falls back to.

function expressionValuesEqual(a: ExpressionValue, b: ExpressionValue): boolean

source

True when two values of one type are equal, compared by value; what == gives.

function evaluateExpression( expression: Expression, environment: ExpressionValueEnvironment ): ExpressionValue

source

Evaluates an expression that has passed the check. It never throws for such an expression when the environment holds every property, variable and stored value the checking environment typed; a missing one is a programming error and throws, since a wrong value would be a silent failure.

interface ExpressionValueEnvironment

source

What an expression reads while it runs: the counterpart of the checking ExpressionEnvironment, with values in place of types. Every name the checking environment typed must have a value here.

propertyValue(name: string): ExpressionValue | undefinedsource

The value of the component property with this name, or undefined when there is none.

variableValue(name: string): ExpressionValue | undefinedsource

The value of the document variable "Collection/Name" in the active mode, or undefined when there is none.

readonly stored?: ExpressionValuesource

The component’s base value of the field being computed, which stored reads.

function finiteExpressionNumber(value: number): number

source

0 for a number that is not finite (from very large values or 0 / 0); negative zero is plain 0.

function clampExpressionAmount(value: number): number

source

A number held between 0 and 1: what amounts and colour channels become.

class ExpressionCache

source

Keeps parsed and checked expressions per source text for one version of a checking environment. Bump version whenever the properties, the variables or the components change; the cache then starts over.

check( source: string, expected: ExpressionType, environment: ExpressionEnvironment, version: string | number ): CheckedExpressionsource

Parses and checks source against the field’s type, or returns the cached result for this version.

get size(): numbersource

How many expressions are cached for the current version.

clear(): voidsource

Forgets everything.

interface CheckedExpression

source

A parsed and checked expression. expression is absent after a syntax error.

readonly expression?: Expressionsource
readonly result: ExpressionCheckResultsource

The expression editor reads the rest. expressionTypeNotes gives the type expected and found at every node, which completion uses on unfinished text through completeExpression; highlightExpression colours source text that may not parse. expressionValueSource writes a value back as source. readValueTable and readShowWhen recognise exactly the value table and “Show when” forms of design.md 8.8, and writeValueTable, setValueTableRow and writeShowWhen build them again.

function expressionTypeNotes( expression: Expression, expected: ExpressionType, environment: ExpressionEnvironment ): readonly ExpressionTypeNote[]

source

Checks the expression and returns, for every node and match pattern, the type expected there and the type found, in the order the checker visited them. The expression editor reads it for completion (which options fit a . here, which fields a value has) and for the type shown under the text.

interface ExpressionTypeNote

source

What the checker knew about one node or match pattern: the type it expected there, and the type it found.

readonly span: ExpressionSpansource
readonly expected?: ExpressionTypesource

The type the place asks for, when it asks for one (a field, an argument, the other side of ==, a match pattern).

readonly type?: ExpressionTypesource

The node’s type, when it could be told.

function completeExpression( source: string, caret: number, context: ExpressionCompletionContext, explicit = false ): ExpressionCompletion | undefined

source

The completions at caret. After a . that starts an option literal or pattern it offers the options of the enum expected there; after a . that reads a field, the fields of the value’s type; inside var("..."), the variables; in a word, properties, variables written as var("Collection/Name"), functions with their signature and keywords, the ones whose type fits the place first. Names that start with the typed text come before names that only contain it. Returns undefined inside a comment, a colour or another string, or at an empty word unless it follows match or explicit is set (the user asked with Ctrl+Space).

interface ExpressionCompletionContext

source

What completion offers and where it comes from.

readonly expected: ExpressionTypesource

The type the whole expression must have: the field’s type, or boolean for a condition.

readonly environment: ExpressionEnvironmentsource
readonly properties: readonly string[]source

The component’s property names, in panel order.

readonly variables: readonly string[]source

The document’s variables as "Collection/Name".

interface ExpressionCompletion

source

The completions at a caret, for the text between from and to.

readonly from: numbersource
readonly to: numbersource
readonly expected?: ExpressionTypesource

The type expected at the caret, when the checker could tell.

readonly items: readonly ExpressionCompletionItem[]source

interface ExpressionCompletionItem

source

One completion. Choosing it replaces from–to of the completion with insert.

readonly kind: | "property" | "option" | "field" | "variable" | "function" | "keyword"source
readonly label: stringsource

What the list shows.

readonly insert: stringsource
readonly caret?: numbersource

Where the caret goes inside insert (after the ( of a function); the end when absent.

readonly detail?: stringsource

The type, or a function’s signature.

readonly fits: booleansource

True when its type is the type expected at the caret; these come first.

function highlightExpression( source: string, isProperty: (name: string) => boolean ): ExpressionHighlight[]

source

Splits source text into coloured stretches. It never fails: text the lexer would reject (an unknown character, an unfinished string) comes out as invalid or as far as it goes. Names are property when isProperty says so, function before a (, and name otherwise (let names and unknown names).

interface ExpressionHighlight

source

One coloured stretch; from and to are UTF-16 offsets, to exclusive.

readonly from: numbersource
readonly to: numbersource
readonly kind: ExpressionHighlightKindsource

type ExpressionHighlightKind = | "keyword" | "property" | "function" | "name" | "option" | "field" | "number" | "string" | "color" | "comment" | "operator" | "punctuation" | "invalid"

source

What a stretch of source text is, for its colour.

function expressionValueSource(value: ExpressionValue): string | undefined

source

The value as expression source text that evaluates back to it, such as 8px, .large, insets(4px, 8px) or solid(#ff0000, 0.5), or undefined when the language cannot write it: a component (there is no component literal), an image paint, a gradient with uneven stops, a paint with a blend mode, a dashed stroke. Numbers keep four decimals.

function formatExpressionNumber(value: number): string

source

Up to four decimals, without trailing zeros and never in exponent form.

function formatExpressionString(value: string): string

source

A string literal with ", \ and line breaks escaped as the lexer reads them.

function readValueTable( source: string, environment: ExpressionEnvironment ): ExpressionValueTable | undefined

source

Recognises a value table: a match on one enum or boolean property, one pattern per arm, no else, every arm a constant. Rows follow the property’s option order (“on” before “off”); an option the match does not cover yet (added to the enum later) is a row without source. Returns undefined for anything else, which the editor shows as an expression.

function writeValueTable( table: Pick<ExpressionValueTable, "property" | "subject" | "rows"> ): string

source

Writes a value table as a match, one arm per row in row order; rows without source are left out. It fits on one line when short enough, as in match size { .small => 4px, .large => 8px }, and otherwise puts each arm on its own line.

function startValueTable( property: string, subject: ValueTableSubject, rowSource: string ): string

source

A new value table over the property with every row holding rowSource, the field’s current value.

function setValueTableRow( source: string, environment: ExpressionEnvironment, key: string, rowSource: string ): string

source

Replaces one row’s constant. A row that is already written is replaced in place, so the rest of the text (comments, line breaks) stays; a row the match does not cover yet is added by writing the table again. Throws when source is not a value table or key is not one of its rows: the caller only offers rows of a table it read.

function valueTableSubject( property: string, environment: ExpressionEnvironment ): ValueTableSubject | undefined

source

The subject for a property, or undefined when a table cannot vary by it (it is not an enum or boolean).

function valueTableKeys( subject: ValueTableSubject ): readonly { readonly key: string; readonly label: string }[]

source

The rows a value table over this subject has, without values.

function isConstantExpression(expression: Expression): boolean

source

True when the expression reads nothing that changes with property values: no property, stored, if, let or match.

interface ExpressionValueTable

source

A value table read from source text: one row per option, in the property’s option order.

readonly property: stringsource

The property the table varies by.

readonly subject: ValueTableSubjectsource
readonly rows: readonly ValueTableRow[]source

interface ValueTableRow

source

One row of a value table.

readonly key: stringsource

The option name, or "true" / "false" for a boolean.

readonly label: stringsource

What the row shows: the option name, or “on” / “off”.

readonly source?: stringsource

The row’s constant as source text; absent when the match has no arm for it yet.

type ValueTableSubject = | { readonly kind: "enum"; readonly type: ExpressionEnumType } | { readonly kind: "boolean" }

source

What a value table varies by: an enum property’s options, or a boolean’s on and off.

function readShowWhen( source: string, environment: ExpressionEnvironment ): ShowWhenCondition | undefined

source

Recognises a “Show when” condition: showIcon, not showIcon, size == .large, or size == .a or size == .b ... with one property, known options and no option twice. Options come back in the order written. Returns undefined for anything else.

function writeShowWhen(condition: ShowWhenCondition): string

source

Writes a “Show when” condition. Throws for an enum condition without options, which would never show the layer.

type ShowWhenCondition = | { readonly kind: "boolean"; readonly property: string; readonly on: boolean; } | { readonly kind: "enum"; readonly property: string; readonly options: readonly string[]; }

source

A “Show when” condition in one of the forms of design 8.8.

Component properties

A component’s properties (docs/components/design.md 8.2 and 8.7) are named, typed inputs listed in ComponentDefinition.properties; each instance sets values in InstanceNode.propertyValues, keyed by the stable property ID, and an outer instance can set a nested instance’s values in LayerOverride.propertyValues. Layers inside a component can store expressions (source text keyed by ComputableField) and a condition. assertDocument checks only their shape: an expression that does not check is an allowed state, and the field then shows its stored value.

The adapters connect a document to the expression language: componentExpressionEnvironment and componentValueEnvironment give the checking and evaluation environments of a component (its properties, the document’s variables by "Collection/Name" in the active mode, and stored), computableFieldType and storedFieldValue give a field’s expected type and base value, and expressionFieldContext puts them together for one field. A number variable reads as a length when all its scopes are lengths (variableExpressionType). The operation builders keep expressions valid through edits: renames rewrite every affected expression in the same edit; deletions and kind changes remove instance values that no longer apply and report how many expressions use what changed.

type ComponentPropertyDefinition = | (ComponentPropertyBase<"boolean"> & { readonly defaultValue: boolean }) | ( & ComponentPropertyBase<"number"> & { readonly unit: ComponentNumberUnit; readonly defaultValue: number; readonly min?: number; readonly max?: number; readonly step?: number; }) | ( & ComponentPropertyBase<"text"> & { readonly defaultValue: string; readonly multiline?: boolean }) | ( & ComponentPropertyBase<"enum"> & { readonly options: readonly ComponentPropertyOption[]; readonly defaultValue: string; }) | (ComponentPropertyBase<"color"> & { readonly defaultValue: string }) | ( & ComponentPropertyBase<"instance"> & { readonly defaultValue: ComponentId; readonly preferred?: readonly ComponentId[]; })

since 0.24.0source

A named, typed input on a component. Each instance chooses a value for it in InstanceNode.propertyValues, or leaves the default.

interface ComponentPropertyBase<Kind extends ComponentPropertyKind>

since 0.24.0source

Fields every component property has.

readonly id: ComponentPropertyIdsource

Stable and never shown; instance values are keyed by it.

readonly kind: Kindsource
readonly name: stringsource

Unique in the component and an identifier (letters, digits and _, not starting with a digit, not a keyword of the expression language): expressions read the property by this name.

readonly description?: stringsource

type ComponentPropertyKind = (typeof COMPONENT_PROPERTY_KINDS)[number]

since 0.24.0source

One of COMPONENT_PROPERTY_KINDS.

const COMPONENT_PROPERTY_KINDS: readonly [ "boolean", "number", "text", "enum", "color", "instance" ]

since 0.24.0source

The kinds of component property, as ComponentPropertyDefinition.kind.

type ComponentNumberUnit = (typeof COMPONENT_NUMBER_UNITS)[number]

since 0.24.0source

One of COMPONENT_NUMBER_UNITS.

const COMPONENT_NUMBER_UNITS: readonly ["none", "px", "%", "deg"]

since 0.24.0source

The unit of a number property: "none" reads as a number in expressions, "px" as a length, "%" as a percent and "deg" as an angle.

interface ComponentPropertyOption

since 0.24.0source

One option of an enum property. The ID is stable; the name is what expressions write after the dot (.large) and must be an identifier, unique within the property.

readonly id: stringsource
readonly name: stringsource

type ComponentPropertyId = Brand<string, "ComponentPropertyId">

since 0.24.0source

The stable ID of a component property (ComponentPropertyDefinition.id), unique within its component. It never changes, so instance values keyed by it survive a rename.

const componentPropertyId: (value: string) => ComponentPropertyId

since 0.24.0source

Brands a string as a ComponentPropertyId. Throws a TypeError for an empty string.

type ComponentPropertyValue = boolean | number | string

since 0.24.0source

A value an instance sets for a property: a boolean, a number, a string (text), an option ID (enum), a colour string or a component ID (instance swap). A value that does not fit the property’s current kind is ignored, as is a value for a property the component no longer has.

type ComponentPropertyValues = Readonly<Record<string, ComponentPropertyValue>>

since 0.24.0source

Property values keyed by ComponentPropertyId. Missing means the default.

type ComputableField = | (typeof COMPUTABLE_FIELDS)[number] | `propertyValues.${string}`

since 0.24.0source

A field that can hold an expression: a COMPUTABLE_FIELDS name or propertyValues.<property id>.

const COMPUTABLE_FIELDS: readonly [ "visible", "opacity", "fills", "strokes", "effects", "cornerRadius", "cornerRadii", "cornerSmoothing", "width", "height", "minWidth", "maxWidth", "minHeight", "maxHeight", "gap", "wrapGap", "columnGap", "rowGap", "padding", "direction", "align", "justify", "wrap", "characters", "fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textColor", "textStyle", "componentId" ]

since 0.24.0source

The layer fields that can hold an expression. Which ones apply depends on the node type (see computableFieldsOf); propertyValues.<property id> computes a nested instance’s property value. Position, rotation, flips, blend modes, masks and the layer name are not computable.

type NamedComputableField = (typeof COMPUTABLE_FIELDS)[number]

since 0.24.0source

A computable field other than propertyValues.<id>.

type NodeExpressions = Readonly<Partial<Record<ComputableField, string>>>

since 0.24.0source

Expression source text keyed by computed field.

function computableFieldsOf(node: SceneNode): readonly NamedComputableField[]

since 0.24.0source

The named fields a node of this type can compute, in COMPUTABLE_FIELDS order. An instance node can also compute propertyValues.<property id> for any property of its component.

function isComputableField( node: SceneNode, field: string ): field is ComputableField

since 0.24.0source

True when field can hold an expression on this node. It checks the name only: a propertyValues.<id> field naming a property the nested component does not have is allowed and has no effect.

function propertyValueFieldId(field: string): string | undefined

since 0.24.0source

The property ID in a propertyValues.<property id> field, or undefined for any other field.

const PROPERTY_VALUE_FIELD_PREFIX: "propertyValues."

since 0.24.0source

The prefix of the computed field that holds a nested instance’s property value.

const COMPUTABLE_FIELD_VARIABLE_PROPERTIES: Readonly< Record<NamedComputableField, readonly VariableProperty[]> >

since 0.24.0source

The variable binding properties that set the same value as a computed field. A field holds a binding or an expression, never both: saving an expression removes these bindings.

function variablePropertiesOfField(field: string): readonly VariableProperty[]

since 0.24.0source

The variable binding properties that clash with a computed field; empty for propertyValues.<id>.

function isExpressionIdentifier(name: string): boolean

since 0.24.0source

True when name can be written in an expression as a property or an enum option name: letters, digits and _, not starting with a digit, and not a keyword of the language.

function findComponentProperty( component: ComponentDefinition, propertyId: string ): ComponentPropertyDefinition | undefined

since 0.24.0source

The property with this ID, or undefined.

function findComponentPropertyByName( component: ComponentDefinition, name: string ): ComponentPropertyDefinition | undefined

since 0.24.0source

The property with this name, or undefined.

function propertyValueFits( property: ComponentPropertyDefinition, value: ComponentPropertyValue ): boolean

since 0.24.0source

True when value fits the property’s kind: a boolean for a boolean, a finite number for a number, a string for text and colour, an existing option ID for an enum, and a non-empty string for an instance swap.

function effectivePropertyValue( property: ComponentPropertyDefinition, value: ComponentPropertyValue | undefined ): ComponentPropertyValue

since 0.24.0source

The value a property takes: the given value when it fits the property’s kind, else the default.

const LENGTH_VARIABLE_PROPERTIES: readonly VariableProperty[]

since 0.24.0source

The binding properties that hold a length in pixels. A number variable whose scopes are all in this list reads as a length in expressions.

function variableExpressionType(variable: VariableDefinition): ExpressionType

since 0.24.0source

The type a variable reads as in expressions. Colour, string and boolean variables read as color, string and boolean. A number variable reads as a length (pixels) when it has scopes and every scope is a length (LENGTH_VARIABLE_PROPERTIES), and as a plain number otherwise: with no scopes, or any scope such as opacity, font weight or line height.

function variableExpressionName( collectionName: string, variableName: string ): string

since 0.24.0source

The name an expression reads a variable by: "Collection/Name".

function variablesByExpressionName( theme: DocumentTheme ): ReadonlyMap<string, LocatedVariable>

since 0.24.0source

The document’s variables by "Collection/Name". A name that two variables share is left out, so an expression that uses it shows an unknown-variable error instead of reading either one.

function variableExpressionValue( located: LocatedVariable, modes?: VariableModes ): ExpressionValue | undefined

since 0.24.0source

The value of a variable in the mode its collection shows under modes (see resolveVariableValue; without modes, the active mode), typed as variableExpressionType.

function componentPropertyExpressionType( property: ComponentPropertyDefinition, enumName: string = property.name ): ExpressionType

since 0.24.0source

The type a property reads as: boolean, string (text), color, component (instance swap), an enum type named enumName (the property name by default) with the option names, or for a number property number, length, percent or angle by its unit.

function componentPropertyExpressionValue( property: ComponentPropertyDefinition, value?: ComponentPropertyValue ): ExpressionValue

since 0.24.0source

The expression value of a property: value when it fits the property’s kind, else the default. An enum value reads as its option’s name.

function nestedPropertyEnumName( component: ComponentDefinition, property: ComponentPropertyDefinition ): string

since 0.24.0source

The enum type name of a nested component’s property, as an outer component’s expressions see it: "<component name>.<property name>". A property name has no dot, so it never clashes with the outer component’s own enum types, which are named after their properties.

function componentExpressionEnvironment( document: DesignDocument, component: ComponentDefinition | ComponentId, storedType?: ExpressionType ): ExpressionEnvironment

since 0.24.0source

The checking environment of a component’s expressions: its properties by name, the document’s variables by "Collection/Name", and storedType for stored (leave it out for a condition).

function componentValueEnvironment( document: DesignDocument, component: ComponentDefinition | ComponentId, propertyValues: ComponentPropertyValues = {}, stored?: ExpressionValue, modes?: VariableModes ): ExpressionValueEnvironment

since 0.24.0source

The evaluation environment matching componentExpressionEnvironment: each property’s value from propertyValues (an instance’s values; values that do not fit and missing ones read as the default), each variable in the mode its collection shows under modes (the active mode by default), and stored.

type ExpressionField = ComputableField | "condition"

since 0.24.0source

A computed field, or "condition".

const LAYOUT_DIRECTION_TYPE: ExpressionEnumType

since 0.24.0source

The enum type of the direction field.

const LAYOUT_ALIGN_TYPE: ExpressionEnumType

since 0.24.0source

The enum type of the align field.

const LAYOUT_JUSTIFY_TYPE: ExpressionEnumType

since 0.24.0source

The enum type of the justify field; space_between and the others are the model’s "space-between" and so on.

function computableFieldType( document: DesignDocument, node: SceneNode, field: ExpressionField ): ExpressionType | undefined

since 0.24.0source

The type an expression in field must have: boolean for a condition, the field’s type from the table in design 8.3, or for propertyValues.<id> the type of that property of the nested instance’s component (with its enum named by nestedPropertyEnumName). Undefined for a field the node cannot compute or a property the nested component does not have.

function storedFieldValue( document: DesignDocument, node: SceneNode, field: ExpressionField ): ExpressionValue | undefined

since 0.24.0source

The base value of a computed field, which stored reads: the plain value stored on the node, as an expression value. Absent optional values read as zero (for example a missing maxWidth is 0 px, a missing corner radius 0 px). The text fields read the first run’s resolved style; line height, a multiplier in the model, reads as a percent (1.5 is 150%), and as a length in text_style (1.5 at 16 px is 24 px). A height, a multiplier in the model, reads as a percent (1.5 is 150%), and as pixels inside a text_style (1.5 at 14 px is 21 px). A nested instance’s propertyValues.<id> reads the value stored on the node, else the nested component’s default. Undefined for a condition and for fields computableFieldType does not type.

interface ExpressionFieldContext

since 0.24.0source

What checking one computed field or condition needs.

readonly expected: ExpressionTypesource
readonly environment: ExpressionEnvironmentsource

function expressionFieldContext( document: DesignDocument, component: | ComponentDefinition | ComponentId, node: SceneNode, field: ExpressionField ): ExpressionFieldContext | undefined

since 0.24.0source

The expected type and checking environment of one field of a layer in component, or undefined when the field has no type (see computableFieldType).

function expressionFieldValueEnvironment( document: DesignDocument, component: ComponentDefinition | ComponentId, node: SceneNode, field: ExpressionField, propertyValues: ComponentPropertyValues = {}, modes?: VariableModes ): ExpressionValueEnvironment

since 0.24.0source

The evaluation environment of one field of a layer in component for an instance with propertyValues: componentValueEnvironment with stored set to storedFieldValue.

interface ComponentExpressionSite

since 0.24.0source

One stored expression: a computed field or a condition of a layer.

readonly nodeId: NodeIdsource
readonly field: ExpressionFieldsource
readonly source: stringsource

function componentLayerIds( document: DesignDocument, component: ComponentDefinition | ComponentId ): readonly NodeId[]

since 0.24.0source

The IDs of a component’s layers, root first, in tree order.

function componentExpressionSites( document: DesignDocument, component: | ComponentDefinition | ComponentId ): readonly ComponentExpressionSite[]

since 0.24.0source

Every expression and condition stored on a component’s layers, in tree order, conditions after the node’s computed fields.

function componentPropertyUsage( document: DesignDocument, componentId: ComponentId, propertyId: string, optionId?: string ): number

since 0.24.0source

How many of component’s expressions and conditions name the property (or, with optionId, that option of an enum property), plus the propertyValues.<id> expressions of nested instances of it in other components that name the option. The editor shows it before a deletion or a kind change (“3 expressions use size”).

interface ExpressionEditResult

since 0.24.0source

Operations for one edit, and how many expressions it touches.

readonly operations: readonly DocumentOperation[]source

Apply them together, in one applyOperations call.

readonly count: numbersource

For a rename, how many references were rewritten; for a deletion or a kind change, how many expressions use what changed and will stop checking (see componentPropertyUsage).

function renameComponentPropertyOperations( document: DesignDocument, componentId: ComponentId, propertyId: string, name: string ): ExpressionEditResult

since 0.24.0source

Renames a property and rewrites every expression in the component that reads it, in one edit. Throws a ModelError when the name is not an identifier or another property has it.

function renameComponentPropertyOptionOperations( document: DesignDocument, componentId: ComponentId, propertyId: string, optionId: string, name: string ): ExpressionEditResult

since 0.24.0source

Renames an option of an enum property and rewrites every expression that names it: in the component, and in propertyValues.<id> expressions of nested instances of the component inside other components. Option IDs do not change, so instance values need no edit.

function deleteComponentPropertyOperations( document: DesignDocument, componentId: ComponentId, propertyId: string ): ExpressionEditResult

since 0.24.0source

Deletes a property. In the same edit it removes every instance’s value for it (instance nodes and nested instance overrides) and the propertyValues.<id> expressions of nested instances of the component. Expressions that read the property are kept and stop checking, so they fall back to the stored value; count says how many there are.

function deleteComponentPropertyOptionOperations( document: DesignDocument, componentId: ComponentId, propertyId: string, optionId: string ): ExpressionEditResult

since 0.24.0source

Deletes one option of an enum property. Instance values that chose it are removed in the same edit, so those instances take the default; when the option was the default, the first remaining option becomes the default. Expressions that name the option are kept and stop checking; count says how many. Throws a ModelError for the last option.

function changeComponentPropertyOperations( document: DesignDocument, componentId: ComponentId, next: ComponentPropertyDefinition ): ExpressionEditResult

since 0.24.0source

Replaces a property’s definition with one of another kind (or the same kind with other settings), keeping its ID and name; rename with renameComponentPropertyOperations. Instance values that do not fit the new definition are removed in the same edit. Expressions that read the property, and propertyValues.<id> expressions of nested instances of the component, are kept and may stop checking; count is how many there are (0 when the kind is unchanged and so are a number’s unit and an enum’s option names: a new option makes a match non-exhaustive and a removed one leaves references unresolved). Throws a ModelError when a number property’s minimum is above its maximum.

function renameVariableOperations( document: DesignDocument, variableId: VariableId, name: string ): ExpressionEditResult

since 0.24.0source

Renames a variable and rewrites every var("Collection/Name") that reads it, in every component, in one edit.

function renameVariableCollectionOperations( document: DesignDocument, collectionId: VariableCollectionId, name: string ): ExpressionEditResult

since 0.24.0source

Renames a variable collection and rewrites every var("Collection/Name") that reads one of its variables, in every component, in one edit.

const propertySampleIds: { readonly document: DocumentId; readonly page: PageId; readonly pageRoot: NodeId; readonly textStyle: TextStyleId; readonly button: ComponentId; readonly buttonRoot: NodeId; readonly buttonIcon: NodeId; readonly buttonLabel: NodeId; readonly size: ComponentPropertyId; readonly small: "opt-small"; readonly large: "opt-large"; readonly kind: ComponentPropertyId; readonly primary: "opt-primary"; readonly secondary: "opt-secondary"; readonly disabled: ComponentPropertyId; readonly label: ComponentPropertyId; readonly icon: ComponentPropertyId; readonly showIcon: ComponentPropertyId; readonly plus: ComponentId; readonly plusRoot: NodeId; readonly plusShape: NodeId; readonly iconSize: ComponentPropertyId; readonly iconSmall: "icon-small"; readonly iconLarge: "icon-large"; readonly minus: ComponentId; readonly minusRoot: NodeId; readonly buy: NodeId; readonly plain: NodeId; readonly brand: VariableCollectionId; readonly brandMode: VariableModeId; readonly accent: VariableId; readonly radius: VariableId; readonly fade: VariableId; }

since 0.24.0source

The IDs the property sample uses, so tests can refer to them by name.

function propertyButtonSample(): DesignDocument

since 0.24.0source

The Button of design 8.10 with its expressions, nested Icon/Plus instance and text label, two Button instances on the page, and a Brand variable collection with a colour, a length (radius, scoped to corner radius) and a plain number (fade, unscoped).

Code styles

Defaults for new code nodes and the two built-in syntax palettes, adapted from the MIT-licensed Code - OSS themes.

const CODE_FONT_FAMILY: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"

source

const CODE_FONT_SIZE: 32

source

const CODE_LINE_HEIGHT: 1.3

source

const BUILT_IN_CODE_STYLES: Readonly<Record<string, CodeStyle>>

source

const DARK_2026_CODE_STYLE: CodeStyle

source

const DARK_2026_CODE_STYLE_ID: CodeStyleId

source

const LIGHT_2026_CODE_STYLE: CodeStyle

source

const LIGHT_2026_CODE_STYLE_ID: CodeStyleId

source

function getCodeLineNumberStart( lineNumbers: CodeNode["lineNumbers"] ): number | null

source

Returns null when line numbers are hidden, otherwise the first line number.

Text measurement

Resolves the effective style of text and measures text nodes with a 2D canvas context (OffscreenCanvas or a DOM canvas), for layout that happens outside the renderer, such as auto-sized text frames. Measuring throws when neither canvas is available.

function resolveTextStyle( document: DesignDocument, styleId: string | undefined, overrides: TextStyleOverrides | undefined ): TextStyle

source

function resolveTextFontFamily( document: DesignDocument, style: TextStyle ): string

source

Resolves embedded font identities into an asset-specific CSS family stack. The primary embedded face replaces the authored primary family, ordered embedded fallbacks follow it, and the authored CSS fallback tail remains last. Asset aliases keep same-family faces distinct and deterministic.

function documentFontFamilyAlias(assetId: string): string

source

Returns the stable CSS family used for one document font asset identity.

function textStyleToCanvasFont( style: TextStyle, document?: DesignDocument ): string

source

function measureTextNode( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): TextMeasurement

source

interface TextMeasurement

source
readonly width: numbersource
readonly height: numbersource
readonly lineCount: numbersource
readonly lines: readonly TextLineMetrics[]source

interface TextLineMetrics

source
readonly width: numbersource
readonly ascent: numbersource
readonly descent: numbersource
readonly height: numbersource

function getTextNodeBaselines( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): readonly number[]

source

function getAutoSizedTextFrame( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): Rect

source

The frame an auto-sized text box takes for its content: the size the renderer gave a text like it (recordDrawnTextSize), or, until the renderer has drawn one, an estimate measured with the browser canvas. Fixed-size text keeps its frame. The new frame keeps the point from textAutoResizeAnchor in place on the page.

function autoResizedTextFrame( node: Pick<TextualNode, "frame" | "rotation">, paragraphs: readonly TextParagraph[], size: { readonly width: number; readonly height: number } ): Rect

source

The frame of an auto-sized text box after its content changed its size. The box turns about its center, so keeping frame.x and frame.y would move rotated text across the page as it grows; this keeps the anchor from textAutoResizeAnchor fixed in page space instead.

function textAutoResizeAnchor( paragraphs: readonly TextParagraph[] ): { readonly x: number; readonly y: number }

source

The point of a text box that stays in place on the page when its content resizes it, as fractions of the frame’s local width and height. As in Figma, the top edge stays put, and auto-width text keeps its alignment edge: the left edge for left-aligned or justified text, the center for centered text and the right edge for right-aligned text. When paragraphs disagree, the left edge stays put.

function resolveTextListLayout( document: DesignDocument, paragraph: TextParagraph, paragraphIndex: number ): TextListLayout | null

source

interface TextListLayout

source
readonly text: stringsource
readonly style: TextStylesource
readonly gap: numbersource
readonly nestingOffset: numbersource

Additional body and marker inset contributed by the authored list level.

readonly left: numbersource
readonly right: numbersource
readonly firstLine: numbersource
readonly markerWidth: numbersource
readonly markerBoxWidth: numbersource

function resolveListMarkerStyle( document: DesignDocument, paragraph: Pick<TextParagraph, "runs" | "list"> ): TextStyle

source

The style a list marker of the paragraph is drawn in. A marker takes the shared style and the overrides it does not name itself from the first run of its paragraph, each on its own: a marker with a style and no overrides still takes the first run’s overrides. A marker the renderer writes itself (a bullet or a number) takes both. The page compiler, the canvas estimate and textLayoutKey all resolve markers here, so they cannot disagree.

function getTextNodeFontRequests( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): readonly TextFontRequest[]

source

interface TextFontRequest

source
readonly font: stringsource
readonly text: stringsource

function clearTextMeasurementCache(): void

source

Other exports

Exports not yet placed in a section above, in alphabetical order.

function applyComputedFields( document: DesignDocument, node: SceneNode, results: readonly { readonly field: ComputableField; readonly value: ExpressionValue; }[], onError?: (field: ComputableField, message: string) => void ): SceneNode

since 0.25.0source

node with each computed value written into its model field and clamped to the field’s range. Fields the node type cannot compute are ignored. A text layer that sizes itself is measured again when its text changed.

function carryPropertyExpressions( from: ComponentDefinition | undefined, to: ComponentDefinition, expressions: NodeExpressions | undefined ): NodeExpressions | undefined

since 0.26.0source

Expressions carried from one component’s instance to another’s, as property values are: a propertyValues.<id> field is renamed to the new component’s property with the same name and kind, and dropped when there is none or the old component lacks the ID. Other fields stay. Undefined when nothing is left.

function carryPropertyValues( from: | ComponentDefinition | undefined, to: ComponentDefinition, values: | Readonly< Record< string, ComponentPropertyValue > > | undefined, mapComponent: ( id: ComponentId ) => ComponentId = (id) => id ): Record<string, ComponentPropertyValue> | undefined

since 0.26.0source

Property values carried from one component to another, as a swap does (design 8.9): a value is kept when the new component has a property with the same name and kind; an enum value only when the new property has an option with the same name, and then as that option’s ID. Values that do not fit, or name a property the old component does not have, are dropped. An instance swap value goes through mapComponent, for a paste that maps component IDs. Undefined when nothing is left.

function clearDrawnTextSizes(documentId?: DocumentId): void

source

Forgets every drawn text size of one document, or of all documents when none is given.

interface ComponentExpressionError

since 0.25.0source

A stored expression or condition that does not parse or check. It is not evaluated: the field shows its stored value, and a condition counts as no condition (design 8.5).

readonly nodeId: NodeIdsource
readonly field: ExpressionFieldsource
readonly source: stringsource
readonly errors: readonly ExpressionError[]source

function componentExpressionErrors( document: DesignDocument, componentId: ComponentId, options?: ResolveInstanceOptions ): readonly ComponentExpressionError[]

since 0.25.0source

The expressions and conditions on componentId’s layers that do not parse or check, in tree order. Each is left out when the component is resolved: the field shows its stored value, and a condition counts as no condition (design 8.5). After them come the computed values the default property values make invalid, such as a font family with no font in the document, which show their stored value too. A nested component’s errors are listed under its own ID.

interface ComputedComponent

since 0.25.0source

A component’s layer tree for one set of property values, before overrides and layout: the layers a condition leaves out, and the layers with computed fields written into their model fields.

readonly nodes: ReadonlyMap<NodeId, SceneNode>source

Layers with at least one computed field, by stored ID; every other layer is as stored.

readonly leftOut: ReadonlySet<NodeId>source

Layers whose condition is false. Their sublayers are left out with them and are not listed.

readonly errors: readonly ComponentExpressionError[]source

Computed values these property values make invalid; each field shows its stored value.

function computedTextFitOperations( document: DesignDocument, componentIds: readonly ComponentId[] ): readonly DocumentOperation[]

source

The node replacements that bring the stored sizes of componentIds up to date with the sizes their texts are drawn at, with no edit to follow: each component’s self-sizing computed texts, the auto layout frames around them, and every instance of the components (an instance with its own property values or text overrides included) with the frames around it. Empty when every stored size already fits.

The editor calls it when the renderer has measured a text at another size than the estimate the stored sizes were derived from (recordDrawnTextSize).

function confirmDrawnTextSize(document: DesignDocument, node: TextualNode): void

source

Marks the remembered size of a text as right for the font files in use: the renderer has just drawn the text at that size. Does nothing for a text with no remembered size, or with another one.

function documentWithVariantInstance( document: DesignDocument, instance: InstanceNode ): DesignDocument

source

document with instance added to its nodes, so the resolver can read it.

function drawnNodes(document: DesignDocument): readonly SceneNode[]

since 0.26.0source

Every node the renderer draws, for code that scans the document for the fonts, characters or assets its text uses: the stored nodes (page layers and component layers), then the layers of every stored instance as resolveInstance derives them, with the instance’s overrides, property values and nested instances applied and the virtual ID as id. Text or a font that exists only in an override is found here and nowhere in document.nodes. An instance that cannot be resolved (a missing component or a nesting cycle) adds no layers; the compiler reports that itself. The same document object gives the same array, and unchanged instances give the same layer objects, so per-node caches keyed by object stay warm.

function drawnTextSize( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): DrawnTextSize | undefined

source

The size the renderer gave a text like this one in this document, when it has drawn one.

interface DrawnTextSize

source

The size the renderer gave a text it drew.

readonly width: numbersource
readonly height: numbersource

function filledPropertyValues( component: ComponentDefinition, values: ComponentPropertyValues = {} ): readonly ComponentPropertyValue[]

since 0.25.0source

The instance’s values for component’s properties with defaults filled in, in the component’s property order: the key of a computed result.

function forgetDrawnTextSizesOfEarlierFonts(documentId: DocumentId): number

source

Forgets the sizes of a document that were recorded with earlier font files and that no draw has found right since (setDrawnTextFontSet): text that is not drawn now, whose size with the files in use is unknown. Such a text is estimated again and corrected when it is next drawn. Call it once every text on screen is settled. Returns how many sizes were forgotten.

function getDrawnTextSizesVersion(): number

source

Changes whenever a drawn text size is recorded, changed or forgotten, in any document. Anything that keeps sizes derived from getAutoSizedTextFrame (resolved instances, the component view) compares it to know they are out of date.

type GroupVariableBindings = Readonly< & Partial<Record<GroupVariableProperty, VariableId>> & { readonly width?: never; readonly height?: never } >

source

A group’s variable bindings. A group’s size is always computed from its layers, so a group never binds width or height.

type GroupVariableProperty = Exclude<VariableProperty, "width" | "height">

source

The variable properties a group can bind: all but its size.

function hasComputedTextContent(node: SceneNode | undefined): boolean

source

Whether the node is a text layer whose content is computed: bound to a component property, varied by a property, or written as an expression (all three are stored as its characters expression).

function isLayerInDocument(document: DesignDocument, id: NodeId): boolean

since 0.23.0source

True when id names a layer the document shows: a stored node that exists, or a virtual ID whose every path segment is a layer of the component shown by the instance above it (the outer instance, then each nested instance as swapped). A node moved out of a component, or deleted, leaves the virtual IDs through it unresolved.

function isLayoutFieldInUse(node: SceneNode, field: string): boolean

since 0.24.0source

False for a layout field the frame’s current layout kind does not use: gap on a grid or absolute frame, columnGap on a stack, any layout field on an absolute frame. True for every other field. A frame can keep an expression on any layout field (see computableFieldsOf), so it stays valid when the layout kind changes; the resolver applies, and the editor offers, only the fields in use.

const jsonCodecStats: { jsonPathAllocations: number; canonicalJsonCalls: number; }

source

const modelApplyStats: { documentDiffs: number; operationDeserializes: number; positionMapContainersVisited: number; }

source

function multiplyTransforms( left: TransformMatrix, right: TransformMatrix ): TransformMatrix

source

Composes two affine transforms. The result applies right first, then left. Matrices use the [a, b, c, d, e, f] layout, where x' = a*x + c*y + e and y' = b*x + d*y + f. In 3×3 form this is the matrix product M=LRM = L R, so Mp=L(Rp)M\mathbf{p} = L(R\mathbf{p}).

function nodeIdsRenamedWithSuffix(nodeIds: readonly string[]): string[]

source

The stored node IDs in nodeIds whose 0.23.0 rename needs a collision suffix, because their plain “_” form is already taken. Operation migration cannot see those suffixes, so a stored history whose root document has one is ambiguous.

function nodeLocalTransform(node: SceneNode): TransformMatrix

source

The transform from a layer’s own coordinates (the top-left of its frame at the origin) to its parent’s: its flips, then its rotation, both about the middle of its frame, then its position. A path carries its transform itself. A layer that is not rotated or flipped gives exactly its position, with no rounding.

function nodePageTransform( document: DesignDocument, id: NodeId, parents: ReadonlyMap< NodeId, NodeId > = buildParentMap(document.nodes), transforms = new Map<NodeId, TransformMatrix>() ): TransformMatrix

source

The transform from a layer’s own coordinates to canonical page coordinates: every ancestor’s position, rotation and flips, then the layer’s own. The structural root (a page’s root frame, or the root of a component) is the coordinate origin, so its own transform is left out. Pass parents (from buildParentMap) and a transforms cache when calling this for many layers of one document.

const OVERRIDE_FIELD_PROPERTIES: Readonly< Partial<Record<OverrideField, readonly VariableProperty[]>> >

since 0.23.0source

The variable properties each override field can bind (decision A). A property missing here (size, corner radius, gaps, padding) binds a field that is not overridable, so a binding for it is unsupported on a virtual ID.

function overrideFieldForProperty(property: string): OverrideField | undefined

since 0.23.0source

The override field a variable property belongs to, or undefined when that property binds a field an instance cannot override.

function recordDrawnTextSize( document: DesignDocument, node: TextualNode, size: DrawnTextSize ): boolean

source

Remembers the size the renderer gave an auto-sized text it drew, so every text of the same document with the same words, styles, font files and sizing takes that size from now on (getAutoSizedTextFrame). The renderer is the authority for text sizes: it lays text out with the fonts it draws with, which the browser canvas often cannot measure (a Google Fonts family, an automatic fallback font).

node is the text as it was drawn; its frame’s width is part of the key when only its height follows the text. Returns whether the size is new or differs from the one remembered, in which case drawnTextSizesVersion changes and sizes derived before it are out of date.

function resetJsonCodecStats(): void

source

function resetModelApplyStats(): void

source

function resetTextMeasurementStats(): void

source

function resolvedShownComponents( document: DesignDocument, instanceId: NodeId ): (path: readonly NodeId[]) => ComponentId | undefined

since 0.26.0source

A shownComponentAt for the stored instance instanceId: the component each nested instance shows once the instance resolves, so a component an instance swap property selects counts. The instance resolves on the first lookup. A path the resolved tree leaves out (a layer hidden by its condition), or an instance that does not resolve, gives undefined, and the stored component counts.

function setDrawnTextFontSet(documentId: DocumentId, name: string): boolean

source

Tells the memory which font files the renderer draws a document with. name stands for the whole set: every file of every font asset that has arrived (a Google Fonts family arrives in parts) and the fallback fonts the renderer picks itself, none of which the document names. The same name twice changes nothing, so every surface that shows the document can say it.

With another name, the sizes recorded so far are from other files and may be wrong. They are kept for now, so text on screen does not jump to the canvas estimate and back: each is either found right by the next draw (confirmDrawnTextSize) or replaced (recordDrawnTextSize), and the ones no draw vouched for are dropped by forgetDrawnTextSizesOfEarlierFonts. Returns whether the name changed.

function splitCssFontFamilies(fontFamily: string): readonly string[]

source

function textLayoutKey( document: DesignDocument, node: Pick< TextualNode, "autoResize" | "wrap" | "frame" | "paragraphs" >, paragraphs: readonly TextParagraph[] = node.paragraphs ): string

source

Names everything in the document that decides the size of an auto-sized text: its words, the styles they resolve to in this document (so a theme or variable mode that changes a style changes the key), the font file each of their font assets stands for, the paragraph settings, how the box sizes itself, and its width when only its height follows the text. A list marker is named by the style the renderer draws it in (resolveListMarkerStyle), which can come from the first run of its paragraph. Two texts of one document with the same key are laid out alike by the renderer (the order in which the fields of a style or a paragraph were written is not part of the key), as long as it draws with the same font files; the files that are not in the document (which parts of a Google Fonts family have arrived, the fallback fonts the renderer picks itself) are told apart by setDrawnTextFontSet. The drawn sizes are kept by this key (recordDrawnTextSize), and anything else that remembers or counts something about one layout of a text uses it too, so a change of font, size, wrap width or sizing mode is never taken for the layout before it.

const textMeasurementStats: { layoutComputations: number; fontRequestTextRebuilds: number; resolvedStyleCopies: number; }

source

const VARIANT_VIEW_CAP: 64

source

The most views “Show all variants” draws; above it the user picks which properties to vary.

interface VariantCombination

source

One “Show all variants” view: its property values and label.

readonly values: ComponentPropertyValuessource

The values that differ from the defaults; the defaults view has none.

readonly label: stringsource

The values that differ from the defaults (“size: large, disabled”), or “Component” for the defaults.

readonly key: stringsource

Stable text for the combination (“” for the defaults), for editor-only IDs.

function variantCombinationOf( component: ComponentDefinition, values: ComponentPropertyValues ): VariantCombination

since 0.27.0source

The combination that values name: the enum and boolean values that differ from the defaults (a value no option has counts as the default), with the label and key variantCombinations gives the same view. Values of other kinds of property are left out; a view keeps their defaults.

function variantCombinations( component: ComponentDefinition, vary?: readonly ComponentPropertyId[] ): readonly VariantCombination[]

source

The combinations of the varied properties’ values (every variant property when vary is omitted), the first property changing slowest and each property’s default first, so the first combination is the defaults. Empty when there would be more than VARIANT_VIEW_CAP views.

function variantInstanceNode( document: DesignDocument, componentId: ComponentId, id: NodeId, name: string, values: ComponentPropertyValues ): InstanceNode | undefined

source

An editor-only instance of componentId with values, at the page origin with the component root’s size for those values (a computed fixed width or height included): what a variant view draws.

function variantLabel( component: ComponentDefinition, values: ComponentPropertyValues ): string

source

The label of a view with these values: each value that differs from the default, in the component’s order. An enum reads “size: large”, a boolean that is on reads “disabled” and one that is off reads “showIcon: off”. “Component” when every value is the default.

function variantProperties( component: ComponentDefinition ): readonly VariantProperty[]

source

The component’s enum and boolean properties, in the component’s order.

type VariantProperty = Extract< ComponentPropertyDefinition, { readonly kind: "enum" | "boolean" } >

source

A property “Show all variants” can vary: an enum or a boolean.

function variantPropertyValues( property: VariantProperty ): readonly ComponentPropertyValue[]

source

The values a variant property takes across the views, the default first: a boolean’s default then its opposite, an enum’s default option then the other options in their order.

function variantViewCount( component: ComponentDefinition, vary?: readonly ComponentPropertyId[] ): number

source

How many views varying these properties (every variant property when omitted) gives.

function variantViewSize( document: DesignDocument, componentId: ComponentId, values: ComponentPropertyValues, options?: ResolveInstanceOptions ): { readonly width: number; readonly height: number } | undefined

source

The natural size of the component with values: the resolved root of an instance with those values (hugging axes take their content’s size).

function withoutClashingBindings<T extends SceneNode>(node: T): T

since 0.26.0source

The node without the variable bindings that clash with its expressions: a field holds a binding or an expression, never both, and the expression wins. The same node when nothing clashes.

@taco-slides/editor TypeScript

308 of 474 exports documented · outline

@taco-slides/editor is the part of interactive editing that does not depend on the browser or the renderer. It depends on the document model (@taco-slides/model) but not on React, the DOM, Playwright, Skia or the native renderer. Most of it is pure functions: they take the document, the current editor state and one input event, and return the next state plus a description of what changed. The caller owns document operations and history.

Raw pointer input and screen geometry use CSS pixels relative to the interactive surface. ViewportState.documentToScreen maps document units into that space. Device pixels come only from devicePixelRatio and are never accepted as pointer input.

The package also defines the boundary between the editor and a renderer: EditorDriver, which shared test scenarios drive, and InteractiveRenderSession, which answers hit tests and geometry queries. After a render session has loaded, these queries are synchronous, so pointer down, move and up all see one retained scene generation.

TypeScript stays the source of truth for the document, commands and history. A renderer receives a validated document, a selected page and the exact transitions that follow. It never keeps a second history or changes the document on its own.

Input events

Every input the editor accepts is an EditorInput: pointer, keyboard, IME composition, text and clipboard events, already normalized from the platform. assertEditorInput checks one at the boundary.

type EditorInput = | EditorPointerInput | EditorKeyboardInput | EditorCompositionInput | EditorTextInput | EditorClipboardInput

source

Every input event the editor accepts. Check one with assertEditorInput.

interface EditorPointerInput

source

A pointer event normalized from the platform. Shared by mouse, pen and touch.

readonly kind: "pointer"source
readonly phase: "down" | "move" | "up" | "cancel" | "lost-capture"source

cancel and lost-capture end the pointer sequence without an up. updateBodyInteraction treats both as a cancel and commits nothing.

readonly pointerId: numbersource

Identifies one pointer across its events. A non-negative integer.

readonly pointerType: EditorPointerTypesource
readonly position: Pointsource

Position in CSS pixels relative to the interactive surface.

readonly button: numbersource

The button that changed in this event, numbered as in the DOM button field (0 is the primary button).

readonly buttons: numbersource

Bit mask of the buttons held after this event, as in the DOM buttons field.

readonly pressure: numbersource

Pressure from 0 to 1.

readonly modifiers: EditorModifierssource

type EditorPointerType = "mouse" | "pen" | "touch"

source

The device that produced a pointer event, as in the DOM pointerType.

interface EditorModifiers

source

Modifier keys held during a pointer or keyboard event. meta is the Command key on macOS.

readonly alt: booleansource
readonly control: booleansource
readonly meta: booleansource
readonly shift: booleansource

interface EditorKeyboardInput

source

A key press or release.

readonly kind: "keyboard"source
readonly phase: "down" | "up"source
readonly key: stringsource

The produced key value, as in the DOM KeyboardEvent.key (for example "a" or "ArrowLeft").

readonly code: stringsource

The physical key, as in the DOM KeyboardEvent.code (for example "KeyA").

readonly repeat: booleansource

True when the key is held down and the platform repeats it.

readonly composing: booleansource

True while an IME composition is in progress.

readonly modifiers: EditorModifierssource

interface EditorCompositionInput

source

An IME composition event. text is the whole composition text so far, not a difference from the last update.

readonly kind: "composition"source
readonly phase: "start" | "update" | "end" | "cancel"source
readonly text: stringsource
readonly selectionStart: numbersource

UTF-16 offsets used by browser composition APIs.

readonly selectionEnd: numbersource

interface EditorTextInput

source

Text typed or deleted, modeled on the DOM beforeinput event.

readonly kind: "text"source
readonly inputType: stringsource

The DOM input type, such as "insertText" or "deleteContentBackward".

readonly data: string | nullsource

The inserted text, or null for input types that carry none, such as deletions.

interface EditorClipboardInput

source

A copy, cut or paste. For paste, items holds the clipboard contents.

readonly kind: "clipboard"source
readonly action: "copy" | "cut" | "paste"source
readonly items: readonly EditorClipboardItem[]source

interface EditorClipboardItem

source

One clipboard format and its bytes. An item list holds each MIME type at most once.

readonly mimeType: stringsource
readonly data: Uint8Arraysource

function assertEditorInput(input: unknown): asserts input is EditorInput

source

Validates input at the TypeScript-to-renderer boundary. Checks the shape of each EditorInput kind: known phases and actions, finite pointer positions, pressure from 0 to 1, non-negative integer pointer ids, ordered composition selections, and clipboard items with unique MIME types and byte data. A rich-text clipboard item is decoded to check it. Throws TypeError or RangeError on the first problem.

Rich text travels through the clipboard as UTF-8 JSON under its own MIME type, next to a text/plain fallback.

const EDITOR_RICH_TEXT_MIME_TYPE: "application/x-taco-slides-rich-text+json"

source

The clipboard MIME type for EditorRichTextClipboardPayload, stored as UTF-8 JSON.

interface EditorRichTextClipboardPayload

source

Canonical rich-text clipboard payload; text/plain remains the required fallback.

readonly version: 1source
readonly paragraphs: readonly TextParagraph[]source

function encodeEditorRichTextClipboardPayload( payload: EditorRichTextClipboardPayload ): Uint8Array

source

Encodes a rich-text clipboard payload as UTF-8 JSON bytes. Validates it first and throws TypeError the same way as decodeEditorRichTextClipboardPayload.

function decodeEditorRichTextClipboardPayload( bytes: Uint8Array ): EditorRichTextClipboardPayload

source

Decodes the bytes of an EDITOR_RICH_TEXT_MIME_TYPE clipboard item. Throws TypeError when the bytes are not UTF-8 JSON, when the object has properties other than version and paragraphs, when version is not 1, or when the paragraphs are not valid text node paragraphs.

interface Point

source

A 2D point. The coordinate space depends on where it is used.

readonly x: numbersource
readonly y: numbersource

interface Size

source

A width and height. The units depend on where it is used.

readonly width: numbersource
readonly height: numbersource

Viewport and zoom

A ViewportState places the document on the interactive surface. These functions convert between document units, CSS pixels and device pixels. The zoom helpers turn a requested zoom mode into the ratio the canvas draws at.

interface ViewportState

source

How the document is placed on the interactive surface. Build one with createViewportState; convert points with documentToScreenPoint and the other viewport functions.

readonly screenSize: Sizesource

Size of the interactive surface in CSS pixels.

readonly documentToScreen: TransformMatrixsource

Affine transform from document units to CSS pixels relative to the surface. It must be finite and invertible.

readonly devicePixelRatio: numbersource

Device pixels per CSS pixel. Must be positive.

interface ViewportInput

source

Pan and zoom settings for createViewportState.

readonly screenSize: { readonly width: number; readonly height: number }source

Size of the interactive surface in CSS pixels.

readonly origin: Pointsource

Where the document origin lands on the surface, in CSS pixels.

readonly zoom: numbersource

CSS pixels per document unit. Must be positive.

readonly devicePixelRatio: numbersource

Device pixels per CSS pixel. Must be positive.

function createViewportState(input: ViewportInput): ViewportState

source

Builds a ViewportState with no rotation: document point p maps to origin + p * zoom in CSS pixels, so documentToScreen is [z,0,0,z,ox,oy][z, 0, 0, z, o_x, o_y] and a screen point maps back with p=(q−o)/z\mathbf{p} = (\mathbf{q} - \mathbf{o}) / z. Throws TypeError for non-finite values and RangeError for a negative size or a zoom or device pixel ratio that is not positive.

function assertViewportState(viewport: ViewportState): void

source

Checks that a ViewportState is usable: finite, non-negative screen size, a finite and invertible documentToScreen transform, and a positive device pixel ratio. Throws TypeError or RangeError.

const documentToScreenPoint: (viewport: ViewportState, point: Point) => Point

source

Converts a point from document units to CSS pixels on the surface.

const screenToDocumentPoint: (viewport: ViewportState, point: Point) => Point

source

Converts a point from CSS pixels on the surface to document units. Throws RangeError when the viewport transform cannot be inverted.

const documentToScreenRect: (viewport: ViewportState, rect: Rect) => Rect

source

Converts a document rectangle to the CSS-pixel bounding box of its transformed corners (see transformRect).

const screenToDocumentRect: (viewport: ViewportState, rect: Rect) => Rect

source

Converts a CSS-pixel rectangle to the document-unit bounding box of its transformed corners (see transformRect).

const screenToDevicePoint: (viewport: ViewportState, point: Point) => Point

source

Converts CSS pixels to device pixels by multiplying by the device pixel ratio.

const deviceToScreenPoint: (viewport: ViewportState, point: Point) => Point

source

Converts device pixels to CSS pixels by dividing by the device pixel ratio.

const documentToDevicePoint: (viewport: ViewportState, point: Point) => Point

source

Converts a point from document units to device pixels.

const deviceToDocumentPoint: (viewport: ViewportState, point: Point) => Point

source

Converts a point from device pixels to document units.

function transformPoint(transform: TransformMatrix, point: Point): Point

source

Applies an affine transform to a point.

function transformRect(transform: TransformMatrix, rect: Rect): Rect

source

Transforms the four corners of a rectangle and returns their axis-aligned bounding box. Under rotation the result is larger than the rectangle.

function multiplyTransforms( left: TransformMatrix, right: TransformMatrix ): TransformMatrix

source

Composes two affine transforms. The result applies right first, then left. Matrices use the [a, b, c, d, e, f] layout, where x' = a*x + c*y + e and y' = b*x + d*y + f. In 3×3 form this is the matrix product M=LRM = L R, so Mp=L(Rp)M\mathbf{p} = L(R\mathbf{p}).

The arithmetic is the model’s (multiplyTransforms there), which the layer transforms shared by the page compiler and the editor are built on.

function invertTransform(transform: TransformMatrix): TransformMatrix

source

Inverts an affine transform. Throws RangeError when the determinant is not finite or is too close to zero. With Δ=ad−bc\Delta = ad - bc:

[acebdf001]−1=1Δ[d−ccf−de−babe−af00Δ]\begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{bmatrix}^{-1} = \frac{1}{\Delta} \begin{bmatrix} d & -c & cf - de \\ -b & a & be - af \\ 0 & 0 & \Delta \end{bmatrix}

type ZoomMode = | { readonly kind: "fit" } | { readonly kind: "ratio"; readonly ratio: number }

source

The zoom the user asked for: fit the content in the stage, or a fixed ratio.

interface ZoomMetrics

source

The resolved zoom. Build it with resolveZoomMetrics.

readonly actualRatio: numbersource

The ratio the canvas draws at: CSS pixels per document unit.

readonly minimumRatio: numbersource

The lowest allowed ratio. Currently always MIN_ZOOM_RATIO.

readonly fitRatio: numbersource

The ratio that fits the content in the stage.

function resolveZoomMetrics(mode: ZoomMode, fitRatio: number): ZoomMetrics

source

The zoom the canvas draws for a zoom mode. The canvas viewport and the zoom indicator both read this one value, so the indicator cannot show a ratio the canvas is not using. fitRatio is the ratio that fits the page content in the stage.

const initialZoomMetrics: ZoomMetrics

source

Starting zoom metrics: 100%, with a fit ratio of 1.

function clampZoomRatio(ratio: number, minimumRatio = MIN_ZOOM_RATIO): number

source

Clamps a ratio to minimumRatio and MAX_ZOOM_RATIO. A non-finite ratio becomes 1, or minimumRatio if that is larger.

function stepZoomRatio(ratio: number, direction: 1 | -1): number

source

The next zoom step: times 1.25 when zooming in (direction 1) or times 0.8 when zooming out, clamped and rounded to two decimal places:

z′=round⁡(100⋅clamp⁡(z⋅k, 0.02, 64))100,k={1.25zoom in0.8zoom outz' = \frac{\operatorname{round}\bigl(100 \cdot \operatorname{clamp}(z \cdot k,\ 0.02,\ 64)\bigr)}{100}, \qquad k = \begin{cases} 1.25 & \text{zoom in} \\ 0.8 & \text{zoom out} \end{cases}

function formatZoomPercent(ratio: number): string

source

Formats a ratio as a whole percentage, such as "125%".

function sameZoomMetrics(left: ZoomMetrics, right: ZoomMetrics): boolean

source

True when all three fields of two ZoomMetrics are equal.

const MIN_ZOOM_RATIO: 0.02

source

The canvas is unbounded, so zoom has fixed limits instead of page-relative ones.

const MAX_ZOOM_RATIO: 64

source

The largest zoom ratio, 6400%.

Editor driver and snapshots

An EditorDriver wraps one backend (SVG, Wasm or native) behind one interface. Tests send it input and read back an EditorSnapshot holding the document, selection, active gesture, previews, history and rendered frame. A driver reports what it cannot do through its capabilities, and rejects that input with UnsupportedEditorCapabilityError.

interface EditorDriver

source
getCapabilities(): Promise<EditorDriverCapabilities>source
reset( config: { readonly document: DesignDocument; readonly viewport: ViewportState; } ): Promise<void>source
setDocument(document: DesignDocument): Promise<void>source
setViewport(viewport: ViewportState): Promise<void>source
setTool(tool: EditorTool): Promise<void>source
sendInput(input: EditorInput): Promise<void>source
flush(): Promise<EditorSnapshot>source
getSnapshot(): Promise<EditorSnapshot>source
hitTest(screenPoint: Point): Promise<readonly EditorHit[]>source
getNodeGeometry(nodeId: NodeId): Promise<NodeGeometry | null>source
getTextGeometry(nodeId: NodeId): Promise<TextGeometry | null>source
hitTestText(query: TextHitQuery): Promise<TextHitResult | null>source
navigateText(query: TextNavigationQuery): Promise<TextNavigationResult | null>source
getVectorGeometry(nodeId: NodeId): Promise<VectorGeometry | null>source
hitTestVector(query: VectorHitQuery): Promise<VectorHitTestResult | null>source
getAccessibilitySnapshot(): Promise<AccessibilitySnapshotV1 | null>source
performAccessibilityAction( request: AccessibilityActionRequestV1 ): Promise<boolean>source
captureFrame(): Promise<RenderedEditorFrame>source
dispose(): Promise<void>source

interface EditorDriverCapabilities

source
readonly pointerTypes: readonly EditorPointerType[]source
readonly keyboardRepeat: booleansource
readonly composition: booleansource
readonly clipboard: readonly EditorClipboardInput["action"][]source
readonly textEditing: booleansource

Exposes selection and composition state while text editing is active.

readonly textGeometry: booleansource
readonly textHitTesting: booleansource
readonly textNavigation: booleansource
readonly vectorGeometry: booleansource
readonly vectorHitTesting: booleansource
readonly vectorBooleanOperations: booleansource
readonly accessibility: booleansource
readonly frameCapture: booleansource
readonly variableDevicePixelRatio: booleansource

Whether reset can recreate the surface at a different device-pixel ratio.

type EditorDriverCapability = | `pointer:${EditorPointerType}` | "keyboard-repeat" | "composition" | `clipboard:${EditorClipboardInput["action"]}` | "text-editing" | "text-geometry" | "text-hit-testing" | "text-navigation" | "vector-geometry" | "vector-hit-testing" | "vector-boolean-operations" | "accessibility" | "frame-capture" | "variable-device-pixel-ratio"

source

class UnsupportedEditorCapabilityError extends Error

source

A stable failure for input or observations a driver explicitly does not provide.

readonly code: "unsupported-editor-capability"source
new (readonly capability: EditorDriverCapability, message: string)source

interface EditorSnapshot

source
readonly document: DesignDocumentsource
readonly activePageId: PageId | nullsource
readonly selection: EditorSelectionStatesource
readonly hoveredNodeId: NodeId | nullsource
readonly textEditing: ActiveTextEditingState | nullsource

Canonical text selection plus any uncommitted IME replacement.

readonly editingTextNodeId: NodeId | nullsource

Compatibility identity; it must agree with textEditing.nodeId when active.

readonly vectorEditing: VectorEditingSnapshot | nullsource

Existing-path component editing and its uncommitted native overlay preview.

readonly editingCodeNodeId: NodeId | nullsource
readonly activeTool: EditorToolsource
readonly viewport: ViewportStatesource
readonly gesture: EditorGestureState | nullsource
readonly previews: readonly EditorPreviewGeometry[]source
readonly lastHit: EditorHit | nullsource
readonly history: EditorHistoryStatesource
readonly accessibility: AccessibilitySnapshotV1 | nullsource

Exact versioned semantic state exposed by the active editor surface.

readonly revisions: EditorRevisionssource

interface EditorSelectionState

source
readonly pageIds: readonly PageId[]source
readonly nodeIds: readonly NodeId[]source
readonly primaryNodeId: NodeId | nullsource

interface EditorHistoryState

source
readonly canUndo: booleansource
readonly canRedo: booleansource
readonly headCommitId: string | nullsource
readonly commits: readonly CommitRecord[]source

interface EditorRevisions

source
readonly document: numbersource
readonly surface: numbersource
readonly frame: numbersource
readonly accessibility: numbersource

type EditorTool = | "move" | "hand" | "scale" | "frame" | "rectangle" | "line" | "arrow" | "connector" | "ellipse" | "polygon" | "star" | "image" | "pen" | "pencil" | "text" | "code" | "text-path" | "comment"

source

type EditorGestureKind = | "draw" | "marquee" | "move" | "resize" | "connector-endpoint" | "rotate" | "scale" | "pan" | "text" | "vector"

source

type EditorGestureState = | ( & EditorGestureBase & { readonly kind: "draw" | "marquee" | "text" | "vector" }) | ( & EditorGestureBase & { readonly kind: "move"; readonly duplicate: boolean; readonly previewDelta: Point; }) | ( & EditorGestureBase & { readonly kind: "resize"; readonly handle: EditorResizeHandle }) | ( & EditorGestureBase & { readonly kind: "connector-endpoint"; readonly endpoint: "start" | "end"; }) | ( & EditorGestureBase & { readonly kind: "rotate"; readonly center: Point; readonly previewAngle: number; }) | ( & EditorGestureBase & { readonly kind: "scale"; readonly handle: EditorScaleHandle; readonly anchor: Point; readonly previewFactor: number; }) | (EditorGestureBase & { readonly kind: "pan" })

source

type EditorResizeHandle = | "north-west" | "north" | "north-east" | "east" | "south-east" | "south" | "south-west" | "west"

source

type EditorScaleHandle = | "north-west" | "north-east" | "south-east" | "south-west"

source

type EditorPreviewGeometry = | { readonly kind: "rect"; readonly nodeId?: NodeId; readonly rect: Rect } | { readonly kind: "line"; readonly nodeId?: NodeId; readonly start: Point; readonly end: Point; } | { readonly kind: "path"; readonly nodeId?: NodeId; readonly points: readonly Point[]; readonly closed: boolean; } | { readonly kind: "alignment-guide"; readonly orientation: "horizontal" | "vertical"; readonly start: Point; readonly end: Point; readonly movingAnchor: "start" | "center" | "end" | "baseline"; }

source

type EditorHit = | { readonly nodeId: NodeId; readonly part: "body"; readonly frameTitle?: true; } | { readonly nodeId: NodeId; readonly part: "resize-handle"; readonly handle: EditorResizeHandle; } | { readonly nodeId: NodeId; readonly part: "rotate-handle"; readonly corner: EditorScaleHandle; } | { readonly nodeId: NodeId; readonly part: "scale-handle"; readonly handle: EditorScaleHandle; } | { readonly nodeId: NodeId; readonly part: "connector-endpoint"; readonly endpoint: "start" | "end"; } | { readonly nodeId: NodeId; readonly part: "vector-anchor"; readonly vertexId: VectorVertexId; } | { readonly nodeId: NodeId; readonly part: "vector-handle"; readonly handleId: VectorHandleId; } | { readonly nodeId: NodeId; readonly part: "vector-segment"; readonly segmentId: VectorSegmentId; } | { readonly nodeId: NodeId; readonly part: "vector-region"; readonly regionId: VectorRegionId; }

source

interface RenderedEditorFrame

source
readonly mimeType: "image/png"source
readonly width: numbersource
readonly height: numbersource
readonly density: numbersource
readonly data: Uint8Arraysource

Render session boundary

InteractiveRenderSession is what a renderer implements for the editor. It loads a document, applies each document transition, answers hit tests and geometry queries, and draws frames with the editor overlay on top.

interface InteractiveRenderSession

source
loadDocument(load: RenderDocumentLoad): Promise<void>source

Replaces the canonical document and seeds exact document-store revision continuity.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.apply: taco_render_session_apply

applyDocumentUpdate(update: DocumentSceneUpdate): Promise<void>source

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.apply: taco_render_session_apply

setActivePage(pageId: PageId | null): Promise<void>source

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.apply: taco_render_session_apply

setViewport(viewport: ViewportState): Promise<void>source
hitTest(screenPoint: Point): readonly EditorHit[]source

Native, through InteractiveRetainedBackend.hitTest: taco_render_session_hit_test

getNodeGeometry(nodeId: NodeId): NodeGeometry | nullsource

Native, through InteractiveRetainedBackend.getNodeGeometry: taco_render_session_get_node_geometry

getSceneGeometrySnapshot(): SceneGeometrySnapshotsource

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.getGeometrySnapshot: taco_render_session_get_geometry_snapshot

getTextGeometry(nodeId: NodeId): TextGeometry | nullsource

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

hitTestText(query: TextHitQuery): TextHitResult | nullsource

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

navigateText(query: TextNavigationQuery): TextNavigationResult | nullsource

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

getVectorGeometry(nodeId: NodeId): VectorGeometry | nullsource

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

hitTestVector(query: VectorHitQuery): VectorHitTestResult | nullsource

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

booleanVectorPaths(request: VectorBooleanPathRequest): VectorBooleanPathResultsource

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

setOverlays(overlays: EditorOverlayState): Promise<void>source

Native, through InteractiveRetainedBackend.setOverlays: taco_render_session_set_text_editing_overlay, taco_render_session_clear_text_editing_overlay, taco_render_session_set_vector_editing_overlay, taco_render_session_clear_vector_editing_overlay, taco_render_session_set_selection_chrome, taco_render_session_set_canvas_overlay

renderFrame(): Promise<RenderFrameReport>source

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

getAccessibilitySnapshot(): Promise<AccessibilityNodeSnapshot | null>source
dispose(): Promise<void>source

interface RenderDocumentLoad

source

One atomic canonical document load and its exact resolved resource input.

readonly document: DesignDocumentsource
readonly rendererDocument?: DesignDocumentsource

Renderer-only projection paired atomically with the canonical document.

readonly rendererNodeOwners?: RendererNodeOwnerssource

Exact renderer-node ownership paired atomically with the renderer projection.

readonly rendererQueryTargets?: RendererQueryTargetssource

Canonical public query IDs paired atomically with renderer-only query targets.

readonly commitId: CommitIdsource
readonly resourceSnapshot: RenderResourceSnapshotsource

interface DocumentSceneUpdate

source
readonly reason: TransitionReasonsource
readonly previousCommitId: CommitIdsource
readonly currentCommitId: CommitIdsource
readonly previousDocument: DesignDocumentsource
readonly document: DesignDocumentsource
readonly previousRendererDocument?: DesignDocumentsource

Exact renderer-only projection committed with previousDocument.

The coordinator continues to own previousDocument as canonical editor authority. A projection may replace unsupported canonical nodes with deterministic retained nodes, but must preserve every other document surface. When omitted, the canonical document is also the renderer input.

readonly previousRendererNodeOwners?: RendererNodeOwnerssource

Exact renderer-node ownership map committed with previousRendererDocument.

Every renderer node must appear exactly once and resolve directly to a self-owned canonical node. The coordinator uses this only to translate renderer query identities back to canonical editor authority.

readonly previousRendererQueryTargets?: RendererQueryTargetssource

Exact canonical-to-renderer query targets committed with the previous projection.

readonly rendererDocument?: DesignDocumentsource

Renderer-only projection paired atomically with document.

readonly rendererNodeOwners?: RendererNodeOwnerssource

Exact renderer-node ownership map paired atomically with rendererDocument.

readonly rendererQueryTargets?: RendererQueryTargetssource

Canonical public query IDs mapped to their renderer-only text/vector nodes.

readonly operations: readonly DocumentOperation[]source
readonly changes: ChangeSetsource
readonly rendererChanges?: ChangeSetsource

Exact change set between the previous and current renderer projections. Required whenever either side uses a renderer document or ownership map.

readonly resourceSnapshot: RenderResourceSnapshotsource

Complete renderer resources for document, resolved from the same immutable revision.

interface RenderResource

source
readonly assetId: stringsource
readonly mimeType: stringsource
readonly bytes: Uint8Arraysource
readonly slice?: RenderFontSlicesource

Set when this resource is one file of a font asset whose source is “google-fonts”. Such an asset is split into many files by Unicode coverage, so it appears once per loaded file, each with a distinct slice.id, and may appear zero times while nothing is loaded yet.

interface RenderFontSlice

source

One file of a font asset split by Unicode coverage.

readonly id: stringsource

Unique within the asset, made of letters, digits, “.”, “_” and “-“.

readonly unicodeRanges: readonly (readonly [number, number])[]source

Sorted, non-overlapping inclusive code point ranges the file covers.

interface RenderResourceSnapshot

source

Complete, immutable renderer resource input for one canonical document revision.

readonly resources: readonly RenderResource[]source

type RendererNodeOwners = Readonly<Record<string, NodeId>>

source

Immutable, exhaustive renderer-node to canonical-node ownership.

interface RendererQueryTargets

source

Optional renderer-only targets for canonical query identities.

Text targets are complete retained text materializations of canonical code nodes. Vector targets preserve the canonical path topology. Every target must be page-reachable and share its owner’s exact node-local coordinate space so results can retain the canonical public namespace without a lossy coordinate conversion.

readonly text: Readonly<Record<string, NodeId>>source
readonly vector: Readonly<Record<string, NodeId>>source

interface RenderFrameReport

source
readonly changedRegions: readonly Rect[]source

Repainted regions in CSS pixels relative to the render target.

readonly sceneUpdateMs: numbersource
readonly layoutMs: numbersource
readonly paintMs: numbersource
readonly submitMs: numbersource
readonly paint?: RenderFramePaintStatisticssource

Present when the backend reports paint statistics.

interface RenderFramePaintStatistics

source

What one frame painted, for performance instrumentation.

readonly fullPaint: booleansource

The whole target repainted instead of only the damaged region.

readonly nodesPainted: numbersource
readonly nodesConsidered: numbersource

Nodes examined for this frame, including ones culled before painting.

readonly targetWidth: numbersource

Render target size in device pixels.

readonly targetHeight: numbersource
readonly retainedPayloadBytes: numbersource

Bytes of retained scene payload held by the renderer.

readonly decodedFontEntries: numbersource
readonly textLayoutEntries: numbersource

The overlay is what the editor draws over the document: selection, hover, gesture previews, text carets and selections, vector editing handles, and canvas lines such as snap guides and measurements.

interface EditorOverlayState

source
readonly selectedNodeIds: readonly NodeId[]source
readonly hoveredNodeId: NodeId | nullsource
readonly gesture: EditorGestureState | nullsource
readonly previews: readonly EditorPreviewGeometry[]source
readonly decorations: readonly EditorNodeDecoration[]source
readonly nodePreviews: readonly EditorNodePreview[]source
readonly textEditing: | (ActiveTextEditingState & { readonly caretVisible: boolean }) | nullsource

Selection, caret, and IME state painted by the shared renderer.

readonly vectorEditing: VectorEditingSnapshot | nullsource

Existing-path component selection and full node-local preview network.

readonly hitRegions: readonly EditorOverlayHitRegion[]source

Topmost-first hit regions. Overlay hits always precede retained scene-body hits.

readonly selectionChrome?: EditorSelectionChrome | nullsource

Selection outlines and handles for a backend that paints them itself, in the same frame as the scene. Omitted when the host draws its own chrome.

readonly canvasOverlay?: EditorCanvasOverlay | nullsource

Snap lines, ruler guides, option-drag measurements and the pixel grid, painted by the backend in the same frame as the scene. Omitted leaves the backend’s current overlay in place; null clears it.

interface EditorCanvasOverlay

source
readonly anchorNodeId: NodeId | nullsource

The node whose local coordinates the overlay uses; null for scene coordinates.

readonly devicePixelRatio: numbersource
readonly grid: | { readonly originX: number; readonly originY: number; readonly step: number; readonly rgba: number; } | nullsource

A line after every step units from the origin, across the canvas.

readonly lines: readonly EditorOverlayLine[]source
readonly marks: readonly EditorOverlayMark[]source
readonly titles?: readonly EditorOverlayTitle[]source

Names drawn above top-level frames; absent means none.

readonly rulers?: EditorOverlayRulerssource

The rulers along the top and left edges; absent means none.

readonly boxes?: readonly EditorOverlayBox[]source

Filled, hatched or outlined boxes (auto layout gaps and padding); absent means none.

readonly handles?: readonly EditorOverlayHandle[]source

Ticks with a border (auto layout spacing handles); absent means none.

readonly badges?: readonly EditorOverlayBadge[]source

Value pills (the value of a spacing drag, at most 16); absent means none.

interface EditorOverlayLine

source
readonly orientation: "vertical" | "horizontal"source
readonly side: EditorOverlayLineSidesource
readonly position: numbersource

x of a vertical line, y of a horizontal one.

readonly start: number | nullsource

The line’s extent along its length; null crosses the whole canvas.

readonly end: number | nullsource
readonly rgba: numbersource

0xRRGGBBAA.

readonly dash?: { readonly dashCssPx: number; readonly gapCssPx: number }source
readonly scene?: booleansource

Scene coordinates instead of coordinates local to the overlay’s anchor.

readonly anchorNodeId?: NodeIdsource

A node whose local coordinates this line uses instead of the overlay’s anchor, such as a frame whose own guides must follow it through a move or resize preview.

readonly spansAnchorBox?: booleansource

Run along the anchor node’s own box (0 to its width or height); start and end are ignored.

type EditorOverlayLineSide = "after" | "before" | "centered"

source

Which device pixels a one-CSS-pixel overlay line covers around its position: “after” for a left or top edge and for coordinate lines, “before” for a right or bottom edge, “centered” for a center line.

interface EditorOverlayMark

source

An anti-aliased x centered on the pixels of two crossing lines.

readonly x: numbersource
readonly y: numbersource
readonly xSide: EditorOverlayLineSidesource
readonly ySide: EditorOverlayLineSidesource
readonly halfSizeCssPx: numbersource
readonly rgba: numbersource
readonly scene?: booleansource

interface EditorOverlayHitRegion

source
readonly hit: EditorHitsource
readonly bounds: Rectsource

The renderer also draws the rest of the canvas chrome from the same overlay, so the page and its chrome never drift apart: boxes (such as the auto layout padding and gap areas), handles (such as the spacing handles), badges with a short text, the rulers along the top and left edges, and the names above top-level frames.

interface EditorOverlayBox

source

A rectangle in its anchor’s local coordinates (the overlay’s anchor when omitted), mapped through the anchor’s whole transform, so a rotated frame’s boxes turn with it. Each look is optional: a fill, diagonal hatching, and a one-CSS-pixel outline inside the box.

readonly anchorNodeId?: NodeIdsource
readonly x: numbersource
readonly y: numbersource
readonly width: numbersource
readonly height: numbersource
readonly fillRgba?: numbersource

0xRRGGBBAA.

readonly hatchRgba?: numbersource
readonly outlineRgba?: numbersource

interface EditorOverlayHandle

source

A round-ended tick with a border, centred on a point, along the anchor’s local x or y axis.

readonly anchorNodeId?: NodeIdsource
readonly x: numbersource
readonly y: numbersource
readonly axis: "x" | "y"source
readonly lengthCssPx: numbersource
readonly thicknessCssPx: numbersource
readonly rgba: numbersource

0xRRGGBBAA.

readonly borderRgba: numbersource

interface EditorOverlayBadge

source

A rounded pill with one line of text, its top-left corner a CSS pixel offset from a point.

readonly anchorNodeId?: NodeIdsource
readonly x: numbersource
readonly y: numbersource
readonly offsetXCssPx: numbersource
readonly offsetYCssPx: numbersource
readonly fontSizeCssPx: numbersource
readonly text: stringsource
readonly backgroundRgba: numbersource

0xRRGGBBAA.

readonly textRgba: numbersource

interface EditorOverlayRulers

source

The rulers, in the overlay anchor’s units (page coordinates). The renderer lays out ticks and numbers from the page as it is in the frame being drawn.

readonly sizeCssPx: numbersource
readonly fontSizeCssPx: numbersource
readonly minimumMajorCssPx: numbersource
readonly backgroundRgba: numbersource

0xRRGGBBAA colours.

readonly borderRgba: numbersource
readonly inkRgba: numbersource
readonly selectionFillRgba: numbersource
readonly selectionInkRgba: numbersource
readonly selectionX: readonly { readonly start: number; readonly end: number }[]source

The selection’s extents: sorted, apart; a zero-length one marks one value.

readonly selectionY: readonly { readonly start: number; readonly end: number }[]source
readonly labels: readonly { readonly axis: "x" | "y"; readonly position: number; readonly backgroundRgba: number; readonly textRgba: number; }[]source

Guide positions drawn on their ruler as tags (at most 16).

interface EditorOverlayTitle

source

The name drawn above a top-level frame, on the top-left corner of the frame’s screen box as the frame is in the frame being drawn, cut to the frame’s width with an ellipsis.

readonly anchorNodeId: NodeIdsource
readonly text: stringsource
readonly rgba: numbersource

0xRRGGBBAA.

readonly fontSizeCssPx: numbersource
readonly gapCssPx: numbersource

Space between the title’s line box and the frame’s top edge.

interface EditorSelectionChrome

source
readonly items: readonly EditorSelectionChromeItem[]source
readonly strokeRgba: numbersource

Default colours of items that give none, 0xRRGGBBAA.

readonly handleFillRgba: numbersource
readonly devicePixelRatio: numbersource

interface EditorSelectionChromeItem

source

One outline or handle, resolved against its node when the frame is painted.

readonly nodeId: NodeId | nullsource

Null places the rectangle in scene coordinates instead of node-local ones.

readonly kind: "outline" | "square-handle" | "round-handle"source
readonly hover?: booleansource
readonly dashed?: booleansource
readonly x: numbersource

An outline’s rectangle. A handle’s centre is x and y, and its width and height are its finished size on screen in CSS pixels.

readonly y: numbersource
readonly width?: numbersource
readonly height?: numbersource
readonly angle?: numbersource

A handle’s turn on screen in radians, clockwise; 0 when omitted.

readonly strokeRgba?: numbersource

0xRRGGBBAA overrides of the chrome’s default colours.

readonly fillRgba?: numbersource
readonly offsetX?: numbersource

Scene-space offset for a preview the retained scene does not hold.

readonly offsetY?: numbersource
readonly sceneTransform?: TransformMatrixsource

Local-to-scene matrix of a scene-anchored item; identity when omitted.

readonly nodeTransform?: TransformMatrixsource

Item-to-node matrix of a node-anchored item; identity when omitted.

interface EditorNodeDecoration

source
readonly nodeId: NodeIdsource
readonly role: "hover" | "selection"source
readonly primary: booleansource
readonly handles: "none" | "resize" | "rotate" | "scale" | "vector"source

type EditorNodePreview = | { readonly kind: "subtree-transform"; readonly nodeId: NodeId; readonly transform: TransformMatrix; readonly mode: "replace" | "duplicate"; } | { readonly kind: "node-override"; readonly nodeId: NodeId; readonly documentBounds: Rect; }

source

Scene and text geometry

A render session publishes the geometry of the scene it drew as a SceneGeometrySnapshot tagged with a generation number. Text geometry, text hit testing and caret navigation work in node-local coordinates.

interface SceneGeometrySnapshot

source
readonly generation: bigintsource

Retained-scene generation; it is intentionally not an editor history ID.

readonly sceneBounds: Rectsource
readonly painterOrder: readonly NodeId[]source

Back-to-front canonical paint order.

readonly nodes: readonly NodeGeometry[]source
readonly byId: ReadonlyMap<NodeId, NodeGeometry>source

interface NodeGeometry

source
readonly nodeId: NodeIdsource
readonly localBounds: Rectsource
readonly documentBounds: Rectsource
readonly visibleDocumentBounds: Rect | nullsource

Bounds after effective visibility and ancestor clips, or null when fully hidden.

readonly screenBounds: Rectsource
readonly visibleScreenBounds: Rect | nullsource

Screen-space form of visibleDocumentBounds.

readonly localToDocument: TransformMatrixsource
readonly localToScreen: TransformMatrixsource
readonly painterOrder: numbersource
readonly effectivelyVisible: booleansource
readonly effectiveOpacity: numbersource

function indexSceneGeometryNodes( nodes: readonly NodeGeometry[] ): ReadonlyMap<NodeId, NodeGeometry>

source

function withSceneGeometryIndex( snapshot: Omit<SceneGeometrySnapshot, "byId"> ): SceneGeometrySnapshot

source

interface TextGeometry

source
readonly nodeId: NodeIdsource
readonly generation: bigintsource

Retained-scene generation used to reject geometry after a scene update.

readonly coordinateSpace: "node-local"source
readonly carets: readonly TextCaretGeometry[]source
readonly selection: TextRangeGeometry | nullsource
readonly composition: TextRangeGeometry | nullsource

interface TextCaretGeometry

source
readonly position: TextPositionsource
readonly localBounds: Rectsource

Caret bounds in the edited node’s local coordinate space.

interface TextRangeGeometry

source
readonly range: TextRangesource
readonly localRects: readonly Rect[]source

One rectangle per shaped fragment, all in node-local coordinates.

interface TextHitQuery

source
readonly nodeId: NodeIdsource
readonly localPoint: Pointsource

Query position in the text node’s local coordinate space.

interface TextHitResult

source
readonly nodeId: NodeIdsource
readonly generation: bigintsource
readonly position: TextPositionsource
readonly inside: booleansource

False when the result is the nearest caret outside laid-out text.

function assertTextHitQuery(value: unknown): asserts value is TextHitQuery

source

Checks that a value is a TextHitQuery with a node id and a finite local point.

interface TextNavigationQuery

source
readonly nodeId: NodeIdsource
readonly from: TextPositionsource
readonly operation: TextNavigationOperationsource
readonly preferredLocalX?: numbersource

Retained across vertical moves; node-local x coordinate.

type TextNavigationOperation = | "grapheme-backward" | "grapheme-forward" | "word-backward" | "word-forward" | "visual-left" | "visual-right" | "line-up" | "line-down" | "line-start" | "line-end" | "paragraph-start" | "paragraph-end" | "document-start" | "document-end"

source

interface TextNavigationResult

source
readonly nodeId: NodeIdsource
readonly generation: bigintsource
readonly position: TextPositionsource
readonly preferredLocalX?: numbersource

Updated node-local x coordinate for a following vertical move.

function assertTextNavigationQuery( value: unknown ): asserts value is TextNavigationQuery

source

Checks that a value is a TextNavigationQuery with a known operation and, when given, a finite preferred local x.

Selection

These functions decide what a click, double click, marquee or keyboard command selects. A plain click selects the outermost node the current scope allows; groups and nested frames act as one object until a double click enters them. Hidden and locked nodes, and nodes inside them, are never selected.

type SelectionMode = "replace" | "toggle" | "add"

source

How updateNodeSelection combines new node ids with the current selection.

function updateNodeSelection( current: readonly NodeId[], incoming: readonly NodeId[], mode: SelectionMode ): readonly NodeId[]

source

Combines a selection with incoming node ids. replace returns the incoming ids without duplicates, add appends the ones not yet selected, and toggle removes each incoming id that is selected and appends each one that is not. Order is kept, so the last id is the primary selection.

interface CanvasSelection

source

The node a press selects, and the node the renderer actually hit to get there.

readonly hitNodeId: NodeIdsource
readonly targetId: NodeIdsource
readonly frameTitle?: truesource

The press is on the frame’s title, which never starts a marquee.

function frontmostCanvasSelection( document: DesignDocument, rootId: NodeId, hits: readonly EditorHit[], primarySelectedNodeId: NodeId | null, deepSelect = false ): CanvasSelection | null

source

Walks hits in order and returns the first one that canvasSelectionTarget maps to a node, with the node it selects. Pass deepSelect for Cmd or Ctrl clicks, which select the innermost node.

function frontmostCanvasSelectionTarget( document: DesignDocument, rootId: NodeId, hits: readonly EditorHit[], primarySelectedNodeId: NodeId | null, deepSelect = false ): NodeId | null

source

Like frontmostCanvasSelection, but returns only the selected node id.

function canvasSelectionTarget( document: DesignDocument, rootId: NodeId, hitNodeId: NodeId, primarySelectedNodeId: NodeId | null, deepSelect = false ): NodeId | null

source

The node one plain click selects: the outermost ancestor of the hit node that the current scope has not already been entered past. Deep selection ignores the scope and takes the innermost node under the pointer instead.

function descendantSelectionTarget( document: DesignDocument, rootId: NodeId, hitNodeId: NodeId, primarySelectedNodeId: NodeId | null ): NodeId | null

source

The node a double click selects: exactly one level further in than now.

function selectableBodyHits( document: DesignDocument, rootId: NodeId, hits: readonly EditorHit[] ): readonly Extract<EditorHit, { readonly part: "body" }>[]

source

Keeps only body hits on nodes that can be selected: the node and every ancestor below rootId are visible and unlocked. Order is kept.

function frameMarqueeTarget( document: DesignDocument, rootId: NodeId, selection: CanvasSelection, selectedNodeIds: readonly NodeId[], deepSelect = false ): NodeId | null

source

The frame a drag should draw a selection marquee inside, instead of moving the frame. The rule: a frame must be selected before a drag moves it. So a press on the empty background of a frame that is not selected, and whose children a click can reach, starts a marquee that selects those children. A click without dragging still selects the frame.

Frames a click walks straight into count: frames drawn on the page, the frame the current selection is inside, and any frame under deep select (Cmd or Ctrl). A frame nested in another one answers a click as a single object, like a group, so dragging it moves it, as in Figma. A press on a child inside a frame selects and moves that child, as before.

function marqueeSelectionNodeIds( document: DesignDocument, rootId: NodeId, geometry: SceneGeometrySnapshot, marquee: Rect, nested: boolean, scopeId: NodeId = rootId, selectionScopeId: NodeId | null = null ): readonly NodeId[]

source

Returns intersecting selectable nodes in retained back-to-front painter order. marquee is in document units and is tested against each node’s visible document bounds from geometry. scopeId is the container the marquee selects inside: the page root by default, or the frame a marquee started on. A plain marquee selects that container’s direct children; a nested one selects the deepest nodes in it. selectionScopeId is the one layer the canvas selects within, if any.

function selectAllSelectionTargets( document: DesignDocument, rootId: NodeId, primarySelectedNodeId: NodeId | null ): readonly NodeId[]

source

The layers Select All (Cmd+A) selects, as in Figma: every layer at the same level as the primary selected layer, meaning the children of its parent frame or group, or every top-level layer on the page when nothing is selected. Hidden and locked layers are skipped, as they are for canvas clicks and marquee selection. A selection that is not on this page counts as none.

function topLevelSelectableNodeIds( document: DesignDocument, rootId: NodeId ): readonly NodeId[]

source

The direct children of the page root that can be selected, in child order. Hidden and locked nodes are left out.

function childSelectionTarget( document: DesignDocument, rootId: NodeId, nodeId: NodeId ): NodeId | null

source

The child of a container that Enter selects, or null: the first child in listed order (listedChildIds, front layer first) that can be selected, which is the first row the layers panel shows under the container. An instance, or a layer inside one, opens to its resolved layers. A node can be selected when it and every ancestor below rootId are visible and unlocked.

function parentSelectionTarget( document: DesignDocument, rootId: NodeId, nodeId: NodeId ): NodeId | null

source

The parent of a node, or null when the parent is the page root rootId.

function siblingSelectionTarget( document: DesignDocument, rootId: NodeId, nodeId: NodeId, direction: 1 | -1 ): NodeId | null

source

The sibling that Tab (direction 1) or Shift+Tab (-1) selects, following the layers as listed (listedChildIds, front layer first): 1 is the next row down the list (the layer behind) and -1 the row above (the layer in front), wrapping around at the ends. Null when there is no other selectable sibling.

function nodeSelectionPath( document: DesignDocument, rootId: NodeId, nodeId: NodeId ): readonly NodeId[]

source

The ids from just below rootId down to nodeId, outermost first. Empty when nodeId is the root or is not inside it. A component’s layers are on no page; isolation mode draws them in a component view beside the page (components design 5.5), so a path that ends at a component root counts as a path from the page root, with the component root as an artboard on it.

Body interaction

updateBodyInteraction turns pointer events on node bodies into selection changes, marquee selection and moves. It reads hit tests and scene geometry from the render session but never changes the document: a finished move comes back as a BodyMoveCompletion for the caller to commit.

function createBodyInteractionState( options: { readonly selectedNodeIds?: readonly NodeId[]; readonly hoveredNodeId?: NodeId | null; } = {} ): BodyInteractionState

source

Creates a BodyInteractionState with no gesture. The last selected id becomes the primary selection.

function updateBodyInteraction( environment: BodyInteractionEnvironment, state: BodyInteractionState, input: EditorPointerInput ): BodyInteractionUpdate

source

Reduces body-level pointer input without DOM access or document mutation. A completed move is descriptive; the caller owns operations and history.

interface BodyInteractionEnvironment

source

The document, page and renderer state that updateBodyInteraction reads.

readonly document: DesignDocumentsource
readonly rootId: NodeIdsource

The root node of the active page.

readonly viewport: ViewportStatesource

Must describe the same screen geometry as renderSession’s current snapshot.

readonly renderSession: BodyRenderQueriessource
readonly snapThresholdScreenPx?: numbersource

Object-alignment distance in CSS pixels. Defaults to 5.

readonly guideLines?: readonly BodyGuideLine[]source

Ruler guides a move may snap to, in the same document space as viewport. An “x” guide is a vertical line at that x; a “y” guide a horizontal one.

readonly moveRefusal?: (targetId: NodeId) => string | nullsource

Why a press on a node starts no move, or null to move it. Without it, a layer inside an instance (a virtual ID) keeps its position; isolation mode passes its own rule (components design 5.5).

readonly autoLayoutDropAllowed?: (frameId: NodeId) => booleansource

Whether a move may drop into this auto-layout frame. Defaults to every frame; isolation mode allows only the open component’s frames (design 5.5).

readonly marqueeSelectable?: (nodeId: NodeId) => booleansource

Whether a marquee may preview and select this node. Defaults to every node; isolation mode rejects the page layers outside its level (design 5.5).

readonly selectionScopeId?: NodeIdsource

The one layer this canvas selects within, when it does not select the whole page: a press, the hover and a marquee answer inside it (scopedSelectionTarget). The isolation split’s page pane passes the opened instance (components design 5.5).

readonly minimumMarqueeScreenPx?: numbersource

Both marquee dimensions must reach this CSS-pixel size. Defaults to 1.

readonly autoLayoutDropMarginScreenPx?: numbersource

Extra CSS-pixel distance around an auto-layout frame that accepts a drop. Defaults to 12.

type BodyRenderQueries = Pick< InteractiveRenderSession, "hitTest" | "getSceneGeometrySnapshot" >

source

The two render session queries the body interaction needs: hit testing and the retained scene geometry.

interface BodyInteractionState

source

State for updateBodyInteraction. Create it with createBodyInteractionState.

readonly selection: BodySelectionStatesource
readonly hoveredNodeId: NodeId | nullsource

The node a click would select under the pointer, or null.

readonly gesture: BodyInteractionGesture | nullsource

The gesture in progress, or null between gestures.

interface BodySelectionState

source

The selected nodes as the body interaction sees them.

readonly nodeIds: readonly NodeId[]source

Selected node ids without duplicates, in selection order.

readonly primaryNodeId: NodeId | nullsource

The last id in nodeIds, or null when nothing is selected.

interface BodyInteractionUpdate

source

The result of updateBodyInteraction.

readonly state: BodyInteractionStatesource
readonly pointerCapture: "none" | "capture" | "release"source

What the caller should do with pointer capture: capture when a gesture starts, release when it ends or is cancelled.

readonly completedMove: BodyMoveCompletion | nullsource

Set only on the pointer up that ends a move with a non-zero delta.

readonly refusedMove?: stringsource

Why a press that would have started a move did not: a layer inside an instance keeps the position its component gives it (components design 4.2). The caller shows the reason, so nothing fails silently.

interface BodyMoveCompletion

source

A finished move, returned once on pointer up. It describes the move; the caller turns it into document operations and history.

readonly kind: "move"source
readonly nodeIds: readonly NodeId[]source
readonly documentDelta: Pointsource

Canonical document-space delta; command/history ownership remains with the caller.

readonly duplicate: booleansource
readonly autoLayoutDrop: | Pick<BodyAutoLayoutDropTarget, "frameId" | "insertionIndex"> | nullsource

Present when the move was released over an auto-layout slot. For a plain move the index counts the frame’s children without the moved nodes; for a duplicate it counts all of them, because the originals stay.

readonly sourceGeneration: bigintsource

Generation of the scene geometry snapshot the move started from.

type BodyInteractionGesture = BodyMarqueeGesture | BodyMoveGesture

source

The active body gesture: a marquee or a move.

interface BodyMarqueeGesture extends BodyGestureBase

source

A drag that draws a selection rectangle. Starts on empty canvas, or on the empty background of a frame that frameMarqueeTarget picks.

readonly kind: "marquee"source
readonly geometry: SceneGeometrySnapshotsource

The retained snapshot is fixed for the lifetime of this marquee.

readonly additive: booleansource

Shift was held at the press: the marquee adds to the selection it started with.

readonly nested: booleansource

Cmd or Ctrl was held at the press: the marquee selects the deepest nodes instead of direct children (see marqueeSelectionNodeIds).

readonly frameId: NodeId | nullsource

The unselected frame this marquee started on, or null on empty canvas. The marquee selects inside this frame, and a click that never becomes a drag selects the frame itself.

readonly dragging: booleansource

False while a press on a frame is still within click distance.

readonly rect: Rectsource

The marquee rectangle in document units.

readonly previewNodeIds: readonly NodeId[]source

Nodes inside the marquee right now, before they combine with the starting selection.

readonly previewSelection: BodySelectionStatesource

The selection to show while dragging. It becomes the real selection on pointer up.

interface BodyMoveGesture extends BodyGestureBase

source

A drag that moves the selected nodes. Only the outermost selected nodes move; a selected node inside another selected node moves with it.

readonly kind: "move"source
readonly geometry: SceneGeometrySnapshotsource

The retained snapshot is fixed for hit-independent move policies such as drop targets.

readonly nodeIds: readonly NodeId[]source
readonly rawCurrentAtScreen: Pointsource

The pointer position in CSS pixels before snapping. currentAtScreen holds the snapped position.

readonly previewDelta: Pointsource

How far the nodes have moved so far, in document units, after snapping.

readonly duplicate: booleansource

Alt (Option) is held: releasing places copies and leaves the originals.

readonly moved: booleansource

True once the pointer has moved from the press point.

readonly dragged: booleansource

True once the pointer has traveled DRAG_SCREEN_PX or more from the press point.

readonly clickNarrowsToNodeId: NodeId | nullsource

The layer pressed inside a multiple selection, or null. If the press ends without a drag, the selection narrows to this layer, as in Figma.

readonly alignmentGuides: readonly Extract< EditorPreviewGeometry, { readonly kind: "alignment-guide" } >[]source

Snap guides to draw, in document units.

readonly initialScreenBounds: AlignmentRectsource

Combined bounds of the moving nodes at the press, in CSS pixels. Snapping works on these bounds.

readonly alignmentTargets: readonly AlignmentTarget[]source

Bounds of other nodes, and ruler guides, that the move can snap to, in CSS pixels.

readonly duplicateSourceTargets: readonly AlignmentTarget[]source

The moving objects themselves as snap targets. They count only while the move duplicates (⌥ held): the originals then stay in place, so the copy can snap to them, as in Figma.

readonly autoLayoutDrop: BodyAutoLayoutDropTarget | nullsource

The auto-layout frame and slot under the pointer, or null. Snapping is off while this is set.

interface BodyGuideLine

source

A ruler guide that a move can snap to.

readonly axis: "x" | "y"source
readonly position: numbersource

The guide’s x or y in document units.

readonly frameId?: NodeIdsource

The frame that owns the guide; omitted for a page guide. As in Figma, a frame’s guides snap only objects inside that frame. They never snap the frame itself, which carries them along as it moves.

interface BodyAutoLayoutDropTarget

source

An auto-layout frame under a moving selection, with its insertion slots.

readonly frameId: NodeIdsource
readonly insertionIndex: numbersource

The child index of the slot nearest the pointer.

readonly activeSlot: numbersource

The position in slots of the slot nearest the pointer. Use it, not insertionIndex, to highlight one indicator, because two slots of a wrapping layout can share an index.

readonly slots: readonly BodyAutoLayoutInsertionSlot[]source

interface BodyAutoLayoutInsertionSlot

source

One place a dragged node could be inserted into an auto-layout frame.

In a wrapping row or column each line has its own slots, so the end of one line and the start of the next are two slots with the same index: both put the node at the same place in the child order.

readonly index: numbersource

The child index the node would take.

readonly rect: Rectsource

Renderer-neutral document-space rectangle for the insertion indicator.

These read what to draw while a gesture is active.

function displayedBodySelection(state: BodyInteractionState): BodySelectionState

source

Selection rendered during a marquee; canonical selection changes only on pointer up.

function bodyInteractionGesture( state: BodyInteractionState ): EditorGestureState | null

source

The active gesture as a renderer-neutral EditorGestureState, with points in document units, or null.

function bodyInteractionPreviews( state: BodyInteractionState ): readonly EditorPreviewGeometry[]

source

Overlay shapes for the active gesture, in document units: the marquee rectangle once it is dragging, the auto-layout insertion slots, or the snap guides of a move.

function bodyMoveNodePreviews( state: BodyInteractionState ): readonly BodyMoveNodePreview[]

source

One BodyMoveNodePreview per moving node during a move; empty otherwise.

interface BodyMoveNodePreview

source

How to draw one node during a move: shifted by documentDelta (document units), either in place of the original or as a copy beside it.

readonly nodeId: NodeIdsource
readonly documentDelta: Pointsource
readonly mode: "replace" | "duplicate"source

function bodyAutoLayoutDropPreview( state: BodyInteractionState ): BodyAutoLayoutDropTarget | null

source

The auto-layout drop target of the active move, or null.

function clearBodyInteractionHover( state: BodyInteractionState ): BodyInteractionState

source

Clears the hovered node. Does nothing during a gesture.

When the view pans during a gesture, as it does when a drag reaches the canvas edge, the page moves under the pointer. These move what the gesture measured by the same offset, so it keeps going instead of jumping.

function translateBodyInteractionGesture( state: BodyInteractionState, documentDelta: Point, viewport: ViewportState ): BodyInteractionState

source

Moves everything the active gesture measured in document and screen space by documentDelta, so a gesture keeps going when the page moves under it, as it does when the view pans while the pointer is held. A move then keeps its selection under the pointer and its snapping targets on the layers they belong to. Offsets such as a move’s preview delta do not change. The viewport’s scale must be the one the gesture started with; a zoom changes the viewport and ends the gesture instead.

function holdBodyMoveAtPointerThroughPan( state: BodyInteractionState, documentDelta: Point, viewport: ViewportState ): BodyInteractionState

source

Like translateBodyInteractionGesture, for a pan that happens while the pointer rests: the page moves by documentDelta and the pointer stays where it is on screen. An active move keeps its layers under the pointer, so its preview delta shrinks by documentDelta, which is what the next pointer sample at the same place would measure before snapping. The next sample measures the move again and snaps it. Other gestures are translated as translateBodyInteractionGesture does.

function translateSceneGeometry( snapshot: SceneGeometrySnapshot, documentDelta: Point, viewport: ViewportState ): SceneGeometrySnapshot

source

translateNodeGeometry for every node of a snapshot, and its scene bounds.

function translateNodeGeometry( geometry: NodeGeometry, documentDelta: Point, viewport: ViewportState ): NodeGeometry

source

Moves one node’s geometry by documentDelta in document space, and by the matching screen offset on screen. Used when the page moves under a gesture, as it does when the view pans, so geometry measured before the pan still describes the node where it is now drawn.

function translateRect(rect: Rect, delta: Point): Rect

source

Moves a rectangle by an offset in its own space.

function documentDeltaToScreenDelta( viewport: ViewportState, documentDelta: Point ): Point

source

The screen offset that matches a document offset under viewport: the viewport’s scale and rotation apply, its translation does not.

Drawing, resizing, scaling and rotating

Geometry for the other pointer gestures: drawing a shape, dragging a resize handle, rotating and scaling. The functions work in whatever space their points are given in.

function rectangleFromPoints(start: Point, current: Point): Rect

source

The rectangle between two corner points, with a non-negative width and height whichever way the drag went. Throws TypeError for non-finite points.

function combineRects(rects: readonly Rect[]): Rect | null

source

The smallest rectangle that holds every rectangle in the list, or null for an empty list.

function boundsForTransformedRect(rect: Rect, transform: TransformMatrix): Rect

source

The axis-aligned bounds of a rectangle after a transform. Same as transformRect.

function constrainLineEndpoint( start: Point, current: Point, constrained: boolean ): Point

source

When constrained (Shift held), snaps the end of a line drawn from start to the nearest multiple of 45 degrees, keeping its length. Otherwise returns current unchanged.

function constrainBoxEndpoint( start: Point, current: Point, constrained: boolean ): Point

source

When constrained (Shift held), moves the dragged corner so the box drawn from start is a square, using the larger side. Otherwise returns current unchanged.

interface ResizeHandleDirection

source

Which way a resize handle pulls: -1 for west or north, 1 for east or south, 0 for an axis the handle does not change.

readonly x: -1 | 0 | 1source
readonly y: -1 | 0 | 1source

function directionForResizeHandle( handle: EditorResizeHandle ): ResizeHandleDirection

source

The ResizeHandleDirection for a resize handle name.

function rotationFromPointerPoints( options: { readonly initialRotation: number; readonly center: Point; readonly start: Point; readonly current: Point; readonly snapIncrement?: number; } ): number

source

The rotation in degrees during a rotate drag: initialRotation plus the angle the pointer has turned around center since start, wrapped with normalizeRotationDegrees. With snapIncrement, the result is rounded to a multiple of it (for example 15 while Shift is held). All points must be in the same space. Throws RangeError for a snap increment that is not positive. With atan2⁡\operatorname{atan2} taking the offset of each point from center:

r=wrap⁡ ⁣(r0+180π(atan2⁡(Δycurrent,Δxcurrent)−atan2⁡(Δystart,Δxstart)))r = \operatorname{wrap}\!\left(r_0 + \frac{180}{\pi} \bigl(\operatorname{atan2}(\Delta y_\text{current}, \Delta x_\text{current}) - \operatorname{atan2}(\Delta y_\text{start}, \Delta x_\text{start})\bigr)\right)

and, when snapping, r←wrap⁡(s⋅round⁡(r/s))r \leftarrow \operatorname{wrap}(s \cdot \operatorname{round}(r / s)) with ss = snapIncrement.

function rotatePointAround(point: Point, center: Point, degrees: number): Point

source

Rotates a point around a center by degrees. In a y-down space such as the canvas, positive angles turn clockwise. Throws TypeError for non-finite input. With θ=degrees⋅π/180\theta = \text{degrees} \cdot \pi / 180 and center (cx,cy)(c_x, c_y):

x′=cx+(x−cx)cos⁡θ−(y−cy)sin⁡θy′=cy+(x−cx)sin⁡θ+(y−cy)cos⁡θ\begin{aligned} x' &= c_x + (x - c_x)\cos\theta - (y - c_y)\sin\theta \\ y' &= c_y + (x - c_x)\sin\theta + (y - c_y)\cos\theta \end{aligned}

function normalizeRotationDegrees(degrees: number): number

source

Wraps an angle into the range from -180 (included) to 180 (excluded) degrees. Throws TypeError for a non-finite angle.

Resizing a group scales the positions and sizes of what it holds. The scale tool also scales stroke widths, corner radii, effects and font sizes.

function resizeGroup( document: DesignDocument, groupId: NodeId, frame: Rect ): readonly SceneNode[]

source

Resizes a group to frame by scaling every descendant, like a plain resize of a group in Figma. Positions, sizes and vector geometry scale; stroke widths, corner radii, effects and font sizes do not (that is what the scale tool is for). The caller chooses frame so the anchored edge stays fixed.

Returns the group followed by every changed descendant, or an empty list when the group cannot be resized.

function uniformScaleFactorFromPointer( options: { readonly anchor: Point; readonly start: Point; readonly current: Point; readonly minimumFactor: number; } ): number

source

The uniform scale factor during a scale drag. It projects the pointer’s offset from anchor onto the start offset, so 1 means no change and 2 means twice as far from the anchor. Never below minimumFactor. Returns 1 when start is on the anchor. Throws RangeError when minimumFactor is not positive. With s\mathbf{s} = start − anchor and p\mathbf{p} = current − anchor:

k=max⁡ ⁣(kmin,p⋅s∥s∥2)k = \max\!\left(k_\text{min}, \frac{\mathbf{p} \cdot \mathbf{s}}{\lVert \mathbf{s} \rVert^2}\right)

type ScaleCorner = "north-west" | "north-east" | "south-east" | "south-west"

source

type ScaleOrigin = ScaleCorner | "center"

source

The point a scale keeps still: the corner opposite the dragged handle, or the layer’s center when Option/Alt is held.

function scaleAnchor(corner: ScaleOrigin, frame: Pick<Rect, "width" | "height">)

source

function scaleSubtree( document: DesignDocument, rootId: NodeId, factor: number, corner: ScaleOrigin ): readonly SceneNode[]

source

A multi-selection moves as one box, as in Figma. selectionBoxBounds is the box around several sibling layers; resizeNodesInBox, rotateNodesAbout and scaleNodesAbout resize, rotate and scale them together. A connector is not turned or stretched: its ends move (connectorWithMovedEnds) and the page compiler places an attached end on its layer again.

function selectionBoxBounds(nodes: readonly SceneNode[]): Rect | null

source

The bounding box of several sibling layers in their shared parent’s coordinates: the smallest upright box around each layer’s rotated bounds, as Figma draws one box around a multi-selection.

function resizeNodesInBox( document: DesignDocument, nodeIds: readonly NodeId[], from: Rect, to: Rect, boxAngle = 0 ): readonly SceneNode[]

source

Resizes several sibling layers together, like resizing a multi-selection’s bounding box in Figma: the box from becomes to (both in the layers’ shared parent coordinates), and every layer’s position and size scale with it by the same rules as a group resize (resizeGroup). A rotated layer keeps its rotation and takes the size whose bounds are its old bounds scaled with the box, so the layers fill to (scaleLayerInParent describes when that needs a skew and what happens then); text that grew with its content gets a fixed size; a frame’s box scales and its children follow by their constraints (constrainChildren), as when the frame itself is resized. Stroke widths, radii, effects and font sizes do not change.

boxAngle is the box’s turn in degrees (clockwise, screen y down) when it is not upright, as after rotating the selection: from and to are then in the box’s own coordinates, the parent’s turned by boxAngle about its origin, and the layers scale along the box’s sides.

Returns every changed node, each selected layer followed by its changed descendants, or an empty list when a layer is missing or locked.

function rotateNodesAbout( nodes: readonly SceneNode[], degrees: number, center: Point ): readonly SceneNode[]

source

Turns sibling layers by degrees (clockwise, screen y down) around center in their parent’s coordinates, as rotating a multi-selection does in Figma: each layer’s center moves around center and its own rotation grows by the same angle. A path’s transform turns as a whole.

An upright connector is not turned: its two ends move around center (connectorWithMovedEnds), so a free end turns with the selection like a line’s end, and an attached end is placed on its layer again by the page compiler, which cannot attach a turned connector.

function scaleNodesAbout( document: DesignDocument, nodeIds: readonly NodeId[], factor: number, anchor: { readonly x: number; readonly y: number } ): readonly SceneNode[]

source

Scales several sibling layers together by factor about anchor in their shared parent’s coordinates, as the scale tool (K) scales a multi-selection: every layer’s position moves away from the anchor by factor, and each layer scales as scaleSubtree scales one, strokes, radii, effects and text included. Returns every changed node, or an empty list when a layer is missing or locked or factor is not positive.

function connectorWithMovedEnds( node: ConnectorNode, move: (point: Point) => Point ): ConnectorNode

source

An upright connector whose two ends are moved by move (from and to points in its parent’s coordinates); its frame becomes the box around the moved ends. The web app’s store then refits the frame to the line the renderer draws (apps/web/src/store/connectorFrames.ts), which places an attached end on its layer’s edge whatever point is stored for it.

Alignment, snapping and measurement

Object snapping lines the edges, centers and text baselines of a moving box up with nearby targets and reports the guides to draw. Distance measurement gives the red distances shown while a copy is Option-dragged away from its original, and on Option hover.

function snapObjectBounds( initialBounds: AlignmentRect, rawDeltaX: number, rawDeltaY: number, targets: readonly AlignmentTarget[], threshold: number, movingBaselines: readonly number[] = [] ): ObjectSnapResult

source

interface ObjectSnapResult

source
readonly deltaX: numbersource
readonly deltaY: numbersource
readonly guides: readonly AlignmentGuide[]source

interface AlignmentRect

source
readonly left: numbersource
readonly top: numbersource
readonly right: numbersource
readonly bottom: numbersource

interface AlignmentTarget

source
readonly id: stringsource
readonly bounds: AlignmentRectsource
readonly baselines?: readonly number[]source
readonly line?: "x" | "y"source

Makes the target a single line, such as a ruler guide. An “x” line sits at bounds.left and only offers a horizontal position; a “y” line sits at bounds.top and only offers a vertical one. Any edge or center of the moving bounds may land on the line, and the match line shown for it spans only the moving bounds, because the guide itself is already visible.

type AlignmentAnchor = "start" | "center" | "end" | "baseline"

source

Which point of a box lines up: its left/top, center, right/bottom, or a text baseline.

interface AlignmentGuide

source
readonly orientation: "horizontal" | "vertical"source
readonly position: numbersource
readonly start: numbersource
readonly end: numbersource
readonly movingAnchor: AlignmentAnchorsource

The moving box’s anchor on this guide. Overlays use it to draw the guide on the same device pixels as the moving selection outline, which the renderer draws inside the box: a guide on a left or top edge covers the pixels after the line, one on a right or bottom edge the pixels before it.

function measureDistances( reference: Rect, moving: Rect ): readonly DistanceMeasurement[]

source

Measurements from reference to moving, both axis-aligned rectangles in the same coordinate space. Zero distances are left out, so aligned edges produce no measurement.

interface DistanceMeasurement

source
readonly axis: MeasurementAxissource

“x” measures a horizontal distance, drawn as a horizontal line.

readonly start: numbersource

Where the line starts and ends on the measured axis; start < end.

readonly end: numbersource
readonly position: numbersource

The line’s coordinate on the other axis.

readonly value: numbersource

end - start, the number the label shows.

readonly kind: "gap" | "offset" | "inset"source

Which kind of distance this is, for labels and tests.

readonly extension: MeasurementExtension | nullsource

type MeasurementAxis = "x" | "y"

source

Red distance measurements shown while a copy is ⌥-dragged away from its original, following Figma’s option-drag and ⌥-hover measurements.

Each measurement is a straight line on one axis between an edge of the reference (the original) and an edge of the moving object (the copy):

  • When the two objects are apart on an axis, it spans the gap.
  • When they overlap or touch on that axis but are apart on the other, it spans the offset between the edges on the side where the moving object extends past the reference.
  • When they overlap on both axes, four inset measurements run between each pair of matching edges.

The line runs through the reference’s center (or through the center of the overlap when the objects overlap on both axes). When the moving object does not reach that line, a dashed extension continues the moving object’s edge so the measurement has something to land on.

All values are in the same units as the input rectangles (page units).

interface MeasurementExtension

source
readonly position: numbersource

Coordinate of the moving object’s edge on the measured axis.

readonly start: numbersource

Span on the other axis, from the moving object to the measurement line.

readonly end: numbersource

function formatMeasurementValue(value: number): string

source

The number a measurement label shows: at most two decimals and no trailing zeros, as Figma shows distances (“84”, “12.5”, “0.33”).

Rich-text editing

Text positions are paragraph-relative, count UTF-16 code units, and carry an affinity for positions that can be drawn in two places. A selection keeps its anchor and focus, so its direction is kept.

interface TextPosition

source

A paragraph-relative text location. Offsets always count UTF-16 code units.

readonly paragraphIndex: numbersource
readonly utf16Offset: numbersource
readonly affinity: TextAffinitysource

type TextAffinity = "upstream" | "downstream"

source

Which side of a boundary a text position belongs to when one offset can be drawn in two places, such as a line wrap or a bidirectional text run edge.

interface TextRange

source

Anchor and focus preserve selection direction instead of normalizing the range.

readonly anchor: TextPositionsource
readonly focus: TextPositionsource

interface TextCompositionState

source

Transient IME text replacing a canonical document range.

readonly replacementRange: TextRangesource

Canonical range that the composition replaced when it began.

readonly displayRange: TextRangesource

Range occupied by the currently published composition preview.

readonly text: stringsource
readonly selectionStart: numbersource

UTF-16 offsets within text, mirroring browser composition APIs.

readonly selectionEnd: numbersource

interface ActiveTextEditingState

source

The text node being edited, its current selection, and any active IME composition.

readonly nodeId: NodeIdsource
readonly selection: TextRangesource
readonly composition: TextCompositionState | nullsource

function assertTextPosition( value: unknown, label = "Text position" ): asserts value is TextPosition

source

Checks that a value is a TextPosition: integer, non-negative paragraph index and UTF-16 offset, and a known affinity. Throws TypeError or RangeError. It does not check the position against any document.

function assertTextRange( value: unknown, label = "Text range" ): asserts value is TextRange

source

Checks that a value is a TextRange whose anchor and focus pass assertTextPosition.

function assertActiveTextEditingState( value: unknown, label = "Active text editing state" ): asserts value is ActiveTextEditingState

source

Checks that a value is an ActiveTextEditingState. A composition, when present, needs valid ranges and a selection that is ordered, non-negative and within the composition text length in UTF-16 code units.

reduceRichTextEditing is a pure state transition for text editing. It never measures text, dispatches document operations or records history. Instead it returns effects: selection changes, navigation queries for the renderer to answer, paragraph edits inside transactions, and clipboard writes.

function createRichTextEditingState( input: { readonly nodeId: NodeId; readonly paragraphs: readonly TextParagraph[]; readonly selection: TextRange; readonly layoutGeneration: bigint; } ): RichTextEditingState

source

function reduceRichTextEditing( state: RichTextEditingState, action: RichTextEditingAction ): RichTextEditingTransition

source

Pure rich-text state transition. The controller never measures text, chooses grapheme navigation stops, dispatches document operations, or records history.

interface RichTextEditingState

source

Immutable editor state. It contains canonical paragraphs but does not own a document store or history stack; callers apply the returned effects.

readonly nodeId: NodeIdsource
readonly paragraphs: readonly TextParagraph[]source
readonly selection: TextRangesource
readonly layoutGeneration: bigint | nullsource

The generation that exactly matches paragraphs, or null after an edit.

readonly lastLayoutGeneration: bigintsource

Highest synchronized generation, retained while layoutGeneration is null.

readonly composition: RichTextCompositionSession | nullsource
readonly pointerSelection: RichTextPointerSelectionState | nullsource
readonly pendingNavigation: RichTextPendingNavigation | nullsource
readonly pendingRunFormat: RichTextPendingRunFormat | nullsource

Full authored run style used for subsequent collapsed-caret insertion.

readonly preferredLocalX: number | undefinedsource
readonly nextRequestId: numbersource
readonly nextTransactionId: numbersource

type RichTextEditingAction = | RichTextPointerAction | { readonly kind: "layout-synchronized"; readonly generation: bigint } | { readonly kind: "navigation-intent"; readonly operation: TextNavigationOperation; readonly extend: boolean; } | { readonly kind: "navigation-result"; readonly requestId: number; readonly result: TextNavigationResult | null; } | { readonly kind: "beforeinput"; readonly input: EditorTextInput } | { readonly kind: "composition"; readonly input: EditorCompositionInput } | { readonly kind: "clipboard"; readonly input: EditorClipboardInput } | { readonly kind: "keyboard"; readonly input: EditorKeyboardInput; readonly platform: RichTextKeyboardPlatform; } | { readonly kind: "format-runs"; readonly expectedSelection: TextRange; readonly expectedLayoutGeneration: bigint; readonly patch: RichTextRunFormatPatch; } | { readonly kind: "format-paragraphs"; readonly expectedSelection: TextRange; readonly expectedLayoutGeneration: bigint; readonly patch: RichTextParagraphFormatPatch; }

source

interface RichTextEditingTransition

source
readonly state: RichTextEditingStatesource
readonly effects: readonly RichTextEditingEffect[]source

type RichTextEditingEffect = | { readonly kind: "selection-change"; readonly nodeId: NodeId; readonly selection: TextRange; } | { readonly kind: "navigation-request"; readonly requestId: number; readonly query: TextNavigationQuery; } | { readonly kind: "transaction"; readonly boundary: RichTextTransactionBoundary; readonly transactionId: number; readonly intent: RichTextEditIntent; } | { readonly kind: "paragraph-edit"; readonly transactionId: number; readonly intent: RichTextEditIntent; readonly stage: RichTextParagraphEditStage; readonly nodeId: NodeId; readonly paragraphs: readonly TextParagraph[]; readonly selection: TextRange; readonly composition: TextCompositionState | null; } | { readonly kind: "rejected-intent"; readonly inputType: string; readonly reason: string; } | { readonly kind: "clipboard-write"; readonly action: "copy" | "cut"; readonly items: readonly { readonly mimeType: string; readonly data: Uint8Array; }[]; }

source

type RichTextEditIntent = | "insert-text" | "insert-paragraph" | "insert-line-break" | "paste-rich" | "paste-plain" | "delete-grapheme-backward" | "delete-grapheme-forward" | "delete-word-backward" | "delete-word-forward" | "delete-line-backward" | "delete-line-forward" | "delete-paragraph-backward" | "delete-paragraph-forward" | "delete-selection" | "format-runs" | "format-paragraphs" | "composition"

source

type RichTextPointerAction = | { readonly kind: "pointer"; readonly phase: "down"; readonly pointerId: number; readonly hit: TextHitResult; readonly extend: boolean; readonly clickCount?: number; } | { readonly kind: "pointer"; readonly phase: "move" | "up"; readonly pointerId: number; readonly hit: TextHitResult; } | { readonly kind: "pointer"; readonly phase: "cancel" | "lost-capture"; readonly pointerId: number; }

source

interface RichTextPointerSelectionState

source
readonly pointerId: numbersource
readonly anchor: TextPositionsource
readonly unit?: TextRangesource

The word or paragraph a double or triple press selected. Dragging then keeps it selected and extends from its far edge, as browsers do.

readonly baselineSelection: TextRangesource
readonly baselinePendingRunFormat: RichTextPendingRunFormat | nullsource

function pointerUnitRange( paragraphs: readonly TextParagraph[], position: TextPosition, unit: "word" | "paragraph" ): TextRange

source

The word or paragraph a double or triple press selects. A double press between words selects the space or punctuation it lands on, as browsers do.

interface RichTextPendingNavigation

source
readonly requestId: numbersource
readonly layoutGeneration: bigintsource
readonly operation: TextNavigationOperationsource
readonly purpose: RichTextPendingNavigationPurposesource

type RichTextPendingNavigationPurpose = | { readonly kind: "selection"; readonly extend: boolean } | { readonly kind: "delete"; readonly transactionId: number; readonly intent: RichTextEditIntent; readonly origin: TextPosition; }

source

interface RichTextCompositionSession extends TextCompositionState

source
readonly transactionId: numbersource
readonly baselineParagraphs: readonly TextParagraph[]source
readonly baselineSelection: TextRangesource
readonly previewApplied: booleansource

Whether a paragraph preview has been published to the host.

type RichTextKeyboardPlatform = "macos" | "windows-linux"

source

type RichTextTransactionBoundary = "begin" | "commit" | "cancel"

source

type RichTextParagraphEditStage = | "canonical" | "composition-preview" | "composition-commit" | "composition-restore"

source

function activeTextEditingState( state: RichTextEditingState ): ActiveTextEditingState

source

Converts controller state to the renderer overlay contract.

Formatting edits change runs and paragraphs in the selection. The aggregate functions report the formatting a toolbar should show for a selection.

interface RichTextRunFormatPatch

source
readonly styleId?: RichTextFieldEdit<TextStyleId>source
readonly overrides?: { readonly [Key in keyof RichTextRunOverrides]?: RichTextFieldEdit< Exclude<RichTextRunOverrides[Key], undefined> > }source
readonly openTypeFeatureEdits?: RichTextTaggedValueEdits<boolean | number>source

Merges individual tags without replacing unrelated authored feature values.

readonly fontVariationEdits?: RichTextTaggedValueEdits<number>source

Merges individual axes without replacing unrelated authored variation values.

interface RichTextParagraphFormatPatch

source

Paragraph fields use the same explicit set/remove editing contract.

readonly direction?: RichTextFieldEdit<NonNullable<TextParagraph["direction"]>>source
readonly align?: RichTextFieldEdit<NonNullable<TextParagraph["align"]>>source
readonly spacingBefore?: RichTextFieldEdit<number>source
readonly spacingAfter?: RichTextFieldEdit<number>source
readonly indent?: RichTextFieldEdit<NonNullable<TextParagraph["indent"]>>source
readonly list?: RichTextFieldEdit<NonNullable<TextParagraph["list"]>>source
readonly indentFields?: RichTextIndentFieldEditssource

Merges individual indent values; remove restores that value to zero.

readonly listFields?: RichTextListFieldEditssource

Merges list controls while preserving an untouched marker.

readonly listMarkerFields?: RichTextListMarkerFieldEditssource

Merges marker controls without creating a list implicitly.

interface RichTextIndentFieldEdits

source
readonly left?: RichTextFieldEdit<number>source
readonly right?: RichTextFieldEdit<number>source
readonly firstLine?: RichTextFieldEdit<number>source

interface RichTextListFieldEdits

source
readonly kind?: RichTextFieldEdit<"ordered" | "unordered">source

Removing kind clears the list; changing it preserves marker authorship.

readonly level?: RichTextFieldEdit<number>source

Removing level restores level zero.

readonly start?: RichTextFieldEdit<number>source

Removing start restores automatic numbering.

interface RichTextListMarkerFieldEdits

source
readonly text?: RichTextFieldEdit<NonNullable<TextListMarker["text"]>>source

Removing text restores the renderer’s automatic marker text.

readonly styleId?: RichTextFieldEdit<NonNullable<TextListMarker["styleId"]>>source

Removing styleId restores first-run shared-style inheritance.

readonly overrides?: RichTextFieldEdit<NonNullable<TextListMarker["overrides"]>>source

Removing overrides restores first-run direct-format inheritance.

readonly gap?: RichTextFieldEdit<NonNullable<TextListMarker["gap"]>>source

Removing gap restores the renderer’s font-relative automatic gap.

type RichTextFieldEdit<Value> = | { readonly kind: "set"; readonly value: Value } | { readonly kind: "remove" }

source

Omission leaves a field unchanged; remove restores inheritance.

type RichTextTaggedValueEdits< Value > = Readonly<Record<string, RichTextFieldEdit<Value>>>

source

type RichTextRunOverrides = NonNullable<TextRun["overrides"]>

source

type RichTextPendingRunFormat = Readonly<Pick<TextRun, "styleId" | "overrides">>

source

function selectedRichTextRunFormats( state: RichTextEditingState ): readonly RichTextPendingRunFormat[]

source

Enumerates immutable authored run formats intersecting the current selection.

function aggregateRichTextSelectionFormatting( state: RichTextEditingState ): RichTextSelectionFormatting

source

Returns authored selection formatting without consulting DOM editing state.

interface RichTextSelectionFormatting

source
readonly run: { readonly styleId: RichTextAggregatedValue<TextStyleId | undefined>; readonly overrides: RichTextAggregatedRunOverrides; readonly openTypeFeaturesByTag: Readonly< Record<string, RichTextAggregatedValue<boolean | number | undefined>> >; readonly fontVariationsByTag: Readonly< Record<string, RichTextAggregatedValue<number | undefined>> >; }source
readonly paragraph: { readonly direction: RichTextAggregatedValue<TextParagraph["direction"]>; readonly align: RichTextAggregatedValue<TextParagraph["align"]>; readonly spacingBefore: RichTextAggregatedValue<number | undefined>; readonly spacingAfter: RichTextAggregatedValue<number | undefined>; readonly indent: RichTextAggregatedValue<TextParagraph["indent"]>; readonly list: RichTextAggregatedValue<TextParagraph["list"]>; readonly indentFields: { readonly left: RichTextAggregatedValue<number | undefined>; readonly right: RichTextAggregatedValue<number | undefined>; readonly firstLine: RichTextAggregatedValue<number | undefined>; }; readonly listFields: { readonly kind: RichTextAggregatedValue< "ordered" | "unordered" | undefined >; readonly level: RichTextAggregatedValue<number | undefined>; readonly start: RichTextAggregatedValue<number | undefined>; }; readonly listMarkerFields: { readonly text: RichTextAggregatedValue<TextListMarker["text"]>; readonly styleId: RichTextAggregatedValue<TextListMarker["styleId"]>; readonly overrides: RichTextAggregatedValue<TextListMarker["overrides"]>; readonly gap: RichTextAggregatedValue<TextListMarker["gap"]>; }; }source

type RichTextAggregatedValue<Value> = | { readonly kind: "uniform"; readonly value: Value } | { readonly kind: "mixed" }

source

type RichTextAggregatedRunOverrides = { readonly [Key in keyof RichTextRunOverrides]-: RichTextAggregatedValue< RichTextRunOverrides[Key] | undefined > }

source

Vector editing

Vector editing works on a vector network: vertices, segments between them, and regions made of segment loops. The renderer reports the network’s geometry in node-local coordinates and hit tests it.

interface VectorGeometry

source
readonly nodeId: NodeIdsource
readonly generation: bigintsource
readonly vertices: readonly VectorVertexGeometry[]source
readonly handles: readonly VectorCubicHandleGeometry[]source
readonly segments: readonly VectorSegmentGeometry[]source
readonly regions: readonly VectorRegionGeometry[]source

interface VectorVertexGeometry

source
readonly id: stringsource
readonly kind: "vertex"source
readonly localPoint: Pointsource
readonly screenPoint: Pointsource

interface VectorCubicHandleGeometry

source
readonly id: stringsource
readonly kind: "cubic-handle"source
readonly segmentId: stringsource
readonly vertexId: stringsource
readonly endpoint: "start" | "end"source
readonly localPoint: Pointsource
readonly screenPoint: Pointsource

type VectorSegmentGeometry = | VectorLineSegmentGeometry | VectorCubicSegmentGeometry | VectorArcSegmentGeometry

source

interface VectorLineSegmentGeometry extends VectorSegmentGeometryBase

source
readonly kind: "line"source

interface VectorCubicSegmentGeometry extends VectorSegmentGeometryBase

source
readonly kind: "cubic"source
readonly startHandleId: stringsource
readonly endHandleId: stringsource

interface VectorArcSegmentGeometry extends VectorSegmentGeometryBase

source
readonly kind: "elliptical-arc"source
readonly radiusX: numbersource
readonly radiusY: numbersource
readonly xAxisRotation: numbersource
readonly largeArc: booleansource
readonly sweep: booleansource

interface VectorRegionGeometry

source
readonly id: stringsource
readonly kind: "region"source
readonly windingRule: "nonzero" | "evenodd"source
readonly authoredOrder: numbersource
readonly localBounds: Rectsource
readonly screenBounds: Rectsource

interface VectorHitQuery

source
readonly nodeId: NodeIdsource
readonly screenPoint: Pointsource
readonly toleranceCssPx: numbersource

type VectorQueryHit = & { readonly id: string; readonly priority: number; readonly authoredOrder: number; readonly distanceCssPx: number; } & ( | { readonly kind: "vertex" } | { readonly kind: "cubic-handle"; readonly endpoint: "start" | "end" } | { readonly kind: "segment" } | { readonly kind: "region" })

source

interface VectorHitTestResult

source
readonly nodeId: NodeIdsource
readonly generation: bigintsource
readonly hits: readonly VectorQueryHit[]source

The topology functions return a new network with one change applied. They throw VectorTopologyError, with a code, when the change cannot be made: a missing component, a duplicate id, a bad parameter or an unsafe region edit.

class VectorTopologyError extends Error

source
readonly code: VectorTopologyErrorCodesource
readonly componentId: string | nullsource
new ( code: VectorTopologyErrorCode, message: string, componentId: string | null = null )source

type VectorTopologyErrorCode = | "DUPLICATE_ID" | "INVALID_PARAMETER" | "MISSING_HANDLE" | "MISSING_LOOP" | "MISSING_REGION" | "MISSING_SEGMENT" | "MISSING_VERTEX" | "UNSAFE_REGION_EDIT" | "UNSUPPORTED_SEGMENT"

source

function addVectorVertex( network: VectorNetwork, vertex: VectorVertex ): VectorNetwork

source

Adds one caller-identified free anchor without changing existing topology.

function addVectorSegment( network: VectorNetwork, segment: VectorSegment ): VectorNetwork

source

Adds one line or cubic edge between existing anchors using caller-owned IDs.

function updateVectorVertices( network: VectorNetwork, updates: readonly VectorVertexUpdate[] ): VectorNetwork

source

Moves one or more shared vertices without changing endpoint-relative handles.

interface VectorVertexUpdate

source
readonly vertexId: VectorVertexIdsource
readonly x: numbersource
readonly y: numbersource

function updateVectorHandle( network: VectorNetwork, update: VectorHandleUpdate ): VectorNetwork

source

Updates one control handle and applies its optional tangent constraint atomically.

interface VectorHandleUpdate

source
readonly handleId: VectorHandleIdsource
readonly dx: numbersource
readonly dy: numbersource

function setVectorTangentConstraint( network: VectorNetwork, update: VectorTangentConstraintUpdate ): VectorNetwork

source

Couples two handles at one vertex, replacing either handle’s prior pair.

interface VectorTangentConstraintUpdate

source
readonly vertexId: VectorVertexIdsource
readonly handleIds: readonly [VectorHandleId, VectorHandleId]source
readonly drivenHandleId: VectorHandleIdsource

The driven handle keeps its position; the paired handle is aligned to it.

readonly mode: VectorTangentConstraint["mode"]source

function removeVectorTangentConstraints( network: VectorNetwork, handleIds: readonly VectorHandleId[] ): VectorNetwork

source

Removes every tangent pair involving one of the supplied handles.

function deleteVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[] ): VectorNetwork

source

Deletes anchors, every incident edge, affected fill loops, and tangent pairs.

function deleteVectorSegments( network: VectorNetwork, segmentIds: readonly VectorSegmentId[], options: DeleteVectorComponentsOptions = {} ): VectorNetwork

source

Deletes edges and any fill loops that depended on them.

function deleteVectorRegions( network: VectorNetwork, regionIds: readonly VectorRegionId[] ): VectorNetwork

source

Deletes independently paintable regions without changing their shared topology.

interface DeleteVectorComponentsOptions

source
readonly deleteOrphanVertices?: booleansource

Segment deletion preserves free anchors by default.

function addVectorRegion( network: VectorNetwork, region: VectorRegion ): VectorNetwork

source

Adds one independently paintable region over an existing connected chain.

function openVectorRegionLoop( network: VectorNetwork, target: VectorRegionLoopTarget, segmentId: VectorSegmentId ): VectorNetwork

source

Opens one network-closed fill contour by removing an exclusive edge. The remaining chain keeps the same fill through implicitClose; shared fill edges are rejected because removing one would silently damage other regions.

function closeVectorRegionLoop( network: VectorNetwork, target: VectorRegionLoopTarget, segment: VectorSegment ): VectorNetwork

source

Closes one implicitly filled open contour with a caller-identified line or cubic. The supplied edge may use either orientation across the open gap.

interface VectorRegionLoopTarget

source
readonly regionId: VectorRegionIdsource
readonly loopIndex: numbersource

function joinVectorVertices( network: VectorNetwork, sourceVertexId: VectorVertexId, targetVertexId: VectorVertexId, options: JoinVectorVerticesOptions = {} ): VectorNetwork

source

Merges one shared anchor into another and rewires all incident topology.

interface JoinVectorVerticesOptions

source
readonly position?: { readonly x: number; readonly y: number }source

Defaults to the retained target vertex position.

function convertVectorLineToCubic( network: VectorNetwork, segmentId: VectorSegmentId, ids: VectorLineToCubicIds ): VectorNetwork

source

Converts a line to a cubic while preserving its segment ID and region references.

interface VectorLineToCubicIds

source
readonly startHandleId: VectorHandleIdsource
readonly endHandleId: VectorHandleIdsource

function convertVectorCubicToLine( network: VectorNetwork, segmentId: VectorSegmentId ): VectorNetwork

source

Converts a cubic to a line and removes constraints that referenced its handles.

function splitVectorSegment( network: VectorNetwork, segmentId: VectorSegmentId, amount: number, ids: VectorSplitIds ): VectorNetwork

source

Splits a line or cubic at 0<t<1. The original segment keeps its ID and covers the first half; original endpoint-handle IDs remain at their authored endpoints.

interface VectorSplitIds

source
readonly vertexId: VectorVertexIdsource
readonly segmentId: VectorSegmentIdsource
readonly firstEndHandleId?: VectorHandleIdsource

Required when splitting a cubic segment.

readonly secondStartHandleId?: VectorHandleIdsource

Required when splitting a cubic segment.

type VectorEditableSegment = Extract<VectorSegment, { kind: "line" | "cubic" }>

source

reduceVectorEditing holds the component selection, drag state and preview network for one path. It returns effects for the caller: pointer capture, selection changes and a commit-network request with a reason.

function createVectorEditingState( options: { readonly nodeId: NodeId; readonly network: VectorNetwork; readonly selection?: VectorEditingSelection; } ): VectorEditingState

source

function reduceVectorEditing( state: VectorEditingState, action: VectorEditingAction ): VectorEditingResult

source

interface VectorEditingState

source
readonly nodeId: NodeIdsource
readonly network: VectorNetworksource
readonly previewNetwork: VectorNetworksource
readonly selection: VectorEditingSelectionsource
readonly gesture: VectorEditingGesture | nullsource

type VectorEditingAction = | { readonly type: "select"; readonly hit: VectorComponentHit | null; readonly modifiers: EditorModifiers; } | { readonly type: "drag-start"; readonly hit: VectorComponentHit; readonly pointerId: number; readonly position: Point; readonly modifiers: EditorModifiers; readonly bend?: { readonly ids: VectorEditIdAllocator; readonly threshold: number; }; readonly breakThreshold?: number; readonly distanceSpace?: TransformMatrix; } | { readonly type: "point-box-start"; readonly pointerId: number; readonly position: Point; readonly box: VectorPointBox; readonly hit: VectorPointBoxHit; readonly localToBox: TransformMatrix; readonly boxToLocal: TransformMatrix; readonly modifiers: EditorModifiers; } | { readonly type: "drag-move"; readonly pointerId: number; readonly position: Point; readonly modifiers?: EditorModifiers; } | { readonly type: "drag-end" | "drag-cancel" | "lost-capture"; readonly pointerId: number; } | { readonly type: "commit-topology"; readonly network: VectorNetwork; readonly reason: VectorEditingCommitReason; readonly selection?: VectorEditingSelection; } | { readonly type: "external-synchronize"; readonly network: VectorNetwork }

source

interface VectorEditingResult

source
readonly state: VectorEditingStatesource
readonly effects: readonly VectorEditingEffect[]source

type VectorEditingEffect = | { readonly type: "capture-pointer"; readonly pointerId: number } | { readonly type: "release-pointer"; readonly pointerId: number } | { readonly type: "selection-changed"; readonly selection: VectorEditingSelection; } | { readonly type: "commit-network"; readonly nodeId: NodeId; readonly network: VectorNetwork; readonly reason: VectorEditingCommitReason; }

source

type VectorEditingCommitReason = | "move-vertices" | "move-handle" | "move-segment" | "add-segment" | "convert-segment" | "split-segment" | "join-segments" | "delete-components" | "open-path" | "close-path" | "tangent-constraint" | "vertex-corner-radius" | "boolean-operation" | "add-point" | "bend" | "cut" | "region-fill"

source

interface VectorEditingSelection

source
readonly vertexIds: readonly VectorVertexId[]source
readonly handleIds: readonly VectorHandleId[]source
readonly segmentIds: readonly VectorSegmentId[]source
readonly regionIds: readonly VectorRegionId[]source
readonly primary: VectorComponentSelection | nullsource

interface VectorEditingGesture

source
readonly pointerId: numbersource
readonly component: Extract< VectorComponentSelection, { readonly kind: "vertex" | "handle" | "segment" } >source
readonly startedAt: Pointsource
readonly currentAt: Pointsource
readonly sourceNetwork: VectorNetworksource
readonly draggedVertexIds: readonly VectorVertexId[]source
readonly collapseOnClick?: booleansource

A plain press on a component that was already selected among others: if it ends without moving, it selects just that component, as in Figma.

readonly baseNetwork?: VectorNetworksource

The network a handle drag edits, when it isn’t sourceNetwork: an Alt drag on a handle first drops the handle’s mirroring pair.

readonly breakThreshold?: numbersource

How far the pointer must go before an Alt drag applies baseNetwork; a press that never gets that far changes nothing. In network units, or in the units of distanceSpace when that is set.

readonly distanceSpace?: TransformMatrixsource

A matrix from network coordinates to the space this gesture measures its thresholds in, such as the screen. Without it they are in network units.

readonly breakStarted?: booleansource

Set once the pointer has gone past breakThreshold, and stays set.

readonly bend?: VectorBendGesturesource

A Bend tool drag, which shapes handles instead of moving points.

readonly pointBox?: VectorPointBoxGesturesource

A drag of the box around several selected points, which scales or rotates them.

type VectorComponentHit = Extract< EditorHit, { readonly part: | "vector-anchor" | "vector-handle" | "vector-segment" | "vector-region"; } >

source

type VectorComponentSelection = | { readonly kind: "vertex"; readonly id: VectorVertexId } | { readonly kind: "handle"; readonly id: VectorHandleId } | { readonly kind: "segment"; readonly id: VectorSegmentId } | { readonly kind: "region"; readonly id: VectorRegionId }

source

function vectorComponentHitFromQueryHit( nodeId: NodeId, network: VectorNetwork, hit: VectorQueryHit ): VectorComponentHit | null

source

Maps an unbranded renderer query hit back to the matching canonical record. IDs are deliberately validated within their component kind because the ABI permits a vertex, handle, segment, and region to share the same string.

updateVectorInteraction connects pointer input to vector editing. It hit tests only on pointer down; later points in a drag go through the transform fixed at that press.

function createVectorInteractionState( environment: VectorInteractionEnvironment, selection?: VectorEditingSelection ): VectorInteractionState | null

source

Opens component editing only when scene, vector geometry, and node transform all describe one retained generation.

function updateVectorInteraction( environment: VectorInteractionEnvironment, state: VectorInteractionState, input: EditorPointerInput ): VectorInteractionResult

source

Reduces existing-path component input. Only pointer-down performs a vector hit query; a gesture uses its frozen inverse transform for every later point.

function invalidateVectorInteraction( state: VectorInteractionState, reason: VectorInteractionInvalidationReason ): VectorInteractionResult

source

interface VectorInteractionEnvironment

source
readonly nodeId: NodeIdsource
readonly network: VectorNetworksource

Exact canonical network object currently owned by nodeId.

readonly viewport: ViewportStatesource

Exact viewport object currently installed on renderSession.

readonly renderSession: VectorInteractionRenderQueriessource
readonly hitToleranceCssPx?: numbersource

Native component-hit radius in CSS pixels. Defaults to 6.

readonly editTool?: VectorEditToolContextsource

The vector edit mode tool, when it isn’t the Move tool: Pen adds a point where it presses a segment, Bend shapes handles from a point or segment, and Cut breaks the path at a point or segment. ids names what they add.

readonly pointBox?: booleansource

Offer the box around several selected points (the Move tool does): a press on one of its handles or rotate zones scales or rotates the points.

type VectorInteractionRenderQueries = Pick< InteractiveRenderSession, "getSceneGeometrySnapshot" | "getVectorGeometry" | "hitTestVector" >

source

interface VectorInteractionState

source
readonly editing: VectorEditingStatesource
readonly binding: VectorInteractionBindingsource
readonly status: "ready" | "awaiting-commit" | "invalidated"source

interface VectorInteractionBinding

source
readonly generation: bigintsource
readonly expectedNetwork: VectorNetworksource
readonly viewport: ViewportStatesource
readonly renderSession: VectorInteractionRenderQueriessource
readonly nodeGeometry: NodeGeometrysource
readonly screenToLocal: TransformMatrixsource

type VectorInteractionInvalidationReason = | "node-changed" | "network-changed" | "viewport-changed" | "render-session-changed" | "scene-generation-changed" | "node-geometry-changed" | "vector-query-unavailable" | "coordinate-conversion-failed"

source

interface VectorInteractionResult

source
readonly state: VectorInteractionStatesource
readonly effects: readonly VectorInteractionEffect[]source

type VectorInteractionEffect = | { readonly type: "capture-pointer"; readonly pointerId: number } | { readonly type: "release-pointer"; readonly pointerId: number } | { readonly type: "selection-changed"; readonly selection: VectorEditingSelection; } | { readonly type: "commit-network"; readonly nodeId: NodeId; readonly expectedNetwork: VectorNetwork; readonly network: VectorNetwork; readonly reason: Extract< VectorEditingEffect, { readonly type: "commit-network" } >["reason"]; readonly generation: bigint; } | { readonly type: "editing-invalidated"; readonly reason: VectorInteractionInvalidationReason; }

source

function vectorInteractionLocalPoint( state: VectorInteractionState, screenPoint: Point ): Point | null

source

Converts one screen point through the interaction’s exact frozen affine inverse.

function vectorEditingSnapshot( state: VectorInteractionState ): VectorEditingSnapshot | null

source

Returns null once the frozen renderer/canonical authority is invalidated.

interface VectorEditingSnapshot

source

Renderer-neutral state for one existing path’s component editor. The source network is the exact canonical object that authorizes a later commit, while network is the transient preview painted by an overlay.

readonly nodeId: NodeIdsource
readonly generation: bigintsource
readonly coordinateSpace: "node-local"source
readonly status: "ready" | "awaiting-commit"source
readonly sourceNetwork: VectorNetworksource
readonly network: VectorNetworksource
readonly selection: VectorEditingSelectionSnapshotsource
readonly gesture: VectorEditingGestureSnapshot | nullsource
readonly pendingHandle?: PenPendingHandle | nullsource

The handle the Pen is pulling out of a point, or left there for the next segment: drawn as chrome, because no segment of network owns it yet. Its point is in network. Absent or null when there is none.

readonly viewport: ViewportStatesource

Exact viewport object frozen with this retained-scene generation.

readonly localToDocument: TransformMatrixsource
readonly localToScreen: TransformMatrixsource
readonly screenToLocal: TransformMatrixsource

interface VectorEditingSelectionSnapshot

source
readonly vertexIds: readonly VectorVertexId[]source
readonly handleIds: readonly VectorHandleId[]source
readonly segmentIds: readonly VectorSegmentId[]source
readonly regionIds: readonly VectorRegionId[]source
readonly primary: VectorEditingComponentSnapshot | nullsource

interface VectorEditingGestureSnapshot

source
readonly pointerId: numbersource
readonly component: Extract< VectorEditingComponentSnapshot, { readonly kind: "vertex" | "handle" | "segment" } >source
readonly startedAt: Pointsource

Frozen node-local pointer coordinate at gesture start.

readonly currentAt: Pointsource

Latest node-local pointer coordinate used by the preview.

type VectorEditingComponentSnapshot = | { readonly kind: "vertex"; readonly id: VectorVertexId } | { readonly kind: "handle"; readonly id: VectorHandleId } | { readonly kind: "segment"; readonly id: VectorSegmentId } | { readonly kind: "region"; readonly id: VectorRegionId }

source

Boolean operations (union, intersection, difference, exclusion) are requested from the renderer through InteractiveRenderSession.booleanVectorPaths.

type VectorBooleanOperation = | "union" | "intersection" | "difference" | "exclusion"

source

interface VectorBooleanPathRequest

source

One retained-scene Boolean request with caller-owned result ID authority.

readonly primaryNodeId: NodeIdsource
readonly secondaryNodeId: NodeIdsource
readonly operation: VectorBooleanOperationsource
readonly resultIdNamespace: stringsource

interface VectorBooleanPathResult

source

Canonical primary-local geometry produced from one exact retained generation.

readonly generation: bigintsource
readonly primaryNodeId: NodeIdsource
readonly secondaryNodeId: NodeIdsource
readonly operation: VectorBooleanOperationsource
readonly vectorNetwork: VectorNetworksource

Accessibility

createAccessibilitySnapshotV1 builds the semantic tree a screen reader sees from one document, page and scene geometry generation. Children follow the document’s child order, not paint order. Bounds are clipped screen bounds in CSS pixels. Every object it returns is frozen.

interface AccessibilitySnapshotV1

source
readonly version: typeof ACCESSIBILITY_SNAPSHOT_VERSIONsource
readonly documentId: DocumentIdsource
readonly pageId: PageIdsource
readonly generation: bigintsource

Retained-scene generation, not an editor history revision.

readonly focusOwner: AccessibilityFocusOwnerV1source
readonly root: AccessibilityNodeSnapshotV1source

function createAccessibilitySnapshotV1( input: AccessibilitySnapshotInputV1 ): AccessibilitySnapshotV1

source

Builds the renderer-neutral v1 semantic mirror for one active page. It reads but never mutates the canonical document, geometry, or overlay.

interface AccessibilitySnapshotInputV1

source
readonly document: DesignDocumentsource
readonly activePageId: PageIdsource
readonly geometry: SceneGeometrySnapshotsource
readonly overlay: AccessibilityOverlayStateV1source

interface AccessibilityNodeSnapshotV1

source
readonly id: NodeIdsource
readonly role: AccessibilityRoleV1source
readonly name: stringsource
readonly description: string | nullsource
readonly value: string | nullsource
readonly screenBounds: Rectsource

Visible, clipped bounds in CSS pixels relative to the editor surface.

readonly focused: booleansource
readonly selected: booleansource
readonly editing: booleansource
readonly readOnly: booleansource
readonly actions: readonly AccessibilityActionV1[]source
readonly textSelection: AccessibilityTextSelectionV1 | nullsource
readonly children: readonly AccessibilityNodeSnapshotV1[]source

type AccessibilityRoleV1 = typeof ACCESSIBILITY_ROLES_V1[number]

source

const ACCESSIBILITY_ROLES_V1: readonly [ "document", "group", "text", "textbox", "code", "image", "video", "shape", "connector" ]

source

type AccessibilityActionV1 = typeof ACCESSIBILITY_ACTIONS_V1[number]

source

const ACCESSIBILITY_ACTIONS_V1: readonly [ "focus", "select", "toggle-selection", "edit" ]

source

type AccessibilityFocusOwnerV1 = typeof ACCESSIBILITY_FOCUS_OWNERS_V1[number]

source

const ACCESSIBILITY_FOCUS_OWNERS_V1: readonly [ "none", "react-shell", "canvas", "accessibility-mirror", "text-input" ]

source

type AccessibilityEditStateV1 = | { readonly kind: "text"; readonly nodeId: NodeId; readonly selection: AccessibilityTextSelectionV1; } | { readonly kind: "code"; readonly nodeId: NodeId }

source

interface AccessibilityOverlayStateV1

source

UI-owned state layered over one exact retained scene generation.

readonly focusOwner: AccessibilityFocusOwnerV1source
readonly focusedNodeId: NodeId | nullsource
readonly selectedNodeIds: readonly NodeId[]source
readonly edit: AccessibilityEditStateV1 | nullsource
readonly pageLayersHidden?: booleansource

True when the scene draws none of the page’s own layers, only the open component’s view (the isolation split’s component pane). The mirror then lists the component’s layers alone, and no page layer needs geometry.

interface AccessibilityTextPositionV1

source

Paragraph-relative UTF-16 position, independent of renderer layout.

readonly paragraphIndex: numbersource
readonly utf16Offset: numbersource
readonly affinity: AccessibilityTextAffinityV1source

interface AccessibilityTextSelectionV1

source

Anchor and focus retain selection direction.

readonly anchor: AccessibilityTextPositionV1source
readonly focus: AccessibilityTextPositionV1source

type AccessibilityTextAffinityV1 = typeof ACCESSIBILITY_TEXT_AFFINITIES_V1[number]

source

const ACCESSIBILITY_TEXT_AFFINITIES_V1: readonly ["upstream", "downstream"]

source

const ACCESSIBILITY_SNAPSHOT_VERSION: 1

source

class AccessibilitySnapshotErrorV1 extends Error

source
new (readonly code: AccessibilitySnapshotErrorCodeV1, message: string)source

type AccessibilitySnapshotErrorCodeV1 = | "invalid-active-page" | "invalid-document-tree" | "invalid-geometry" | "invalid-overlay"

source

resolveAccessibilityActionAuthorityV1 checks an action request against the snapshot it came from. A request for another document or page, an old generation, a missing node or an action the node does not offer is refused before anything changes.

function resolveAccessibilityActionAuthorityV1( snapshot: AccessibilitySnapshotV1, request: AccessibilityActionRequestV1 ): AccessibilityActionAuthorityV1

source

Resolves authority only. The caller still owns routing and mutation after this function confirms the exact generation and advertised node action.

interface AccessibilityActionRequestV1

source
readonly documentId: DocumentIdsource
readonly pageId: PageIdsource
readonly generation: bigintsource
readonly nodeId: NodeIdsource
readonly action: AccessibilityActionV1source

interface AccessibilityActionAuthorityV1

source
readonly version: typeof ACCESSIBILITY_SNAPSHOT_VERSIONsource
readonly documentId: DocumentIdsource
readonly pageId: PageIdsource
readonly generation: bigintsource
readonly nodeId: NodeIdsource
readonly action: AccessibilityActionV1source
readonly node: AccessibilityNodeSnapshotV1source

class AccessibilityActionAuthorityErrorV1 extends Error

source
new (readonly code: AccessibilityActionAuthorityErrorCodeV1, message: string)source

type AccessibilityActionAuthorityErrorCodeV1 = | "invalid-request" | "context-mismatch" | "stale-generation" | "node-not-found" | "action-not-advertised"

source

interface AccessibilityNodeSnapshot

source
readonly id: stringsource
readonly role: stringsource
readonly name: stringsource
readonly value?: stringsource
readonly bounds?: Rectsource
readonly focused: booleansource
readonly selected: booleansource
readonly actions: readonly string[]source
readonly children: readonly AccessibilityNodeSnapshot[]source

Scenarios

Shared scenarios run the same editing checks against every EditorDriver. Each scenario states the capabilities it needs. The rich-text, vector and accessibility sets are separate so a backend can adopt them when it supports them.

interface EditorScenario

source
readonly name: stringsource
readonly requirements?: EditorScenarioRequirementssource
run(driver: EditorDriver): Promise<void>source

interface EditorScenarioRequirements

source
readonly pointerTypes?: readonly EditorPointerType[]source
readonly keyboardRepeat?: booleansource
readonly composition?: booleansource
readonly clipboard?: readonly EditorClipboardInput["action"][]source
readonly textEditing?: booleansource
readonly textGeometry?: booleansource
readonly textHitTesting?: booleansource
readonly textNavigation?: booleansource
readonly vectorGeometry?: booleansource
readonly vectorHitTesting?: booleansource
readonly vectorBooleanOperations?: booleansource
readonly accessibility?: booleansource
readonly frameCapture?: booleansource
readonly variableDevicePixelRatio?: booleansource

function runEditorScenarios( driver: EditorDriver, scenarios: readonly EditorScenario[] ): Promise<void>

source

function assertEditorDriverCapabilities( actual: EditorDriverCapabilities, required: EditorScenarioRequirements ): void

source

const standardEditorScenarios: readonly EditorScenario[]

source

Backend-neutral, high-signal editing workflows shared by every backend.

const richTextEditorScenarios: readonly EditorScenario[]

source

Rich-text conformance scenarios are separate until a backend explicitly reports every required capability. They fail any backend that claims support without providing authoritative shaping, navigation, and IME behavior.

const vectorEditorScenarios: readonly EditorScenario[]

source

Existing-path conformance workflows for backends with authoritative vector queries.

const vectorBooleanEditorScenarios: readonly EditorScenario[]

source

Native-only Boolean workflows shared by every backend that advertises the capability.

const accessibilityEditorDriverScenariosV1: readonly EditorScenario[]

source

Backend-neutral accessibility acceptance scenarios for version 1 EditorDrivers.

function runAccessibilityEditorDriverScenariosV1( driver: EditorDriver ): Promise<void>

source

Capability-checks and runs the shared accessibility v1 EditorDriver scenarios.

The accessibility oracle scenarios check createAccessibilitySnapshotV1 and resolveAccessibilityActionAuthorityV1, or a replacement for them, without a driver.

const accessibilityOracleScenariosV1: readonly AccessibilityOracleScenarioV1[]

source

function runAccessibilityOracleScenariosV1( adapter: AccessibilityOracleAdapterV1 = defaultAccessibilityOracleAdapterV1 ): void

source

Runs without consulting or advertising any current EditorDriver capability.

interface AccessibilityOracleAdapterV1

source
createSnapshot(input: AccessibilitySnapshotInputV1): AccessibilitySnapshotV1source
resolveAction( snapshot: AccessibilitySnapshotV1, request: AccessibilityActionRequestV1 ): AccessibilityActionAuthorityV1source

interface AccessibilityOracleScenarioV1

source
readonly name: stringsource
run(adapter: AccessibilityOracleAdapterV1): voidsource

const defaultAccessibilityOracleAdapterV1: AccessibilityOracleAdapterV1

source

Other exports

Exports not yet placed in a section above, in alphabetical order.

function abandonVectorSubPath( network: VectorNetwork, vertexId: VectorVertexId, startedVertexId: VectorVertexId | null ): VectorNetwork | null

source

The network without a sub-path that was started and never drawn: the point is removed when it is the one a Pen click started (startedVertexId), no segment touches it and the network has other points. Returns null when there is nothing to remove. A point without segments that was not started this way stays, because it is the user’s own: for example the end left behind when its segments were deleted. The only point of a network stays too (a layer keeps at least the point it was created with).

function addPenSegment( network: VectorNetwork, shape: PenSegmentShape ): VectorNetwork

source

Adds the segment a Pen press makes between two points of the network.

  • Plain click from a point without a remembered handle: a straight line.
  • Otherwise a cubic. It leaves the start along the remembered handle, or along a third of the straight way to the end when there is none. It arrives at the end along the mirror of the drag, or with no handle after a plain click.

When the remembered handle still mirrors the handle of the segment that arrived at the start point, the two are paired (angle and length), so the point stays smooth when either handle is moved later.

function bendVectorSegment( prepared: VectorPreparedSegmentBend, delta: Point ): VectorNetwork

source

A Bend drag on a segment: moves both handles so the point taken hold of at t moves by delta, the handle nearer to it moving more. Handles paired with a neighbouring segment’s handle keep their pair in step; a pair made of the segment’s own two handles is dropped.

function bendVectorVertex( prepared: VectorPreparedVertex, vertexId: VectorVertexId, heldEnd: number, drag: Point ): VectorNetwork

source

A Bend drag from a point: the held segment end’s handle follows the pointer (drag, from the point) and the other end’s handle mirrors it in angle and length. A point joining more than two segment ends is unchanged.

function compareVectorTangentConstraints( left: VectorTangentConstraint, right: VectorTangentConstraint ): number

source

Orders tangent constraints the way the document schema requires.

function constrainVectorCutLineEnd(start: Point, end: Point): Point

source

The end of a cut line held to 45 degree steps from its start, as Shift does: the nearest of the eight directions, at the pointer’s distance along it. Both points are in screen coordinates.

function convertVectorArcToCubics( network: VectorNetwork, segmentId: VectorSegmentId, ids: VectorEditIdAllocator ): VectorArcConversion

source

Replaces an elliptical arc with the cubic pieces that trace it, joined by new vertices whose two handles keep the same angle. An arc SVG draws as a straight line becomes a line. Fill loops run through the pieces in order.

function cornerVectorVertex( network: VectorNetwork, vertexId: VectorVertexId ): VectorNetwork

source

Makes a point a corner: every handle at it goes to zero length and its handle pairs are dropped. A cubic left with no handles becomes a line.

function createStandardAccessibilityEditorDriverDocumentV1(): DesignDocument

source

Resource-complete canonical document for shared EditorDriver acceptance. It reuses the checked-in standard rich-text fonts so rendering does not depend on host fonts or an invalid placeholder resource. Native code-node compilation remains an artifact requirement of the production Wasm run.

function createStandardAccessibilityScenarioDocumentV1(): DesignDocument

source

Canonical document shared by semantic-oracle implementations.

function createStandardAccessibilityScenarioGeometryV1(): SceneGeometrySnapshot

source

Geometry uses a deliberately different order from the canonical child tree.

function createStandardAccessibilityScenarioInputV1(): AccessibilitySnapshotInputV1

source

function createStandardEditorScenarioDocument( kind: StandardEditorScenarioDocument ): DesignDocument

source

Canonical model documents shared by every interactive rendering backend.

function createStandardVectorBooleanScenarioDocument( kind: StandardVectorBooleanScenarioDocument ): DesignDocument

source

function createStandardVectorScenarioDocument( kind: StandardVectorScenarioDocument ): DesignDocument

source

Canonical existing-path documents used by every capable interactive backend.

const CROSSING_TOLERANCE: 0.000001

source

The share of the edges’ size below which two points count as one.

type Cubic = readonly [Point, Point, Point, Point]

source

A cubic Bézier curve as its four control points.

function cubicLineCrossings( cubic: Cubic, from: Point, to: Point ): readonly number[]

source

The positions t in [0, 1] where a cubic crosses the straight cut line from from to to, in order. A crossing is where the curve passes from one side of the line to the other within the line’s length, so a curve that only touches the line (a tangent) is not cut. A curve that starts or ends on the line is crossed there (t = 0 or 1). A curve lying along the line is not crossed.

function cutVectorNetworkAlongLine( network: VectorNetwork, from: Point, to: Point, ids: VectorEditIdAllocator ): VectorCut

source

Cuts the network along the straight line from from to to (network coordinates): each segment the line crosses gets a point at every crossing and breaks there, and each point the line passes through breaks as a Cut click on it does. Fill loops through a break become open loops, as with the click. Returns the network unchanged, with no points, when the line crosses nothing.

function cutVectorSegment( network: VectorNetwork, segmentId: VectorSegmentId, t: number, ids: VectorEditIdAllocator ): VectorCut

source

Cuts a segment at position t (see VectorSegmentPoint.t): a point goes in there without changing the shape, and the path breaks at it into two ends.

function deleteAndHealVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[], allocate: VectorHealIdAllocator ): VectorNetwork

source

Deletes the given anchors one after another, healing each as described above. Anchors an earlier heal already removed are skipped.

function detachVectorVertex( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator ): VectorCut

source

Gives every segment end at a vertex a vertex of its own at the same place; the first segment end keeps the original. Handle pairs at the vertex are dropped, since their handles no longer meet. A vertex with at most one segment end is left as it is.

function doubleClickSelectionAction( document: DesignDocument, rootId: NodeId, hitNodeId: NodeId, primarySelectedNodeId: NodeId | null, mode: InstanceSelectionMode ): DoubleClickSelectionAction | null

source

The action of a double click on hitNodeId. With the instance as one object ("instance"), a double click on an instance, or on any layer of one, enters isolation for the outermost instance’s component; once the selection is inside that instance it acts as in a group (design question 1). Like a group ("group"), and outside instances, a double click selects one level deeper, and edits the hit layer once it is the selected one.

type DoubleClickSelectionAction = | { readonly kind: "select"; readonly nodeId: NodeId } | { readonly kind: "edit"; readonly nodeId: NodeId } | { readonly kind: "isolate"; readonly instanceId: NodeId }

source

What a double click with the move tool does (components design 5.5).

function findUnstoredVectorFaces( network: VectorNetwork ): readonly VectorRegionLoop[]

source

The faces of a network (findVectorFaces) that no region is stored for yet.

function findVectorAreas(network: VectorNetwork): readonly VectorArea[]

source

The areas a network encloses when every crossing (findVectorCrossings) is treated as a corner: a five-point star drawn in one stroke is six, its middle and five tips; a figure of eight is two; two overlapping loops are three. An area that has a separate loop lying inside it keeps its whole inside. An area no larger than the crossing tolerance times the length of its outline is left out; for a long strip that is a width of twice the tolerance.

These are the areas a fill can be seen in. A region cannot store one of them when its boundary runs along a piece of a segment, so findVectorFaces covers them with loops of whole segments.

function findVectorCrossings(network: VectorNetwork): readonly VectorCrossing[]

source

Every place where two segments of a network meet away from a point they share: they cross, one ends on the other or touches it, or they run along each other (the two ends of the shared stretch are given). A curve that crosses itself counts. Segments that meet at a point of the network they both use are joined there, and that is no crossing.

Lines are intersected exactly. Curves are intersected as the straight pieces their outline is drawn with (24 per cubic piece), so a crossing with a curve is found to within that flattening, and at is measured along those pieces. Places no further apart, in a straight line, than a millionth of the network’s size count as one (CROSSING_TOLERANCE).

The network is not changed: no point is added at a crossing.

function findVectorFaces(network: VectorNetwork): readonly VectorRegionLoop[]

source

The areas a network encloses: the bounded faces of the network read as a planar graph, in network order of their first segment. A closed Pen path is one face; a square with a diagonal is two; a loop inside another, not joined to it, is a face of its own next to the outer one, which keeps its whole inside. Open ends, dangling branches and connectors between loops bound nothing and are left out.

Each face is given as the loop a region for it stores: every segment once, from the face’s first segment.

In a connected part whose segments cross or touch away from a shared point the areas are bounded by pieces of segments, which a loop cannot name. Such a part gives loops of whole segments that cover its areas when filled by winding (the nonzero rule): a closed path that crosses itself gives one loop, the path itself, which fills everything it winds around. These loops may cross themselves and lie over each other. An area that no loop of whole segments winds around is not covered: one enclosed by a path that is not closed, or one a closed path circles as often one way as the other. findVectorAreas gives the areas themselves.

function frontFirst<Layer>(storedOrder: readonly Layer[]): readonly Layer[]

source

The order the layers of one parent are listed in: the front layer first and the back layer last, as Figma does. A parent stores its layers the other way round, back layer first (the order they are drawn in), and that stored order is not changed by the listing.

This one rule holds for every parent: a page, a frame with or without auto layout, a group, an instance and a layer inside one. A mask needs no rule of its own: it is stored before the layers it hides parts of, so it is listed below them.

Everything that lists layers or walks them by keyboard asks here, so the orders cannot disagree: the layers panel’s rows, its Shift-click range, the context menu’s “Select layer” list, Enter, Tab and Shift+Tab on the canvas (childSelectionTarget, siblingSelectionTarget), and the tree a screen reader hears (createAccessibilitySnapshotV1), whose aria-posinset counts from the front layer.

function hitVectorPointBox( box: VectorPointBox, point: Point ): VectorPointBoxHit | null

source

What a press at point (box space) takes hold of: the nearest resize handle within reach, else a rotate zone, which is the ring just outside a corner and outside the box. Null for anywhere else.

function insertVectorPoint( network: VectorNetwork, segmentId: VectorSegmentId, t: number, ids: VectorEditIdAllocator ): VectorPointInsertion

source

Adds a point on a segment at position t (see VectorSegmentPoint.t) without changing the shape, as Figma’s Pen tool does when it clicks a path in edit mode. A cubic’s new point has its two handles at the same angle.

function instanceLayerChildren( document: DesignDocument, id: NodeId ): readonly NodeId[]

source

The resolved children of an instance (its component root’s layers) or of a layer inside one, in paint order; empty for anything else.

function instanceLayerParent( document: DesignDocument, id: NodeId ): NodeId | undefined

source

The parent of a layer inside an instance: the instance’s own ID for a first-level layer. undefined for a stored ID or one that does not resolve.

function instanceLayerPath( document: DesignDocument, id: NodeId ): readonly NodeId[] | undefined

source

The virtual IDs from the instance’s first-level layer down to a virtual ID, outermost first. Empty for a stored ID; undefined for a virtual ID that does not resolve.

function instanceLayerResolveStats(): InstanceResolveCache["stats"]

source

How many instances and component views this module resolved anew (misses) and took from its cache (hits). Tests read it to check that a pass over a document resolves each of them once.

type InstanceSelectionMode = "instance" | "group"

source

The “Selecting inside instances” preference (components design 5.5).

function invertVectorTransform( transform: TransformMatrix ): TransformMatrix | null

source

Inverts every finite, nonsingular affine transform with a representable inverse, or returns null. Vector edit mode uses this one rule wherever it inverts a path transform, so a transform that opens the mode (such as a tiny 1e-12 scale) never fails later in a nudge or a typed X/Y position.

function isInSelectionScope( document: DesignDocument, rootId: NodeId, scopeId: NodeId, nodeId: NodeId ): boolean

source

True for the scope layer and every layer inside it. A pointer on a canvas that selects within the scope acts only on these: a layer of a container around the scope is not under it, though that container stands for the scope when a rule answers with it (scopedSelectionTarget).

function isInstanceLayer(document: DesignDocument, id: NodeId): boolean

source

True when id is a stored instance or a nested instance layer selected as a whole.

function lassoVectorSelection( network: VectorNetwork, before: VectorEditingSelection, contents: VectorLassoContents, mode: VectorLassoMode ): VectorEditingSelection

source

The selection after a lasso. “replace” selects just what the lasso holds; “add” (Shift) keeps what was selected before and adds to it; “subtract” (Option or Alt) takes the points the lasso holds out of what was selected before, and with them every selected segment that ends at one of them. Handles and regions selected before an “add” or “subtract” lasso are kept.

function listedChildIds( document: DesignDocument, parentId: NodeId ): readonly NodeId[]

source

The layers a parent holds, in listed order (frontFirst): the children of a stored frame, group or page root, or the resolved layers of an instance or of a layer inside one. Empty for anything else.

function marqueeVectorSelection( network: VectorNetwork, before: VectorEditingSelection, insideVertexIds: readonly VectorVertexId[], additive: boolean ): VectorEditingSelection

source

The selection after a marquee: the anchors inside it, added to the anchors selected before the drag when Shift is held. Handles, segments and regions selected before an additive marquee are kept.

function mergeDeletedFaceRegions( before: VectorNetwork, after: VectorNetwork ): VectorNetwork

source

Keeps a fill over faces that a delete has merged. Deleting a segment that two faces share drops the regions of both, since each used the segment; the face they become gets one region here:

  • with the fills of the largest of the merged faces, when that one had a region (also when that region was emptied: the merged face is then empty);
  • otherwise with the fills of the largest merged face whose region was filled;
  • and none when no merged face had a region, or only emptied ones beside a larger face without one.

The region keeps the ID and the place in the order of the one it follows. before is the network just before the delete removed points and segments, after the one after it. Every region before has and after lacks counts as dropped by that removal, so a region the user deleted on purpose must be gone from before too, or it could come back over the merged face.

function nearestPointOnVectorSegment( network: VectorNetwork, segmentId: VectorSegmentId, target: Point, distanceSpace?: TransformMatrix ): VectorSegmentPoint

source

The point on a segment nearest to target, in network coordinates.

“Nearest” is measured in network units unless distanceSpace is given: a matrix from network coordinates to the space to measure in. Pass the path’s local-to-screen matrix to find the point nearest on screen, which is a different point when the path is stretched unevenly or skewed.

function nudgeVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[], localDelta: Point ): VectorNetwork

source

Moves the given anchors by one local-space offset in one network edit.

function paintVectorTarget( network: VectorNetwork, target: VectorPaintTarget, regionId: VectorRegionId, layerShowsFill: boolean ): VectorPaintResult

source

A Paint click on a target, decided by what is on screen. A region never gets a colour of its own from Paint: it either takes the layer’s fills (fills omitted) or has none (fills: []), so the Fill panel, which edits the layer’s fills, always changes what is painted.

  • A target that shows a fill loses it: fills: []. Fills of its own (an imported SVG, an older document) are dropped with it; undo brings them back, Paint does not.
  • A target that shows none is filled: it takes the layer’s fills. That holds for a region emptied before and for one whose own fills do not show, which are dropped. A loop without a region gets its region first, named regionId: the same region a shape turned into a path stores for its outline (nonzero, one loop, each segment once), so later edits treat it as they treat any stored region.
  • When the layer shows no fill (layerShowsFill false, see vectorFillsShow), filling asks for one on the layer (layerNeedsFill). Every other region that took the layer’s fills, and so showed nothing, is set to fills: [], so the click changes nothing else on screen.

Only the layer’s fills count for layerShowsFill, not its opacity or visibility. On a hidden layer, or one at 0% opacity, nothing is seen whatever Paint does; a click there still switches the stored fill as it would on a layer that shows, and the result is seen once the layer is shown again.

const PEN_ANGLE_STEP_DEGREES: 45

source

Shift holds the Pen to multiples of this angle, as in Figma.

function penArmedVertexId( network: VectorNetwork, selection: VectorEditingSelection ): VectorVertexId | null

source

The point the Pen draws from, or null for none. It is the primary component when that is a point, however many other components are selected with it; otherwise the only selected point, whatever else (a segment, a handle, a region) is selected or primary. A point the network does not have is never armed. Every Pen path asks here, so a click on empty canvas, on a point and on a segment, the cursor badge and Escape all agree on the source.

function pendingPenOutgoingHandle( network: VectorNetwork, selection: VectorEditingSelection, handle: PenOutgoingHandle | null ): PenOutgoingHandle | null

source

The remembered outgoing handle while it still applies: its point is the one the Pen draws from (penArmedVertexId). Null once another point is armed, the path in progress is ended, or the point is gone (for example by undo).

function pendingVectorSubPath( network: VectorNetwork, startedVertexId: VectorVertexId | null ): VectorVertexId | null

source

The started sub-path that still waits for its first segment: startedVertexId while the network has that point with no segment at it, otherwise null. A caller that remembers which point a Pen click started passes every network it comes to hold through this, so the point stops counting as a started sub-path once it is drawn from or removed, and never counts again after that.

interface PenHandleOffset

source

A curve handle as an offset from its point.

readonly dx: numbersource
readonly dy: numbersource

interface PenOutgoingHandle extends PenHandleOffset

source

The handle a Pen drag pulled out of a point for the segment that is drawn from it next. A point stores no handle of its own (handles belong to segments), so the handle is remembered here until that segment exists.

readonly vertexId: VectorVertexIdsource
readonly mirrorHandleId: VectorHandleId | nullsource

The handle of the segment that arrived at the point in the same drag: the exact mirror of this one. Null for the first point of a sub-path, which no segment arrives at.

interface PenPendingHandle extends PenHandleOffset

source

A handle the canvas draws although no segment owns it yet: the one a Pen drag is pulling out of a point, or the one it left there for the next segment. mirrored also draws its exact mirror about the point, for a point no segment touches (the first point of a sub-path); where a segment arrives in the same drag, that segment’s own handle is the mirror.

readonly vertexId: VectorVertexIdsource
readonly mirrored: booleansource

interface PenSegmentShape

source
readonly id: VectorSegmentIdsource
readonly start: VectorVertexIdsource
readonly end: VectorVertexIdsource
readonly startHandleId: VectorHandleIdsource
readonly endHandleId: VectorHandleIdsource
readonly outgoing: PenOutgoingHandle | nullsource

The handle remembered at the start point, or null when the point has none.

readonly drag: PenHandleOffset | nullsource

How far the pointer was dragged from the end point, or null for a plain click. The pointer holds the handle that leaves the end point, so the segment arrives along its mirror.

function placeVectorVerticesOnAxis( network: VectorNetwork, localToParent: TransformMatrix, vertexIds: readonly VectorVertexId[], axis: "x" | "y", value: number ): VectorNetwork

source

Places every given anchor at value on one parent-space axis, keeping its other coordinate, as typing into the X or Y field does with points selected.

function pointInVectorLasso(outline: readonly Point[], point: Point): boolean

source

Whether point lies inside the closed outline through outline (the last point joins the first). A point on the outline counts as inside. An outline that crosses itself encloses everything it winds around (the nonzero rule), so no pocket of a looping drag is left out.

function pointInVectorPolygons( polygons: readonly (readonly Point[])[], windingRule: VectorRegion["windingRule"], point: Point ): boolean

source

Whether a point is inside polygons filled with the given winding rule.

function prepareVectorSegmentBend( network: VectorNetwork, segmentId: VectorSegmentId, t: number, ids: VectorEditIdAllocator ): VectorPreparedSegmentBend

source

Turns the segment under the pointer into a cubic without changing the shape: a line gets handles a third of the way along, and an arc becomes cubic pieces, of which the one at t is kept with its own position.

function prepareVectorVertexHandles( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator ): VectorPreparedVertex

source

Turns the segments at a vertex into cubics without changing the shape: a line gets two zero handles, and an arc becomes its cubic pieces.

const reboundVectorScenarioViewport: ViewportState

source

function resetRichTextEditStats(): void

source

function resolvedComponentLayersOf( document: DesignDocument, componentId: ComponentId ): ReadonlyMap<NodeId, ResolvedLayer> | undefined

source

The layers of a component’s own view (resolveComponent: the component with every property at its default), by their stored IDs. undefined when the component cannot be resolved (a nesting cycle, a missing nested component); it is drawn from its stored nodes then.

The component is resolved once and its layers are listed once, however many of its layers are asked for: the resolved view comes from this module’s cache, which gives the same view again for a later document whose component, theme and assets are unchanged.

function resolvedInstanceOf( document: DesignDocument, instanceId: NodeId ): ResolvedInstance | undefined

source

The resolved tree of an instance, or undefined when instanceId is not an instance or fails to resolve.

function resolvedLayerOf( document: DesignDocument, id: NodeId ): ResolvedLayer | undefined

source

The resolved layer an ID names: the root layer for an instance ID, the layer for a virtual ID, undefined for any other stored ID or an ID that does not resolve.

const richTextEditStats: { runTextConcats: number; formattingKeySorts: number }

source

function scopedSelectionTarget( document: DesignDocument, rootId: NodeId, scopeId: NodeId | null, nodeId: NodeId | null ): NodeId | null

source

What a selection rule’s answer becomes on a canvas that selects only within one layer, the scope: the isolation split’s page pane selects the opened instance and the layers inside it (components design 5.5), at any depth of nesting. The rules above answer from the page root, so their answer is brought into the scope here:

  • the scope layer, or a layer inside it, is itself;
  • a container the scope layer is inside (the instance or frame around a nested instance) stands for the scope layer, as a click on a layer of a closed group selects the group: the scope is as far out as it reaches;
  • any other layer is outside the scope, and the answer is null.

With no scope (null) every answer is itself.

function scopedSelectionTargets( document: DesignDocument, rootId: NodeId, scopeId: NodeId | null, nodeIds: readonly NodeId[] ): readonly NodeId[]

source

scopedSelectionTarget for several answers: each layer once, in order, the ones outside the scope left out.

function selectAllVectorVertices(network: VectorNetwork): VectorEditingSelection

source

Selects every anchor of the network, as Cmd+A does in Figma’s vector edit mode.

function setVectorVertexMirroring( network: VectorNetwork, vertexIds: readonly VectorVertexId[], mirroring: VectorMirroring, ids: VectorEditIdAllocator ): VectorNetwork

source

Sets the mirroring of the given points. A mirrored mode snaps at once: the longer handle stays and the other turns to face it (and, mirroring length, takes its length). A point with no handles yet becomes smooth. Points that cannot mirror are skipped.

function smoothVectorVertex( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator, mode: VectorTangentConstraint["mode"] = "angle-and-length" ): VectorNetwork

source

Makes a corner point smooth: its two handles lie along the line between its two neighbours, each a third of the shorter neighbouring segment long, and mirror each other with mode. Only a point joining exactly two segment ends can be smooth; any other is returned unchanged.

function snapPenOffsetToAngle(offset: PenHandleOffset): PenHandleOffset

source

Snaps an offset to the nearest of the eight directions that are multiples of 45 degrees: the result is the point of that direction’s line nearest to the offset, so it stays as close to the pointer as the direction allows. Horizontal and vertical results have an exact zero in the other axis, and diagonal results have equal lengths in both. A zero offset stays zero.

The offset is in the axes the angle is measured in. For the Pen these are the canvas axes (snapPenPointToAngle), whatever the layer’s own rotation.

function snapPenPointToAngle( anchor: Point, point: Point, localToCanvas: TransformMatrix ): Point

source

Moves point so that the line from anchor to it runs at a multiple of 45 degrees in canvas axes. Both points are in the path’s local units; localToCanvas maps those to the canvas (the document or the screen: the two differ only by zoom and scroll, which keep angles). A rotated, flipped or nested layer is therefore snapped as the user sees it, not along its own axes.

The transform is inverted by the rule vector edit mode opens a path with (invertVectorTransform), so every transform the mode accepts snaps here, however small or large its scale. Throws RangeError for a transform that rule refuses, which opens no vector edit mode.

point is returned as it is when the snapped place cannot be stored as numbers: the offset is too large or too small on the canvas to have a direction, or the result leaves the number range. This is the rule a nudge or a typed position follows on such a path (updateFiniteVertices).

function splitCubic(controls: Cubic, t: number): readonly [Cubic, Cubic]

source

Splits a cubic at t with de Casteljau’s construction; both halves trace it exactly.

const standardAccessibilityEditorDriverCodeV1: "const shared = 1;"

source

const standardAccessibilityEditorDriverIdsV1: Readonly< { document: DocumentId; replacementDocument: DocumentId; page: PageId; root: NodeId; text: NodeId; code: NodeId; shape: NodeId; } >

source

Stable IDs used by the shared EditorDriver accessibility acceptance scenario.

const standardAccessibilityEditorDriverTextV1: "Shared accessibility text"

source

const standardAccessibilityScenarioIdsV1: Readonly< { frame: NodeId; text: NodeId; code: NodeId; image: NodeId; shape: NodeId; hidden: NodeId; clipped: NodeId; transparent: NodeId; lockedGroup: NodeId; lockedChild: NodeId; imageAsset: AssetId; } >

source

type StandardEditorScenarioDocument = | "blank" | "text-blank" | "rectangle" | "nested" | "snapping" | "aspect-resize" | "rotation" | "connector" | "group-scale" | "auto-layout" | "hit-order" | "hit-policy" | "rich-text"

source

const standardEditorScenarioIds: { readonly document: DocumentId; readonly page: PageId; readonly root: NodeId; readonly rectangle: NodeId; readonly target: NodeId; readonly container: NodeId; readonly child: NodeId; readonly connector: NodeId; readonly autoLayout: NodeId; readonly autoFirst: NodeId; readonly autoSecond: NodeId; readonly back: NodeId; readonly front: NodeId; readonly locked: NodeId; readonly hidden: NodeId; readonly text: NodeId; }

source

const standardEditorScenarioViewport: ViewportState

source

The fixed-DPR slice supported by the SVG driver.

const standardRichTextFontAssetIds: { readonly latin: AssetId; readonly hebrew: AssetId; readonly japanese: AssetId; readonly korean: AssetId; readonly emoji: AssetId; }

source

const standardRichTextSamples: { readonly emojiFamily: "👨‍👩‍👧‍👦"; readonly combiningAcute: "é"; readonly deadKeyComposed: "é"; readonly hebrew: "אבג"; readonly bidi: "abc אבג"; readonly japanese: "日本語"; readonly korean: "한글"; readonly listMarker: "・"; }

source

type StandardVectorBooleanScenarioDocument = | "editable" | "explicit-primary-fill" | "three-paths" | "non-path" | "locked-secondary" | "disjoint" | "text-editing"

source

const standardVectorBooleanScenarioIds: { readonly primary: NodeId; readonly secondary: NodeId; readonly third: NodeId; readonly nonPath: NodeId; }

source

const standardVectorBooleanScenarioViewport: ViewportState

source

type StandardVectorScenarioDocument = "editable" | "covered"

source

const standardVectorScenarioIds: { readonly pathA: NodeId; readonly pathB: NodeId; readonly cover: NodeId; readonly a0: VectorVertexId; readonly a1: VectorVertexId; readonly a2: VectorVertexId; readonly a3: VectorVertexId; readonly aStartHandle: VectorHandleId; readonly aEndHandle: VectorHandleId; readonly a0a1: VectorSegmentId; readonly a1a2: VectorSegmentId; readonly a2a3: VectorSegmentId; readonly a3a0: VectorSegmentId; readonly aRegion: VectorRegionId; readonly b0: VectorVertexId; readonly b1: VectorVertexId; readonly b2: VectorVertexId; readonly b0b1: VectorSegmentId; readonly b1b2: VectorSegmentId; readonly b2b0: VectorSegmentId; readonly bRegion: VectorRegionId; }

source

const standardVectorScenarioViewport: ViewportState

source

function startCountingVectorWork(): void

source

Starts a count of comparisons at zero. For tests only.

function startVectorSubPath( network: VectorNetwork, position: Point, vertexId: VectorVertexId ): VectorNetwork

source

Starts a new, separate sub-path in the network: one point at position that no segment touches yet. This is what a Pen click on empty canvas does in vector edit mode when no point is armed to draw from. The caller arms the new point, so the next click draws the first segment of the sub-path, which stays part of the same layer.

function stopCountingVectorWork(): number

source

Ends the count a test started and returns it; the checks count nothing again. For tests only.

function storeClosedLoopRegions( before: VectorNetwork, after: VectorNetwork, regionId: () => VectorRegionId ): VectorNetwork

source

Stores regions for the faces (findVectorFaces) an edit has just made: the faces of after that before did not have.

  • A face in space that was open before, or lying apart inside a face that is still there, gets a new region that takes the layer’s fills, so an enclosed area fills as soon as the layer has a fill, as in Figma, and on a layer without fills it paints nothing.
  • Faces that divide a face before had take over that face’s region: the region is replaced, at its place in the order, by one region per new face with the same fills (its own, the layer’s, or none) and winding rule. The first keeps the region’s ID. When the divided face had no region nothing is stored, and Paint offers the new faces.

Faces that were there before the edit with no region (paths saved before closing made regions) are left as they are, and so is every region that is not stored for a single face (several loops, or an open chain closed for filling).

In a part whose segments cross, a loop can fill several areas and lie over other loops, so its name alone does not say that it is new. There, an edit never changes what an area showed before it:

  • A loop gets a new region only when no area it fills could be filled before: none lay in a face before had, and none is in a stored region. A loop that reaches both a new area and one the user already had (a long diagonal across a ladder of cells, a segment across a filled loop) gets none, and neither does a second new loop that lies over the first. Paint offers what is left without a region (findUnstoredVectorFaces).
  • Loops take over a region only when together they fill exactly what the region filled, each area once. Otherwise the region stays on its loop, which still fills what it filled.

function toggleVectorRegionFill( network: VectorNetwork, regionId: VectorRegionId ): VectorNetwork

source

Switches a region between filled and not filled. A filled region (one that takes the layer’s fills, or has its own) becomes fills: []; a region that is not filled goes back to taking the layer’s fills.

function toggleVectorVertexSmooth( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator ): VectorNetwork

source

A Bend click on a point: a point with handles becomes a corner, a corner becomes smooth.

function transformVector(transform: TransformMatrix, vector: Point): Point

source

Maps a direction through the linear part of an affine transform (no translation).

function transformVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[], matrix: TransformMatrix ): VectorNetwork

source

Moves the given points through matrix (network coordinates) and carries the rest of the geometry that lives in the same coordinates:

  • A Bézier handle attached to a moved point is turned and scaled by the matrix’s linear part, so the curve keeps its shape relative to its point. A handle pair that mirrored before still mirrors: a linear map keeps opposite offsets opposite and equal lengths equal.
  • An elliptical arc with both end points moved is mapped exactly (see transformEllipticalArc). With one end point moved it keeps its radii, rotation and flags and is drawn again between its end points, which is what dragging or nudging that point does.
  • A point’s corner radius stays as it is, as it does when the whole layer is resized (scaleVectorNetwork).

Returns the network unchanged for the identity, and when a result would leave the representable number range.

const VECTOR_HIT_TOLERANCE_CSS_PX: 6

source

How far, in CSS pixels, a point, handle or segment of the open path reaches. Every hit test against the open path uses it, so hover and press agree.

const VECTOR_POINT_BOX_HANDLE_REACH: 10

source

How far from a handle’s centre a press still takes it, in box units (CSS pixels). It is more than the 6 pixels a point is grabbed within, so a handle with a point right under it (a corner of the box often is one of the selected points) can still be taken from just beside the point.

const VECTOR_POINT_BOX_ROTATE_REACH: 26

source

How far outside a corner a press still rotates, in box units (CSS pixels).

interface VectorArcConversion

source

The result of turning an elliptical arc into cubic pieces.

readonly network: VectorNetworksource
readonly segmentIds: readonly VectorSegmentId[]source

The pieces in order from the arc’s start to its end; the first keeps the arc’s ID.

interface VectorArea

source

One area a network encloses, with every crossing treated as a corner.

readonly outline: readonly Point[]source

The boundary as a closed polygon in network coordinates, curves flattened.

readonly area: numbersource

type VectorBendGesture = & { readonly threshold: number; readonly moved: boolean; readonly ids: VectorEditIdAllocator; } & ( | { readonly kind: "vertex"; readonly vertexId: VectorVertexId; readonly prepared: VectorPreparedVertex; readonly heldEnd: number | null; } | { readonly kind: "segment"; readonly prepared: VectorPreparedSegmentBend; })

source

A Bend drag from a point or on a segment. Until the pointer has moved threshold (network units, or the gesture’s distanceSpace) it is a click: on a point, that toggles the point between corner and smooth when the press ends.

function vectorBendHeldEnd( prepared: VectorPreparedVertex, vertexId: VectorVertexId, drag: Point, distanceSpace?: TransformMatrix ): number

source

Which segment end a drag from a point holds: the one whose segment leads most nearly the way the pointer went. The other end’s handle mirrors it.

Both directions are compared as distanceSpace draws them (see vectorDistance): pass the path’s local-to-screen matrix so the choice matches what the user sees when the path is stretched unevenly or skewed.

interface VectorCrossing

source

One place where two segments of a network meet away from a point they share.

readonly point: Pointsource

Where, in network coordinates.

readonly first: { readonly segmentId: VectorSegment["id"]; readonly at: number }source

The two segments and how far along each the place is: 0 at the segment’s start, 1 at its end. A segment that crosses itself is named twice.

readonly second: { readonly segmentId: VectorSegment["id"]; readonly at: number; }source

interface VectorCut

source

The cut network and the points left where the cut was made.

readonly network: VectorNetworksource
readonly vertexIds: readonly VectorVertexId[]source

type VectorCutLineCrossing = | { readonly kind: "segment"; readonly segmentId: VectorSegmentId; readonly t: number; } | { readonly kind: "vertex"; readonly vertexId: VectorVertexId }

source

One place the cut line crosses the network.

function vectorCutLineCrossings( network: VectorNetwork, from: Point, to: Point ): readonly VectorCutLineCrossing[]

source

Every place the straight line from from to to (network coordinates) crosses the network: positions inside segments (see VectorSegmentPoint.t), and the points it passes through, each listed once.

interface VectorEditIdAllocator

source

Supplies fresh, unused IDs for the parts an edit creates.

readonly vertexId: () => VectorVertexIdsource
readonly segmentId: () => VectorSegmentIdsource
readonly handleId: () => VectorHandleIdsource

function vectorEditTargetVertexIds( network: VectorNetwork, selection: VectorEditingSelection ): readonly VectorVertexId[]

source

Anchors moved by a nudge or a position edit: selected anchors and the ends of selected segments.

interface VectorEditToolContext

source
readonly tool: "pen" | "bend" | "cut"source
readonly ids: VectorEditIdAllocatorsource
readonly regionId?: () => VectorRegionIdsource

Names a region the Pen stores when a click connects the path in progress to a segment and that encloses or divides an area. Without it such a click only adds the point.

function vectorFillsShow(fills: readonly Paint[] | undefined): boolean

source

Whether a list of fills would put pixels on screen: one of its paints shows (vectorPaintShows). The one test behind every Paint decision: a region’s own fills, the layer’s fills, the cursor and the hover stripes.

function vectorHatchLines( polygons: readonly (readonly Point[])[], windingRule: VectorRegion["windingRule"], spacing: number, maximumLines = 400 ): readonly (readonly [Point, Point])[]

source

Diagonal stripes over the inside of polygons, for marking a hovered region: straight pieces running up to the right, spacing apart, clipped to the area the winding rule fills. In whatever space the polygons are in (pass screen coordinates for stripes that are even on screen). When the area would take more than maximumLines stripes they are spread wider.

type VectorHealIdAllocator = () => VectorHealSegmentIds

source

Supplies fresh, unused IDs for each edge the heal creates.

interface VectorHealSegmentIds

source

Delete and heal for vector networks, after Figma’s “Delete and Heal” (https://www.figma.com/blog/delete-and-heal-for-vector-networks/): removing an anchor joins its neighbours instead of leaving a gap.

  • An anchor with an odd number of edges loses all of them, as a plain delete.
  • An anchor with an even number of edges has them sorted by angle, and each edge is paired with its opposite; every pair becomes one new edge between the two far ends. A pair whose far ends are the same anchor is removed.
  • The new edge’s curve is fitted to the two old edges with fixed end tangents (one pass of Schneider’s least-squares fit), so the shape keeps its course. Elliptical arcs are fitted against their true curve, as cubic pieces.
  • A kept outer handle whose length the fit changed loses the length part of an angle-and-length constraint, as splitting a segment does.
  • Fill loops that ran through the anchor along a healed pair are rewired to the new edge; loops that ran through any other way are removed.
readonly segmentId: VectorSegmentIdsource
readonly startHandleId: VectorHandleIdsource
readonly endHandleId: VectorHandleIdsource

function vectorInteractionPointBox( state: VectorInteractionState ): VectorPointBox | null

source

The box around the selected points, upright on screen, or null when fewer than two points are selected or they share one place.

function vectorLassoContents( network: VectorNetwork, localToScreen: TransformMatrix, outline: readonly Point[] ): VectorLassoContents

source

The points inside a lasso drawn on screen, and the segments whose two end points are both inside. localToScreen maps the network to the space the outline was drawn in.

interface VectorLassoContents

source

The points and segments a lasso outline takes in.

readonly vertexIds: readonly VectorVertexId[]source
readonly segmentIds: readonly VectorSegmentId[]source

type VectorLassoMode = "replace" | "add" | "subtract"

source

How a lasso changes the selection that was there before the drag.

type VectorMirroring = "none" | VectorTangentConstraint["mode"]

source

Figma’s handle mirroring setting for a point (“No mirroring”, “Mirror angle”, “Mirror angle and length”; VectorVertex.handleMirroring in its plugin API). Our model keeps it as a tangent constraint pairing the two handles at the point, so the setting reads and writes that pair.

interface VectorPaintResult

source

What a Paint click changes: the network, and whether the layer itself must be given a fill.

readonly network: VectorNetworksource
readonly layerNeedsFill: booleansource

The click fills a target on a layer that shows no fill. The target takes the layer’s fills, so the caller adds a fill that shows to the layer’s own fills, in the same undo step as the network.

function vectorPaintShows(paint: Paint): boolean

source

Whether one paint would put pixels on screen: it is not hidden, its opacity is above zero, and what it paints is not fully transparent.

  • A solid paint needs a colour with some alpha.
  • A gradient needs at least one stop with some alpha.
  • An image paint counts whenever it is not hidden and has opacity. Its pixels are not looked at, and an asset that is missing is not a paint that shows nothing: the renderer refuses the document (MEDIA_ASSET_REQUIRED).

Blend modes are not looked at: none of them paints nothing by itself; what one shows depends on what lies under it. The layer’s own opacity and visibility are not looked at either, see VectorPaintFillRule.

type VectorPaintTarget = | { readonly kind: "region"; readonly region: VectorRegion } | { readonly kind: "loop"; readonly loop: VectorRegionLoop }

source

What a Paint click acts on: a stored region, or a face (findVectorFaces) that has none yet.

function vectorPaintTargetAtPoint( network: VectorNetwork, point: Point ): VectorPaintTarget | null

source

What a Paint click at point (network coordinates) acts on, or null when the point is in no paintable area. Of several areas holding the point, the smallest is taken, so a loop inside another is reached by pointing inside it; of equal ones, the frontmost.

function vectorPaintTargetKey(target: VectorPaintTarget): string

source

A name for a paint target that lasts while the network keeps its IDs.

function vectorPaintTargets( network: VectorNetwork ): readonly VectorPaintTarget[]

source

Every paint target of a network: its regions from back to front, then its faces without a region.

function vectorPaintTargetShape( target: VectorPaintTarget ): Pick<VectorRegion, "loops" | "windingRule">

source

The loops and winding rule a paint target fills with.

function vectorPaintTargetShowsFill( target: VectorPaintTarget, layerShowsFill: boolean ): boolean

source

Whether a paint target shows a fill on screen now. A region that takes the layer’s fills shows one only when the layer has one; a region with fills of its own when one of them shows (vectorFillsShow); a loop without a region never.

function vectorPointBox( network: VectorNetwork, vertexIds: readonly VectorVertexId[], localToBox: TransformMatrix ): VectorPointBox | null

source

The box around the given points, or null when fewer than two of them exist or they all sit at one place. A box with no width offers only its top and bottom handles, and one with no height only its left and right handles; either still rotates from its two ends.

interface VectorPointBox

source
readonly vertexIds: readonly VectorVertexId[]source

The selected points the box surrounds, in network order.

readonly bounds: Rectsource

The box in box space. Its width or its height may be zero, not both.

readonly resizeHandles: readonly { readonly handle: VectorPointBoxHandle; readonly center: Point; }[]source

The handles on offer: all eight, or the two that scale a box with no width or no height.

readonly rotateCorners: readonly { readonly corner: VectorPointBoxCorner; readonly center: Point; }[]source

The corners with a rotate zone outside them: four, or the two ends of a flat box.

type VectorPointBoxCorner = "nw" | "ne" | "se" | "sw"

source

A corner of the box that points can be rotated from.

interface VectorPointBoxGesture

source

A drag of a handle or rotate zone of the box around the selected points. The box is fixed as the press found it; transform is what the drag applies to it so far, in the box’s own space.

readonly box: VectorPointBoxsource
readonly hit: VectorPointBoxHitsource
readonly localToBox: TransformMatrixsource

From network coordinates to the box’s space (the screen), and back.

readonly boxToLocal: TransformMatrixsource
readonly modifiers: VectorPointTransformModifierssource
readonly transform: TransformMatrixsource

type VectorPointBoxHandle = "nw" | "n" | "ne" | "e" | "se" | "s" | "sw" | "w"

source

A resize handle, named by the compass side of the box it sits on.

type VectorPointBoxHit = | { readonly kind: "resize"; readonly handle: VectorPointBoxHandle } | { readonly kind: "rotate"; readonly corner: VectorPointBoxCorner }

source

function vectorPointBoxHitDirection( box: VectorPointBox, hit: VectorPointBoxHit ): number

source

The direction a handle’s cursor runs along (resize) or faces (rotate), in degrees clockwise from the positive x axis of box space.

function vectorPointBoxLocalTransform( boxTransform: TransformMatrix, localToBox: TransformMatrix, boxToLocal: TransformMatrix ): TransformMatrix

source

The network-space matrix for a box-space one: into the box’s space, through the matrix, and back. An unchanged box gives exactly the identity.

function vectorPointBoxPress( state: VectorInteractionState, screenPoint: Point, part: VectorComponentHit["part"] | null ): VectorPointBoxPress | null

source

What a press at screenPoint takes of the box around the selected points, given the part of the path under the press (part, null for none).

Points and Bézier handles stay grabbable: a press within reach of one takes it, not the box. A resize handle wins over a segment under it. A rotate zone, which lies outside the box, gives way to any part of the path.

interface VectorPointBoxPress

source

A press on the box around the selected points: the box and the part pressed.

readonly box: VectorPointBoxsource
readonly hit: VectorPointBoxHitsource

function vectorPointBoxTransform( box: Pick<VectorPointBox, "bounds">, hit: VectorPointBoxHit, start: Point, current: Point, modifiers: VectorPointTransformModifiers ): TransformMatrix

source

The box-space matrix a drag of hit from start to current applies.

A resize scales about the opposite handle, or about the box’s centre with Alt. With Shift it keeps the proportions: a corner uses the larger of its two factors for both axes, and an edge scales the other axis by the same factor about that axis’s middle. An axis the box has no extent on is never scaled. A rotate turns about the box’s centre by the angle the pointer has swept around it, in 15 degree steps with Shift.

interface VectorPointInsertion

source

Where a point went in: the vertex now at that place.

readonly network: VectorNetworksource
readonly vertexId: VectorVertexIdsource

interface VectorPointTransformModifiers

source

The modifier keys a point box drag reads.

readonly shift: booleansource
readonly alt: booleansource

interface VectorPreparedSegmentBend

source

A segment ready to bend: a cubic, and where on it the pointer took hold.

readonly network: VectorNetworksource
readonly segmentId: VectorSegmentIdsource
readonly t: numbersource

interface VectorPreparedVertex

source

A network whose segments at a vertex are all cubics, ready for handle edits.

readonly network: VectorNetworksource
readonly ends: readonly VectorVertexHandleEnd[]source

function vectorRegionAtPoint( network: VectorNetwork, point: Point ): VectorRegion | null

source

The region whose area holds point (network coordinates): the frontmost one, since regions paint from back to front. Null outside every region.

function vectorRegionIsFilled(region: VectorRegion): boolean

source

Whether a region paints: it takes the layer’s fills, or has fills of its own.

function vectorRegionOutlines( network: VectorNetwork, region: Pick<VectorRegion, "loops"> ): readonly (readonly Point[])[]

source

A region’s loops as closed polygons in network coordinates, with curves flattened. A loop that fills as an open chain closes straight back to its start, as the renderer fills it.

function vectorSegmentCubics( network: VectorNetwork, segment: VectorSegment ): readonly Cubic[]

source

The segment as cubic pieces from its start to its end: one piece for a line or cubic, up to four for an elliptical arc (one quarter turn each).

interface VectorSegmentPoint

source

Where on a segment a point is nearest.

readonly t: numbersource

Position along the segment, 0 at its start and 1 at its end. For an arc it runs evenly over its cubic pieces: piece i of n covers [i / n, (i + 1) / n].

readonly point: Pointsource
readonly distance: numbersource

In network units, or in the units of the space the search measured in.

interface VectorVertexHandleEnd

source

One segment end at a vertex, and where its segment leads.

readonly segmentId: VectorSegmentIdsource
readonly endpoint: "startHandle" | "endHandle"source
readonly handleId: VectorHandleIdsource
readonly far: VectorVertexIdsource

The vertex at the other end of the segment.

function vectorVertexHasHandles( network: VectorNetwork, vertexId: VectorVertexId ): boolean

source

True when any segment end at the vertex has a handle of non-zero length.

function vectorVertexIdsInScreenRect( network: VectorNetwork, localToScreen: TransformMatrix, rect: Rect ): readonly VectorVertexId[]

source

The anchors whose screen position lies inside rect (edges included).

function vectorVertexIsUnconnected( network: VectorNetwork, vertexId: VectorVertexId ): boolean

source

Whether no segment starts or ends at the point. False for a point the network does not have.

function vectorVertexMirroring( network: VectorNetwork, vertexId: VectorVertexId ): VectorMirroring | null

source

A point’s mirroring, or null when it has none to set: only a point joining exactly two segment ends has a pair of handles to mirror.

function vectorVertexParentPositions( network: VectorNetwork, localToParent: TransformMatrix, vertexIds: readonly VectorVertexId[] ): readonly Point[]

source

The parent-space positions of the given anchors, in network order.

function vectorVertexSelection( network: VectorNetwork, vertexIds: Iterable<VectorVertexId>, primary?: VectorVertexId ): VectorEditingSelection

source

A selection of anchors only, in network order, with the last one primary.

@taco-slides/render-core TypeScript

196 of 270 exports documented · outline

@taco-slides/render-core is the TypeScript side of Kimi Design’s renderer. It turns a page of a DesignDocument into a retained scene: a list of nodes, paints and fonts that the renderer keeps between frames. After the first page, each document change becomes a small change batch that updates only the nodes that changed.

The renderer itself is C++ built on Skia and compiled to WebAssembly (Wasm). TypeScript and C++ share two contracts. The first is a little-endian byte format for change batches, written by RenderChangeBatchEncoder and described in PROTOCOL.md. The second is a stable C ABI (the taco_render_* functions in render_core.h), which PortableWasmRenderCoreSession calls. That session copies each input into Wasm memory, calls one C function, checks the result strictly and copies it back, so no Wasm memory escapes to callers.

The editor does not use the session directly. It talks to InteractiveRenderSessionCoordinator, which owns the document, compiles the active page with ActivePageCompilerSession, and drives a retained backend that the web app implements over WebGPU.

Entries marked Native link to the C functions they reach, and C++ entries link back. A Native link on a type or constant means the C side reads or writes the same bytes or values, rather than a call.

Current versions

The byte formats TypeScript and C++ share, with the versions both sides use today, read from the source when the page is built. The build fails if the TypeScript and C++ values differ.

Scene wire24RENDER_CORE_WIRE_VERSION and TACO_RENDER_WIRE_VERSION; see the wire history
Vector network wire5RENDER_CORE_VECTOR_NETWORK_WIRE_VERSION and TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION
Canvas overlay layout4the version encodeCanvasOverlay writes, read by taco_render_session_set_canvas_overlay

Interactive session

InteractiveRenderSessionCoordinator is the renderer as the editor sees it. It keeps one document and one active page, runs changes one at a time, and answers geometry, hit and text queries against the last committed scene. The low-level work goes to an InteractiveRetainedBackend. A text editing overlay (caret, selection and composition) on a node that the renderer draws through another node, such as a code block drawn as a text node, is painted on that other node, so a code block is edited in place like text.

class InteractiveRenderSessionCoordinator implements InteractiveRenderSession

source

Backend-neutral owner of one canonical document and one retained active page. Mutations are serialized; synchronous queries observe only committed state.

The editor talks to this class through the InteractiveRenderSession interface. It compiles the active page with ActivePageCompilerSession, sends the change batches to an InteractiveRetainedBackend, and maps renderer results back to document node IDs.

Mutating methods (loadDocument, applyDocumentUpdate, setActivePage, setViewport, setOverlays, renderFrame) run one at a time in call order. A failed mutation leaves the previous committed state in place. Synchronous queries throw InteractiveRenderSessionBusyError while a mutation is pending; await whenQueryable first. After dispose, queries and mutations throw InteractiveRenderSessionDisposedError.

new (options: InteractiveRenderSessionCoordinatorOptions)source

Creates a coordinator with no document loaded.

async whenQueryable(): Promise<void>source

Waits through queued mutations and any backend-specific synchronous-query barrier.

async exportDocument( document: DesignDocument, pageId: PageId, options: RenderDocumentExportOptions ): Promise<RenderExportResult>source

Exports a page, or one node of it, from a cold compile of document with the resources this coordinator already holds. Pass the renderer document without any editor workspace around it. Video nodes are left out: a throwaway export session has no decoded video frames.

async outlineDocumentStrokes( document: DesignDocument, pageId: PageId, strokes: readonly StrokeOutlineRequest[], budget?: StrokeOutlineBudget, now: () => number = () => performance.now() ): Promise<readonly StrokeOutlineResult[]>source

Outlines strokes of layers on one page of a document: each requested stroke as a vector network of the area it paints, in its node’s local coordinates (see RenderStrokeOutlineSnapshot). The page is compiled apart from the live scene with the resources this coordinator already holds and outlined in a throwaway session, so the result is the stroke the document describes, whatever the canvas is showing. Pass the renderer document without any editor workspace around it. There is one answer per request, in order; a stroke that cannot be outlined is answered with the reason (see StrokeOutlineResult). A budget is shared by every call of one command: the outline that would take the command past its bytes rejects the call with StrokeOutlineBudgetError, a step that would start after its time is used up rejects it with StrokeOutlineTimeError, and no answer of the call is kept.

Compiling the page is the first step, and it runs on the calling thread like the others: with no time left it is not started, and the time it takes (measured with now, in milliseconds) is counted down in the budget. A compile cannot be interrupted, so one that takes longer than the time left runs to its end; the call is then rejected with StrokeOutlineTimeError and the backend is not asked, so no outlining is added to it.

async loadDocument(load: RenderDocumentLoad): Promise<void>source

Replaces the whole document. The active page is kept when the new document still has it; otherwise the scene is emptied until setActivePage picks a page.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.apply: taco_render_session_apply

async applyDocumentUpdate(update: DocumentSceneUpdate): Promise<void>source

Applies one committed document change. The update must start from the document and commit this coordinator holds, or it is rejected with InteractiveRenderTransitionError. The active page is sent as an incremental batch when possible and as a full reload otherwise.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.apply: taco_render_session_apply

async setActivePage(pageId: PageId | null): Promise<void>source

Compiles and shows another page, or an empty scene for null.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.apply: taco_render_session_apply

async setViewport(viewport: ViewportState): Promise<void>source

Passes a new viewport (pan, zoom, canvas size) to the backend.

hitTest(screenPoint: Point): readonly EditorHit[]source

Hits at a screen point: overlay hit regions first, then selectable document nodes, topmost first.

Native, through InteractiveRetainedBackend.hitTest: taco_render_session_hit_test

hitTestIncludingLocked(screenPoint: Point): readonly EditorHit[]source

The layers painted at a screen point, topmost first, with the layers hitTest leaves out because a click cannot select them: a locked layer and the layers inside one. Overlay hit regions are not included. The canvas menu’s “Select layer” lists from these hits, since it is how a locked layer is selected on the canvas.

Native, through InteractiveRetainedBackend.hitTest: taco_render_session_hit_test

getNodeGeometry(nodeId: NodeId): NodeGeometry | nullsource

The committed geometry of one document node, or null when it is not in the scene.

Native, through InteractiveRetainedBackend.getNodeGeometry: taco_render_session_get_node_geometry

getSceneInfo(): RenderSceneInfosource

The backend’s scene size, counts and generation.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

getSceneGeometrySnapshot(): SceneGeometrySnapshotsource

The geometry of every node in the committed scene, back to front. The result is cached until the generation, viewport or document changes.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

Native, through InteractiveRetainedBackend.getGeometrySnapshot: taco_render_session_get_geometry_snapshot

getTextGeometry(nodeId: NodeId): TextGeometry | nullsource

Caret and selection geometry for a text node, from the backend’s text layout.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

hitTestText(queryInput: TextHitQuery): TextHitResult | nullsource

The text position under a point in a text node.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

navigateText(queryInput: TextNavigationQuery): TextNavigationResult | nullsource

Moves a text position by one caret step, such as a word or a line.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

getTextLayoutMetrics(nodeId: NodeId): InteractiveTextLayoutMetrics | nullsource

Line metrics and the suggested auto-size of a text node.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

getFontMetadata(fontResourceId: string): RenderFontMetadataSnapshot | nullsource

The OpenType features and variation axes of one loaded font resource.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

getVectorGeometry(nodeId: NodeId): VectorGeometry | nullsource

The vertices, handles, segments and regions of a vector node.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

hitTestVector(queryInput: VectorHitQuery): VectorHitTestResult | nullsource

The vector vertex, handle, segment or region under a screen point.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

booleanVectorPaths(request: VectorBooleanPathRequest): VectorBooleanPathResultsource

Combines the paths of two nodes with a Boolean operation and checks that the result is a valid, namespaced vector network.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

getDocumentRevision(): | { readonly document: DesignDocument; readonly commitId: CommitId } | nullsource

The canonical document and commit the session holds now, or null before the first document is loaded. Right after a document change the app can be a revision ahead of the session; a caller can compare this with its own revision to wait instead of asking about nodes the session does not have yet.

createAccessibilityBindingV1( overlay: AccessibilityOverlayStateV1 ): InteractiveAccessibilityBindingV1 | nullsource

Creates the v1 semantic mirror from TypeScript-owned canonical state and native-authoritative geometry. No backend accessibility query is used.

resolveAccessibilityActionRouteV1( binding: InteractiveAccessibilityBindingV1, request: AccessibilityActionRequestV1 ): InteractiveAccessibilityActionRouteV1source

Resolves a semantic action to a closed UI command without invoking a callback or mutating canonical, selection, focus, or renderer state.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

async setOverlays(overlays: EditorOverlayState): Promise<void>source

Replaces the editing overlays: selection chrome, canvas overlay, and the text or vector editing overlay. A later scene change that bumps the generation clears them.

Native, through InteractiveRetainedBackend.setOverlays: taco_render_session_set_text_editing_overlay, taco_render_session_clear_text_editing_overlay, taco_render_session_set_vector_editing_overlay, taco_render_session_clear_vector_editing_overlay, taco_render_session_set_selection_chrome, taco_render_session_set_canvas_overlay

async renderFrame(): Promise<EditorRenderFrameReport>source

Paints one frame after every earlier mutation has finished.

Native, through InteractiveRetainedBackend.getSceneInfo: taco_render_session_get_scene_info

async getAccessibilitySnapshot(): Promise<AccessibilityNodeSnapshot | null>source

Asks the backend for its accessibility tree for the current viewport.

dispose(): Promise<void>source

Waits for pending mutations, then releases the backend and all cached state. Calling it again returns the same promise; after a failure it can be retried.

interface InteractiveRenderSessionCoordinatorOptions

source

Options for InteractiveRenderSessionCoordinator.

readonly backend: InteractiveRetainedBackendsource

The retained backend that holds the scene and paints it.

readonly viewport: ViewportStatesource

The starting viewport. The backend receives it before the first overlay update or frame; with the “workspace” policy, only once a page is active.

readonly hitToleranceScreenPx?: numbersource

Geometric body-hit expansion in CSS pixels. Defaults to four.

readonly rendererProjectionPolicy?: "nodes-only" | "workspace"source

Allows the editor to substitute a larger workspace root around the protected page root.

readonly userLanguage?: stringsource

See CompilePageOptions.userLanguage.

readonly resolveInstance?: InstanceResolversource

See CompilePageOptions.resolveInstance; the canvas and export both use it.

interface InteractiveRetainedBackend

source

Low-level retained backend used by the high-level editor coordinator.

apply must be transactional: when it throws or rejects, its committed retained scene and generation remain unchanged. A successful generation change atomically invalidates the backend’s transient overlays. All other asynchronous setters likewise publish only when they fulfill and leave their previous state unchanged when they reject. Geometry and hits use world (canonical document) coordinates. dispose is retry-safe.

The web app implements it with WasmInteractiveRetainedBackend, which keeps the retained scene in a PortableWasmRenderCoreSession and paints with WebGPU. The Native lines below name the C functions that implementation reaches through the session.

readonly capabilities: InteractiveRetainedBackendCapabilitiessource

Which optional methods this backend implements.

apply(batch: Uint8Array): RenderUpdateReport | Promise<RenderUpdateReport>source

Applies one change batch from RenderChangeBatchEncoder to the retained scene and reports the new generation.

Native: taco_render_session_apply

applyWithStaticImageFrames?( batch: Uint8Array, imageAssets: readonly ImageAsset[] ): RenderUpdateReport | Promise<RenderUpdateReport>source

Atomically applies one scene batch and the complete static-image asset set required by the candidate scene. A rejection must publish neither half.

applyWithMediaFrames?( batch: Uint8Array, imageAssets: readonly ImageAsset[], videoAssets: readonly VideoAsset[] ): RenderUpdateReport | Promise<RenderUpdateReport>source

Atomically applies one scene batch and both exact media asset sets.

updatePendingVideoFrames?(): RenderUpdateReport | Promise<RenderUpdateReport>source

Publishes the backend-owned pending video tick without changing scene identity.

getSceneInfo(): RenderSceneInfosource

The scene size, counts, generation and digest.

Native: taco_render_session_get_scene_info

getNodeGeometry(nodeId: string): RenderNodeGeometry | undefinedsource

The bounds and transform of one retained node, or undefined when the scene has no such node.

Native: taco_render_session_get_node_geometry

getGeometrySnapshot(): RenderGeometrySnapshotsource

The geometry of every retained node, back to front.

Native: taco_render_session_get_geometry_snapshot

hitTest( worldX: number, worldY: number, worldTolerance: number ): readonly RenderHit[]source

Topmost-first retained body hits.

Native: taco_render_session_hit_test

whenQueryable?(): Promise<void>source

Resolves when synchronous queries can safely enter the backend runtime.

setViewport(viewport: ViewportState): void | Promise<void>source

Sizes the render target for a new viewport.

setOverlays(overlays: EditorOverlayState): void | Promise<void>source

Replaces the editing overlays painted over the scene: the text or vector editing overlay, the selection chrome, and the canvas overlay (snap lines, guides, measurements and the pixel grid).

Native: taco_render_session_set_text_editing_overlay, taco_render_session_clear_text_editing_overlay, taco_render_session_set_vector_editing_overlay, taco_render_session_clear_vector_editing_overlay, taco_render_session_set_selection_chrome, taco_render_session_set_canvas_overlay

renderFrame(): EditorRenderFrameReport | Promise<EditorRenderFrameReport>source

Paints the committed scene and overlays for the current viewport.

getTextGeometry?(nodeId: NodeId, viewport: ViewportState): TextGeometry | nullsource

Caret, selection and composition geometry for a text node.

Native: taco_render_session_get_text_carets, taco_render_session_get_text_range_rects

hitTestText?(query: TextHitQuery): TextHitResult | nullsource

The text position under a point in a text node.

Native: taco_render_session_hit_test_text

navigateText?(query: TextNavigationQuery): TextNavigationResult | nullsource

Moves a text position by one caret step, such as a word or a line.

Native: taco_render_session_navigate_text

getTextLayoutMetrics?(nodeId: NodeId): RenderTextLayoutMetrics | undefinedsource

Line metrics and the suggested auto-size of a text node.

Native: taco_render_session_get_text_layout_metrics

getFontMetadata?(fontResourceId: string): RenderFontMetadataSnapshot | undefinedsource

The OpenType features and variation axes of one font resource.

Native: taco_render_session_get_font_metadata

getVectorGeometry?( nodeId: NodeId, viewport: ViewportState ): VectorGeometry | nullsource

The vertices, handles, segments and regions of a vector node.

Native: taco_render_session_get_vector_geometry

hitTestVector?( query: VectorHitQuery, viewport: ViewportState ): VectorHitTestResult | nullsource

The vector vertex, handle, segment or region under a screen point.

Native: taco_render_session_hit_test_vector

booleanVectorPaths?(request: VectorBooleanPathRequest): VectorBooleanPathResultsource

Combines two vector paths with a Boolean operation.

Native: taco_render_session_boolean_vector_paths

exportCompiledScene?( request: RenderCompiledExportRequest ): Promise<RenderExportResult>source

Exports a separately compiled page through a throwaway session, so the live scene, its viewport, and its editing overlays stay out of the file.

Native: taco_render_session_create, taco_render_session_apply_with_static_image_frames, taco_render_session_apply, taco_render_session_export

outlineCompiledStrokes?( request: RenderCompiledStrokeOutlineRequest ): Promise<readonly StrokeOutlineResult[]>source

Outlines strokes of a separately compiled page through a throwaway session: one answer per request, in order. A stroke the renderer cannot outline (the page does not draw its node, the node has no such stroke, the geometry does not resolve) is answered with the reason and does not stop the others.

Native: taco_render_session_create, taco_render_session_apply, taco_render_session_outline_stroke

getAccessibilitySnapshot?( viewport: ViewportState ): AccessibilityNodeSnapshot | null | Promise<AccessibilityNodeSnapshot | null>source
dispose(): void | Promise<void>source

Releases the backend. Safe to call again after a failure.

interface InteractiveRetainedBackendCapabilities

source

Which optional parts of InteractiveRetainedBackend a backend implements. The coordinator checks these flags before calling an optional method.

readonly staticImageFrames: booleansource

Atomically commits a retained-scene batch with its exact decoded static-image set.

readonly videoFrames: booleansource

Supports atomic mixed-scene installation and same-generation video ticks.

readonly textGeometry: booleansource
readonly textHitTesting: booleansource
readonly textNavigation: booleansource
readonly textLayoutMetrics: booleansource
readonly fontMetadata: booleansource
readonly vectorGeometry: booleansource
readonly vectorHitTesting: booleansource
readonly vectorBooleanOperations: booleansource
readonly accessibility: booleansource

type InteractiveRenderAdvancedCapability = | "text-geometry" | "text-hit-testing" | "text-navigation" | "text-layout-metrics" | "font-metadata" | "vector-geometry" | "vector-hit-testing" | "vector-boolean-operations" | "accessibility"

source

An optional query or feature a retained backend may lack. The coordinator throws InteractiveRenderCapabilityError with this name when the editor calls a method the backend does not support.

interface InteractiveTextLayoutMetrics extends RenderTextLayoutMetrics

source

Text layout metrics for one text node, named by its document node ID.

readonly nodeId: NodeIdsource

type VectorBooleanOperation = | "union" | "intersection" | "difference" | "exclusion"

source

A Boolean operation on two vector paths.

interface VectorBooleanPathRequest

source

Combines the paths of two nodes. Every vertex, segment and region ID in the result starts with resultIdNamespace followed by a colon.

readonly primaryNodeId: NodeIdsource
readonly secondaryNodeId: NodeIdsource
readonly operation: VectorBooleanOperationsource
readonly resultIdNamespace: stringsource

interface VectorBooleanPathResult

source

The vector network a Boolean operation produced, for one scene generation.

readonly generation: bigintsource
readonly primaryNodeId: NodeIdsource
readonly secondaryNodeId: NodeIdsource
readonly operation: VectorBooleanOperationsource
readonly vectorNetwork: VectorNetworksource

interface StrokeOutlineRequest

source

One stroke to outline: the node, the stroke’s index in the node’s strokes (VisualStyle.strokes, or 0 for a connector’s one stroke), and the prefix of every ID in the result.

readonly nodeId: NodeIdsource
readonly strokeIndex: numbersource
readonly resultIdNamespace: stringsource

type StrokeOutlineResult = | { readonly outlined: true; readonly vectorNetwork: VectorNetwork } | { readonly outlined: false; readonly reason: string }

source

The answer to one StrokeOutlineRequest: the stroke as a vector network of the area it paints, in its node’s local coordinates (a network without regions when the stroke paints nothing), or the reason the renderer could not outline it.

interface RenderCompiledStrokeOutlineRequest

source

A cold page batch and the strokes to outline in it.

readonly batch: Uint8Arraysource
readonly strokes: readonly StrokeOutlineRequest[]source
readonly budget?: StrokeOutlineBudgetsource

What the command these strokes belong to may still keep, and how long the renderer may still work on it. Every outline is charged to it as it arrives; the one that does not fit is not decoded and fails the whole request with StrokeOutlineBudgetError. Every step is timed, and none is started once the time is used up: the request then fails with StrokeOutlineTimeError.

class InteractiveRenderCapabilityError extends Error

source

Structured failure for an advanced editor surface the backend does not implement.

readonly capability: InteractiveRenderAdvancedCapabilitysource
new (capability: InteractiveRenderAdvancedCapability)source

class InteractiveRenderTransitionError extends Error

source

A rejected document-store transition; no renderer or compiler state is advanced.

readonly code: InteractiveRenderTransitionErrorCodesource
readonly expectedCommitId: CommitId | nullsource
readonly actualCommitId: CommitId | nullsource
new ( code: InteractiveRenderTransitionErrorCode, message: string, commitIds: { readonly expected: CommitId | null; readonly actual: CommitId | null; } = { expected: null, actual: null } )source

type InteractiveRenderTransitionErrorCode = | "DOCUMENT_NOT_LOADED" | "PREVIOUS_DOCUMENT_IDENTITY_MISMATCH" | "PREVIOUS_RENDERER_DOCUMENT_IDENTITY_MISMATCH" | "PREVIOUS_RENDERER_NODE_OWNERS_IDENTITY_MISMATCH" | "PREVIOUS_RENDERER_QUERY_TARGETS_IDENTITY_MISMATCH" | "INVALID_RENDERER_DOCUMENT_PROJECTION" | "INVALID_RENDERER_NODE_OWNERS" | "INVALID_RENDERER_QUERY_TARGETS" | "RENDERER_CHANGES_REQUIRED" | "INVALID_RENDERER_CHANGES" | "PREVIOUS_COMMIT_MISMATCH"

source

Why InteractiveRenderSessionCoordinator.applyDocumentUpdate (or another document change) was rejected. Most codes mean the update was built against a different document, renderer document, or commit than the one the coordinator holds.

class InteractiveRenderSessionDisposedError extends Error

source

A deterministic use-after-dispose failure.

new ()source

class InteractiveRenderSessionBusyError extends Error

source

Synchronous scene queries are unavailable while a queued mutation is unsettled.

new ()source

class InteractiveRenderQueryBusyError extends Error

source

A synchronous backend query attempted while its native runtime is yielding.

readonly stage: stringsource
readonly operation: string | nullsource
new (stage: string, operation: string | null = null)source

class InteractiveRenderResourceError extends Error

source

A document asset the renderer needs is missing or unusable: a media node names a missing or non-media asset, or a stored resource does not match its asset (for example a different MIME type). assetId names the asset.

readonly assetId: stringsource
new (assetId: string, message: string)source

class InteractiveRenderGeometryError extends Error

source

A backend query returned geometry the coordinator cannot trust: a result for another node, a stale scene generation, or a value the editor cannot represent. nodeId names the queried node when there is one.

readonly nodeId: string | nullsource
new (message: string, nodeId: string | null = null)source

Accessibility routing

The coordinator builds an accessibility snapshot of the active page and turns a screen reader action into a UI command. A binding ties the snapshot to the exact document, commit and scene generation it came from, so a stale action is rejected. Right after a document change the app can be a commit ahead of the coordinator; getDocumentRevision returns the document and commit the coordinator holds, so the app can wait for it instead of asking about a node the coordinator does not have yet.

interface InteractiveAccessibilityBindingV1

source

An immutable semantic snapshot bound to the coordinator’s exact committed canonical and retained identities. Only its issuing coordinator can route it.

readonly version: typeof ACCESSIBILITY_SNAPSHOT_VERSIONsource
readonly document: DesignDocumentsource
readonly commitId: CommitIdsource
readonly pageId: PageIdsource
readonly viewport: ViewportStatesource
readonly generation: bigintsource
readonly snapshot: AccessibilitySnapshotV1source

interface InteractiveAccessibilityActionRouteV1

source

Pure UI intent returned only after exact context and action authority checks.

readonly version: typeof ACCESSIBILITY_SNAPSHOT_VERSIONsource
readonly binding: InteractiveAccessibilityBindingV1source
readonly authority: AccessibilityActionAuthorityV1source
readonly command: InteractiveAccessibilityActionCommandV1source

type InteractiveAccessibilityActionCommandV1 = | { readonly kind: "focus-mirror"; readonly nodeId: NodeId; readonly focusOwner: "accessibility-mirror"; } | { readonly kind: "set-node-selection"; readonly nodeId: NodeId; readonly mode: "replace" | "toggle"; readonly focusOwner: "accessibility-mirror"; } | { readonly kind: "begin-text-edit"; readonly nodeId: NodeId; readonly focusOwner: "text-input"; } | { readonly kind: "begin-code-edit"; readonly nodeId: NodeId; readonly focusOwner: "text-input"; }

source

The UI command an accessibility action resolves to.

class InteractiveAccessibilityContextError extends Error

source

A semantic action whose coordinator-owned scene identity is no longer current.

new (readonly code: InteractiveAccessibilityContextErrorCode, message: string)source

type InteractiveAccessibilityContextErrorCode = | "INVALID_BINDING" | "DOCUMENT_IDENTITY_MISMATCH" | "COMMIT_MISMATCH" | "ACTIVE_PAGE_MISMATCH" | "VIEWPORT_IDENTITY_MISMATCH" | "GENERATION_MISMATCH" | "CANONICAL_NODE_MISSING" | "EDIT_TARGET_MISMATCH"

source

Why InteractiveAccessibilityContextError was thrown.

Scene queries and results

What a RenderCoreSession reports about its retained scene. Positions are in world (document) coordinates unless a name says otherwise, and most results carry the scene generation they belong to.

interface RenderSceneInfo

source

The retained scene’s size in document units, its node and resource counts, and the generation and digest that identify its current contents.

Native: TacoRenderSceneInfo

readonly width: numbersource
readonly height: numbersource
readonly nodeCount: numbersource
readonly resourceCount: numbersource
readonly generation: bigintsource
readonly sceneDigest: bigintsource

interface RenderUpdateReport

source

The result of applying one batch: counts, the new scene generation and digest, and the area that changed.

Native: TacoRenderUpdateReport

readonly commandsApplied: numbersource
readonly nodeCount: numbersource
readonly resourceCount: numbersource
readonly generation: bigintsource
readonly sceneDigest: bigintsource
readonly dirtyBounds: RenderDirtyBoundssource

interface RenderDirtyBounds

source

The world-space area the last batch changed: the old and new bounds of the changed nodes and their union.

Native: TacoRenderDirtyBounds

readonly oldBounds?: RenderRectsource

Dirty rectangles are scene/world-space bounds.

readonly newBounds?: RenderRectsource
readonly combinedBounds?: RenderRectsource

interface RenderRect

source
readonly x: numbersource
readonly y: numbersource
readonly width: numbersource
readonly height: numbersource

interface RenderNodeGeometry

source

The bounds, transform, paint order and visibility of one retained node.

Native: TacoRenderNodeGeometry, TacoRenderGeometryRecord

readonly id: stringsource
readonly kind: RetainedNodeRecord["kind"]source
readonly localBounds?: RenderRectsource
readonly worldBounds?: RenderRectsource
readonly visibleWorldBounds?: RenderRectsource

World-space bounds after visibility and ancestor clips are applied.

readonly subtreeVisibleBounds?: RenderRectsource

The union of visibleWorldBounds over this node and all its descendants, with children of a clipping frame clipped to it: everything the subtree paints. Only single-node geometry queries report it.

readonly worldTransform: Transformsource
readonly painterOrder: bigintsource
readonly effectivelyVisible: booleansource
readonly effectiveOpacity: numbersource

interface RenderGeometrySnapshot

source

One immutable, back-to-front geometry view for a committed scene generation.

readonly generation: bigintsource
readonly nodes: readonly RenderNodeGeometry[]source

interface RenderHit

source

One node under a hit-test point.

Native: TacoRenderHit

readonly id: stringsource
readonly kind: RetainedNodeRecord["kind"]source
readonly painterOrder: bigintsource
readonly visibleWorldBounds: RenderRectsource

interface ViewportMappingOptions

source

How to fit the scene into a viewport, for RenderCoreSession.mapViewport.

Native: TacoRenderViewportOptions

readonly viewportWidth: numbersource
readonly viewportHeight: numbersource
readonly mode: "fit" | "fill" | "stretch"source
readonly alignX?: numbersource

Zero aligns to the leading edge and one to the trailing edge.

readonly alignY?: numbersource

interface ViewportMapping

source

The transforms between scene and viewport coordinates, and the scene’s bounds inside the viewport. For a scene of size W×HW \times H and a viewport of size Vw×VhV_w \times V_h, the scales start as sx=Vw/Ws_x = V_w / W and sy=Vh/Hs_y = V_h / H; fit sets both to min⁡(sx,sy)\min(s_x, s_y), fill to max⁡(sx,sy)\max(s_x, s_y), and stretch keeps them. With alignment (αx,αy)(\alpha_x, \alpha_y) (default 0.5):

ox=(Vw−sxW) αx,oy=(Vh−syH) αyo_x = (V_w - s_x W)\,\alpha_x, \qquad o_y = (V_h - s_y H)\,\alpha_y
sceneToViewport=[sx,0,0,sy,ox,oy],viewportToScene=[1sx,0,0,1sy,−oxsx,−oysy]\text{sceneToViewport} = [s_x, 0, 0, s_y, o_x, o_y], \qquad \text{viewportToScene} = \Bigl[\tfrac{1}{s_x}, 0, 0, \tfrac{1}{s_y}, -\tfrac{o_x}{s_x}, -\tfrac{o_y}{s_y}\Bigr]

and sceneViewportBounds is (ox,oy,sxW,syH)(o_x, o_y, s_x W, s_y H).

Native: TacoRenderViewportMapping

readonly sceneToViewport: Transformsource
readonly viewportToScene: Transformsource
readonly sceneViewportBounds: RenderRectsource

interface RgbaRenderTarget

source
readonly kind: "rgba"source
readonly width: numbersource
readonly height: numbersource
readonly rowBytes: numbersource
readonly pixels: Uint8Arraysource

interface RenderFrameReport

source
readonly backend: "skia-cpu" | "skia-webgpu"source
readonly width: numbersource
readonly height: numbersource

interface RenderTextPosition

source

A paragraph-relative authored text position. Offsets count UTF-16 code units.

readonly paragraphIndex: numbersource
readonly utf16Offset: numbersource
readonly affinity: RenderTextAffinitysource

type RenderTextAffinity = "upstream" | "downstream"

source

Which side of a line break or bidirectional boundary a caret belongs to.

Native: TacoRenderTextAffinity

interface RenderTextRange

source

Anchor and focus preserve the authored selection direction.

readonly anchor: RenderTextPositionsource
readonly focus: RenderTextPositionsource

interface RenderTextCaretGeometry

source
readonly position: RenderTextPositionsource
readonly localBounds: RenderRectsource

interface RenderTextCaretSnapshot

source
readonly generation: bigintsource
readonly carets: readonly RenderTextCaretGeometry[]source

interface RenderTextRangeGeometry

source
readonly generation: bigintsource
readonly localRects: readonly RenderRect[]source

interface RenderTextHitResult

source
readonly generation: bigintsource
readonly position: RenderTextPositionsource
readonly inside: booleansource

type RenderTextNavigationOperation = | "grapheme-backward" | "grapheme-forward" | "word-backward" | "word-forward" | "visual-left" | "visual-right" | "line-up" | "line-down" | "line-start" | "line-end" | "paragraph-start" | "paragraph-end" | "document-start" | "document-end"

source

One caret movement for RenderCoreSession.navigateText. The C codes follow this order, from 0.

Native: TacoRenderTextNavigationOperation

interface RenderTextNavigationResult

source
readonly generation: bigintsource
readonly position: RenderTextPositionsource
readonly preferredLocalX?: numbersource

interface RenderTextLayoutMetrics

source
readonly generation: bigintsource
readonly autoResize: RetainedTextNode["autoResize"]source
readonly frameWidth: numbersource
readonly frameHeight: numbersource
readonly contentWidth: numbersource
readonly contentHeight: numbersource
readonly suggestedWidth: numbersource
readonly suggestedHeight: numbersource
readonly paragraphCount: numbersource
readonly lineCount: numbersource
readonly caretCount: numbersource

interface RenderFontMetadataSnapshot

source
readonly fontResourceId: stringsource
readonly generation: bigintsource
readonly featureTags: readonly string[]source
readonly axes: readonly RenderFontAxisMetadata[]source

interface RenderFontAxisMetadata

source
readonly tag: stringsource
readonly hidden: booleansource
readonly minimum: numbersource
readonly defaultValue: numbersource
readonly maximum: numbersource

interface RenderVectorGeometrySnapshot

source
readonly generation: bigintsource
readonly vertices: readonly RenderVectorVertexGeometry[]source
readonly handles: readonly RenderVectorCubicHandleGeometry[]source
readonly segments: readonly RenderVectorSegmentGeometry[]source
readonly regions: readonly RenderVectorRegionGeometry[]source

interface RenderVectorVertexGeometry

source
readonly kind: "vertex"source
readonly id: stringsource
readonly localPoint: { readonly x: number; readonly y: number }source
readonly screenPoint: { readonly x: number; readonly y: number }source

interface RenderVectorCubicHandleGeometry

source
readonly kind: "cubic-handle"source
readonly id: stringsource
readonly segmentId: stringsource
readonly vertexId: stringsource
readonly endpoint: "start" | "end"source
readonly localPoint: { readonly x: number; readonly y: number }source
readonly screenPoint: { readonly x: number; readonly y: number }source

type RenderVectorSegmentGeometry = | RenderVectorLineSegmentGeometry | RenderVectorCubicSegmentGeometry | RenderVectorArcSegmentGeometry

source

interface RenderVectorLineSegmentGeometry extends RenderVectorSegmentGeometryBase

source
readonly kind: "line"source

interface RenderVectorCubicSegmentGeometry extends RenderVectorSegmentGeometryBase

source
readonly kind: "cubic"source
readonly startHandleId: stringsource
readonly endHandleId: stringsource

interface RenderVectorArcSegmentGeometry extends RenderVectorSegmentGeometryBase

source
readonly kind: "elliptical-arc"source
readonly radiusX: numbersource
readonly radiusY: numbersource
readonly xAxisRotation: numbersource
readonly largeArc: booleansource
readonly sweep: booleansource

interface RenderVectorRegionGeometry

source
readonly kind: "region"source
readonly id: stringsource
readonly windingRule: "nonzero" | "evenodd"source
readonly authoredOrder: numbersource
readonly localBounds: RenderRectsource
readonly screenBounds: RenderRectsource

interface RenderVectorHitQuery

source
readonly screenPoint: { readonly x: number; readonly y: number }source
readonly toleranceCssPx: numbersource
readonly documentToScreen: Transformsource

interface RenderVectorHitSnapshot

source
readonly generation: bigintsource
readonly hits: readonly RenderVectorHit[]source

type RenderVectorHit = & { readonly id: string; readonly priority: number; readonly authoredOrder: number; readonly distanceCssPx: number; } & ( | { readonly kind: "vertex" } | { readonly kind: "cubic-handle"; readonly endpoint: "start" | "end" } | { readonly kind: "segment" } | { readonly kind: "region" })

source

type RenderVectorBooleanOperation = | "union" | "intersection" | "difference" | "exclusion"

source

A Boolean operation on two vector paths. The C codes are union 1, intersection 2, difference 3, exclusion 4.

Native: TacoRenderVectorBooleanOperation

interface RenderVectorBooleanPathRequest

source
readonly primaryNodeId: stringsource
readonly secondaryNodeId: stringsource
readonly operation: RenderVectorBooleanOperationsource
readonly resultIdNamespace: stringsource

interface RenderVectorBooleanPathSnapshot

source
readonly generation: bigintsource
readonly vectorNetwork: VectorNetworkRecordsource

interface RenderStrokeOutlineRequest

source

Asks for one stroke of a path or container node as the filled shape it paints. strokeIndex counts the node’s strokes in paint order, hidden ones included, which is the stroke’s index in the model’s VisualStyle.strokes. Every vertex, segment, handle and region ID of the result starts with resultIdNamespace followed by a colon.

Native: TacoRenderStrokeOutlineQuery

readonly nodeId: stringsource
readonly strokeIndex: numbersource
readonly resultIdNamespace: stringsource

interface RenderStrokeOutlineSnapshot

source

The outline of one stroke for one scene generation, in the node’s local coordinates: closed loops in even-odd regions without fills. A ring is one region with a hole, and every dash is a region of its own. A stroke that paints nothing gives a network without components.

Native: TacoRenderStrokeOutlineResult

readonly generation: bigintsource
readonly vectorNetwork: VectorNetworkRecordsource

Overlays

Editing overlays are painted by the renderer in the same frame as the scene: selection outlines and handles, snap lines and guides, and the text or vector editing state. They are not part of the scene, never export, and the next scene change clears the editing overlays.

interface RenderSelectionChrome

source

The selection outlines and transform handles the renderer paints over the scene. RenderCoreSession.setSelectionChrome encodes it into the byte layout that SessionCache.setSelectionChrome reads.

Native: taco_render_session_set_selection_chrome, SessionCache.setSelectionChrome

readonly items: readonly RenderSelectionChromeItem[]source
readonly strokeRgba: numbersource

Default colours of items that give none, 0xRRGGBBAA.

readonly handleFillRgba: numbersource
readonly devicePixelRatio: numbersource

interface RenderSelectionChromeItem

source

One selection outline or transform handle, resolved against a node at paint time. A handle arrives finished: the editor hit-tests handles, so it alone decides their size and turn, and the renderer only places the centre and paints the rectangle it is given.

readonly nodeId: string | nullsource

Null places x and y (and an outline’s width and height) in scene coordinates.

readonly kind: "outline" | "square-handle" | "round-handle"source
readonly hover?: booleansource
readonly dashed?: booleansource
readonly x: numbersource

An outline’s node-local (or scene) rectangle. For a handle, x and y are its centre, and width and height its size on screen in CSS pixels, width along the turned x direction.

readonly y: numbersource
readonly width?: numbersource
readonly height?: numbersource
readonly angle?: numbersource

A handle’s turn on screen in radians, clockwise (screen y points down). 0 when omitted.

readonly strokeRgba?: numbersource

0xRRGGBBAA; the chrome’s strokeRgba when omitted.

readonly fillRgba?: numbersource

A handle’s fill, 0xRRGGBBAA; the chrome’s handleFillRgba when omitted.

readonly offsetX?: numbersource

Scene-space offset for a preview the retained scene does not hold.

readonly offsetY?: numbersource
readonly sceneTransform?: Transformsource

Local-to-scene matrix of a scene-anchored item (nodeId null), so a preview of a rotated node keeps a rotated outline. Identity when omitted.

readonly nodeTransform?: Transformsource

Item-to-node matrix of a node-anchored item (wire version 21): the item maps through it, then through the node’s transform. A multi-selection’s box is anchored to one selected node this way. Identity when omitted.

function encodeSelectionChrome(chrome: RenderSelectionChrome): Uint8Array

source

Encodes the selection chrome in the layout taco_render_session_set_selection_chrome reads (version 3, wire version 20; see render_core.h). Each item carries its own colours, and each handle the size and angle the editor hit-tests it with, so the renderer paints exactly the handle the editor tests against.

Native: taco_render_session_set_selection_chrome, SessionCache.setSelectionChrome

interface RenderCanvasOverlay

source

Editor lines the renderer paints over the scene in the same frame: snap and alignment lines with their x marks, ruler guides, option-drag outlines and measurements, and the pixel grid. They are never document content and never change the retained scene.

readonly anchorNodeId: string | nullsource

The node whose local coordinates the overlay uses (the page root), so the lines follow a pan in the same frame as the page. Null means scene coordinates throughout.

readonly devicePixelRatio: numbersource
readonly grid: RenderOverlayGrid | nullsource
readonly lines: readonly RenderOverlayLine[]source
readonly marks: readonly RenderOverlayMark[]source
readonly titles?: readonly RenderOverlayTitle[]source

Names drawn above top-level frames. Absent means none.

readonly rulers?: RenderOverlayRulerssource

The rulers along the top and left edges. Absent means none.

readonly boxes?: readonly RenderOverlayBox[]source

Filled, hatched or outlined boxes, such as auto layout gaps. Absent means none.

readonly handles?: readonly RenderOverlayHandle[]source

Short ticks with a border, such as auto layout spacing handles. Absent means none.

readonly badges?: readonly RenderOverlayBadge[]source

Small value pills, such as the value of a spacing drag. Absent means none.

function encodeCanvasOverlay(overlay: RenderCanvasOverlay): Uint8Array

source

Encodes the canvas overlay in the layout taco_render_session_set_canvas_overlay reads (version 4; see render_core.h). An empty overlay encodes as no bytes. SessionCache.setCanvasOverlay decodes it.

Native: taco_render_session_set_canvas_overlay, SessionCache.setCanvasOverlay

interface RenderOverlayLine

source

One straight overlay line, drawn over whole device pixels.

readonly orientation: "vertical" | "horizontal"source

A vertical line has a constant x; a horizontal one a constant y.

readonly side: RenderOverlayLineSidesource
readonly position: numbersource

x of a vertical line, y of a horizontal one.

readonly start: number | nullsource

Where the line starts and ends along its length; null crosses the whole canvas.

readonly end: number | nullsource
readonly rgba: numbersource

0xRRGGBBAA.

readonly dash?: { readonly dashCssPx: number; readonly gapCssPx: number }source

Dash and gap lengths in CSS pixels; omitted for a solid line.

readonly scene?: booleansource

Scene coordinates instead of coordinates local to the overlay’s anchor.

readonly anchorNodeId?: stringsource

A node whose local coordinates this line uses instead of the overlay’s anchor, such as a frame whose own guides must follow it through a move or resize preview.

readonly spansAnchorBox?: booleansource

Run along the anchor node’s own box (0 to its width or height); start and end are ignored.

type RenderOverlayLineSide = "after" | "before" | "centered"

source

Which device pixels a one-CSS-pixel line covers around its position: “after” for a left or top edge and for coordinate lines (grid lines, ruler guides), “before” for a right or bottom edge, “centered” for a center line. These are the pixels the selection outline covers for an edge there.

interface RenderOverlayMark

source

An anti-aliased x centered where two overlay lines cross.

readonly x: numbersource
readonly y: numbersource
readonly xSide: RenderOverlayLineSidesource

The sides of the crossing lines: the x is centered on their pixels.

readonly ySide: RenderOverlayLineSidesource
readonly halfSizeCssPx: numbersource
readonly rgba: numbersource
readonly scene?: booleansource

interface RenderOverlayGrid

source

A line after every step units from the origin, across the whole canvas.

readonly originX: numbersource
readonly originY: numbersource
readonly step: numbersource
readonly rgba: numbersource

The canvas overlay also carries the canvas chrome the renderer draws in the same frame: boxes, handles and badges (the auto layout padding, gap and spacing handles), the rulers, and the names above top-level frames. Their text uses the font given to taco_render_session_set_canvas_overlay_font.

interface RenderOverlayBox

source

A rectangle in its anchor’s local coordinates, mapped through the anchor’s whole world transform as it is in the frame being drawn (so a rotated frame’s boxes turn with it). Each of the three looks is optional: a solid fill, diagonal stripes one CSS pixel wide every 4 CSS pixels (hatching), and a one-CSS-pixel outline inside the box.

readonly anchorNodeId?: stringsource

Omitted uses the overlay’s anchor.

readonly x: numbersource
readonly y: numbersource
readonly width: numbersource
readonly height: numbersource
readonly fillRgba?: numbersource

0xRRGGBBAA; omitted draws no fill, hatching or outline.

readonly hatchRgba?: numbersource
readonly outlineRgba?: numbersource

interface RenderOverlayHandle

source

A short tick with round ends and a border, centred on a point in its anchor’s local coordinates. It runs along the anchor’s local x or y axis and keeps its CSS pixel size at every zoom.

readonly anchorNodeId?: stringsource
readonly x: numbersource
readonly y: numbersource
readonly axis: "x" | "y"source

“x” runs along the anchor’s local x axis, “y” along its y axis.

readonly lengthCssPx: numbersource
readonly thicknessCssPx: numbersource
readonly rgba: numbersource

0xRRGGBBAA.

readonly borderRgba: numbersource

interface RenderOverlayBadge

source

A rounded pill with one line of text, its top-left corner a CSS pixel offset from a point in its anchor’s local coordinates. Drawn with the font given to setCanvasOverlayFont; with no font it is left out.

readonly anchorNodeId?: stringsource
readonly x: numbersource
readonly y: numbersource
readonly offsetXCssPx: numbersource
readonly offsetYCssPx: numbersource
readonly fontSizeCssPx: numbersource
readonly text: stringsource
readonly backgroundRgba: numbersource

0xRRGGBBAA.

readonly textRgba: numbersource

interface RenderOverlayRulers

source

The rulers. The renderer lays out their ticks and numbers from the overlay anchor (the page root) as it is in the frame being drawn, so they move with the page during a pan, a zoom or an auto-pan step. Numbers use the font given to setCanvasOverlayFont. Uses the overlay’s anchor.

readonly sizeCssPx: numbersource

The rulers’ thickness.

readonly fontSizeCssPx: numbersource
readonly minimumMajorCssPx: numbersource

Major ticks are a 1-2-5 step at least this far apart.

readonly backgroundRgba: numbersource

0xRRGGBBAA colours.

readonly borderRgba: numbersource
readonly inkRgba: numbersource
readonly selectionFillRgba: numbersource
readonly selectionInkRgba: numbersource
readonly selectionX: readonly RenderOverlayRulerSpan[]source

Sorted extents that do not touch; a zero-length one marks a single value.

readonly selectionY: readonly RenderOverlayRulerSpan[]source
readonly labels: readonly RenderOverlayRulerLabel[]source

At most 16.

interface RenderOverlayRulerSpan

source

An extent on a ruler, in the overlay anchor’s units.

readonly start: numbersource
readonly end: numbersource

interface RenderOverlayRulerLabel

source

A guide’s position, drawn on its ruler as a rounded tag.

readonly axis: "x" | "y"source

“x” is a value on the top ruler, “y” one on the left ruler.

readonly position: numbersource

In the overlay anchor’s units.

readonly backgroundRgba: numbersource

0xRRGGBBAA.

readonly textRgba: numbersource

interface RenderOverlayTitle

source

The name drawn above a top-level frame. The renderer places it on the top-left corner of the frame’s screen bounding box as the frame is in the frame being drawn, so it follows a move, resize, pan or zoom with no lag, and cuts it to the frame’s width with an ellipsis. It is drawn with the font given to setCanvasOverlayFont.

readonly anchorNodeId: stringsource
readonly text: stringsource
readonly rgba: numbersource

0xRRGGBBAA.

readonly fontSizeCssPx: numbersource

The font size, which is also the height of the title’s line box.

readonly gapCssPx: numbersource

Space between the bottom of the line box and the frame’s top edge.

interface RenderTextEditingOverlay

source

One generation-stamped transient selection, caret, and optional IME range.

readonly selection: RenderTextRangesource
readonly composition?: RenderTextRangesource
readonly caretVisible: booleansource

interface RenderVectorEditingOverlay

source
readonly network: VectorNetworkRecordsource
readonly selection: RenderVectorEditingSelectionsource
readonly pendingHandle?: RenderVectorPendingHandle | nullsource

The Pen’s pending handle, when there is one.

readonly viewport: { readonly documentToScreen: Transform; readonly screenWidthCssPx: number; readonly screenHeightCssPx: number; readonly screenPixelDensity: number; }source

interface RenderVectorEditingSelection

source

The selected vector components by kind, and the primary one (or null).

readonly vertexIds: readonly string[]source
readonly handleIds: readonly string[]source
readonly segmentIds: readonly string[]source
readonly regionIds: readonly string[]source
readonly primary: RenderVectorEditingComponent | nullsource

type RenderVectorEditingComponent = | { readonly kind: "vertex"; readonly id: string } | { readonly kind: "handle"; readonly id: string } | { readonly kind: "segment"; readonly id: string } | { readonly kind: "region"; readonly id: string }

source

One vertex, handle, segment or region of a vector network, by ID.

Export

Export paints a page or one layer into PNG, SVG, PDF or raw RGBA bytes. The coordinator compiles the page afresh and exports it through a throwaway session, so the live scene and its overlays stay out of the file.

interface RenderDocumentExportOptions

source

What InteractiveRenderSessionCoordinator.exportDocument exports and how.

readonly format: RenderExportFormatsource
readonly scale?: numbersource

Output units per document unit. Defaults to 1.

readonly rootNodeId?: stringsource

Exports this node’s visible bounds and subtree; omitted means the page.

readonly options?: RenderExportOptionssource

interface RenderExportOptions

source

Export choices beyond the format, scale and root.

readonly fixedSize?: { readonly dimension: "width" | "height"; readonly value: number; }source

A fixed output width or height in output units, replacing scale: the scale becomes this size divided by the exported bounds’ width or height.

readonly includeOverlappingLayers?: booleansource

With a root, paint every visible node inside the root’s bounds, not just the root’s subtree, so overlapping layers are included.

readonly imageSampling?: RenderExportImageSamplingsource
readonly svgNodeMarkers?: booleansource
readonly pdfRasterDpi?: numbersource
readonly maxPixels?: numbersource

The most pixels a raster export (rgba or png) may have. An export over it is refused with RenderExportTooLargeError once its size is measured and before anything is allocated or drawn. Left out: only the renderer’s own limit of 16384 pixels a side applies.

interface RenderCompiledExportRequest

source

A cold page batch plus what a fresh session needs to paint and export it.

readonly batch: Uint8Arraysource
readonly imageAssets: readonly ImageAsset[]source

Every image asset the batch references, for decoding into frames.

readonly format: RenderExportFormatsource
readonly scale: numbersource

Output units per document unit.

readonly rootNodeId?: stringsource

Exports this node’s visible bounds and subtree; omitted means the page.

readonly options?: RenderExportOptionssource

Other options for the export itself; see RenderExportOptions.

interface RenderExportRequest

source

One rectangular scene region painted through the shared painter. Editing overlays never export and the background stays transparent.

Native: TacoRenderExportRequest

readonly format: RenderExportFormatsource
readonly x: numbersource

The region in scene coordinates.

readonly y: numbersource
readonly width: numbersource
readonly height: numbersource
readonly scale?: numbersource

Output units per scene unit: raster pixels, SVG user units, PDF points. Defaults to 1.

readonly rootNodeId?: stringsource

When set, only this node and its descendants paint.

readonly imageSampling?: RenderExportImageSamplingsource

How image fills and media frames are resampled; the screen’s “linear” by default.

readonly svgNodeMarkers?: booleansource

SVG only: surround each painted node’s elements with node markers. See PROTOCOL.md.

readonly pdfRasterDpi?: numbersource

PDF only: the DPI at which blurs and shadows are rasterized; 72 by default.

interface RenderExportResult

source

The bytes of one export and their size.

Native: TacoRenderExportResult

readonly format: RenderExportFormatsource
readonly bytes: Uint8Arraysource

RGBA rows are tightly packed and unpremultiplied; other formats are whole files.

readonly pixelWidth: numbersource

ceil(region * scale); raster formats use it as their pixel size.

readonly pixelHeight: numbersource
readonly rowBytes: numbersource

Bytes per RGBA row; zero for every other format.

type RenderExportFormat = "rgba" | "png" | "svg" | "pdf"

source

Scene export targets. PNG needs a build with a PNG encoder; RGBA never does.

Native: TacoRenderExportFormat

type RenderExportImageSampling = "linear" | "cubic" | "nearest"

source

How export resamples images: “linear” (the screen’s sampling), “cubic”, or “nearest”.

Native: TacoRenderExportImageSampling

Page compiler

The page compiler turns one page of a document into retained records and encodes them as a change batch. compilePageBatch builds a whole page; ActivePageCompilerSession keeps the last page and encodes only what a change set touched. Model data the renderer cannot draw fails with RenderDocumentCompileError instead of disappearing.

function compilePageBatch( document: DesignDocument, pageId: PageId, options: CompilePageOptions ): CompiledPageBatch

source

Compiles one canonical page to a complete change batch that resets the scene to the page size, registers its fonts, and adds every node. Unsupported node and paint semantics fail with a structured RenderDocumentCompileError rather than disappearing.

function compileIncrementalPageBatch( previous: DesignDocument, current: DesignDocument, changes: ChangeSet, pageId: PageId, options: CompilePageOptions ): CompiledPageUpdate

source

Compiles a canonical document transition without resetting the retained scene. The caller must have applied compilePageBatch(previous, pageId) or the preceding successful result in the same transition chain.

The model change set is a conservative invalidation signal. For correctness, the compiler also compares the complete retained representation of the two selected page subtrees, so an incomplete change set cannot make an update disappear silently.

class ActivePageCompilerSession

source

Stateful compiler for the currently displayed page.

Canonical documents are immutable and validated model change sets identify every affected entity. Production updates inspect those IDs plus any structurally shifted siblings; the explicit cold-compilation audit performs the deliberately O(document) verification pass. Updates are transactional: the cache advances only after the complete batch has encoded successfully.

new (options: CompilePageOptions)source

Creates an empty session; call reset before update.

get initialized(): booleansource

Whether reset has compiled a page.

reset(document: DesignDocument, pageId: PageId): CompiledActivePageBatchsource

Compiles and caches a complete active page, including the reset command.

update( document: DesignDocument, documentChanges: ChangeSet ): CompiledActivePageUpdatesource

Compiles a validated transition using its authoritative changed-entity index. Unchanged siblings are neither traversed nor converted.

auditAgainstColdCompilation( document: | DesignDocument | undefined = this.#active?.document ): ActivePageAuditReportsource

Performs the deliberately O(document) correctness audit outside update().

clear(): voidsource

Drops the cached page; the next call must be reset.

interface CompilePageOptions

source

Where the page compiler gets asset bytes and language preferences.

readonly getAssetBytes: (asset: Asset) => Uint8Array | undefinedsource

Supplies bytes for assets registered as wire resources. Scene-only media metadata never resolves bytes; loading decoded frames belongs to a later platform adapter.

readonly getFontSlices?: (asset: FontAsset) => | readonly CompileFontSlice[] | undefinedsource

Supplies the files loaded so far for a font asset whose source is “google-fonts”, in priority order. When the asset is a style’s primary font, the first file is always registered so the text has line metrics; every other file is registered only when it covers a character of the text node. Files that are still loading are simply absent: the host shows the loading state and compiles again once they arrive.

readonly userLanguage?: stringsource

The user’s language as a BCP 47 tag, such as the browser’s navigator.language. It chooses among the CJK automatic fallback families for text whose style names no language; see preferredCjkFamily.

readonly resolveInstance?: InstanceResolversource

Derives an instance’s layers (design 4.1). Defaults to resolveInstance from @taco-slides/model. Each resolved layer compiles as a stored node would, under its virtual ID; the instance compiles as a frame whose paints come from the component root.

interface CompileFontSlice

source

One loaded file of a font asset that is split by Unicode coverage, such as a Google Fonts family served as many small WOFF2 files.

readonly id: stringsource

Unique within its asset. It must not contain “#”.

readonly unicodeRanges: readonly (readonly [number, number])[]source

Sorted, non-overlapping inclusive code point ranges the file covers.

readonly bytes: Uint8Arraysource

interface CompiledPageBatch

source

A complete page as one change batch that resets the retained scene.

readonly batch: Uint8Arraysource

The encoded batch, ready for RenderCoreSession.apply.

readonly nodeIds: readonly NodeId[]source

Every compiled node, in traversal order.

readonly mediaAssetIds: readonly string[]source

Image and video assets the scene uses; the host attaches their frames.

readonly resourceIds: readonly string[]source

Wire resource IDs registered in the batch (fonts and font files).

type CompiledPageUpdate = CompiledIncrementalPageBatch | PageFullReloadRequired

source

The result of compileIncrementalPageBatch.

interface CompiledIncrementalPageBatch

source

A page change encoded as a batch that updates the retained scene in place.

readonly kind: "incremental"source
readonly batch: Uint8Arraysource

A batch with no reset command, including for an unchanged page.

readonly operations: PageIncrementalOperationssource
readonly nodeIds: readonly NodeId[]source
readonly mediaAssetIds: readonly string[]source
readonly resourceIds: readonly string[]source

interface PageIncrementalOperations

source

The commands an incremental batch contains, by node and resource ID.

readonly removeNodeIds: readonly NodeId[]source

Top-level previous subtrees removed by wire commands.

readonly upsertNodeIds: readonly NodeId[]source

Current nodes inserted or replaced in canonical traversal order.

readonly transformNodeIds?: readonly NodeId[]source

Current nodes whose retained payload is unchanged except for transform.

readonly registerResourceIds: readonly string[]source
readonly removeResourceIds: readonly string[]source

interface PageFullReloadRequired

source

The change cannot be sent incrementally; compile the whole page again.

readonly kind: "full-reload-required"source
readonly reasons: readonly PageFullReloadReason[]source

interface PageFullReloadReason

source

One reason a page needs a full reload, with a readable message.

readonly code: PageFullReloadReasonCodesource
readonly message: stringsource

type PageFullReloadReasonCode = | "PREVIOUS_PAGE_NOT_FOUND" | "CURRENT_PAGE_NOT_FOUND" | "PAGE_ROOT_RESIZED" | "PAGE_ROOT_CHANGED" | "THEME_CHANGED" | "PAGE_VARIABLE_MODES_CHANGED"

source

Why an incremental update was refused and the page must be compiled again.

interface CompiledActivePageBatch extends CompiledPageBatch

source

A full page batch from ActivePageCompilerSession.reset, with work counters.

readonly metrics: ActivePageCompileMetricssource

interface CompiledActivePageIncrementalBatch extends CompiledIncrementalPageBatch

source

An incremental batch from ActivePageCompilerSession.update, with work counters.

readonly metrics: ActivePageCompileMetricssource

type CompiledActivePageUpdate = | CompiledActivePageIncrementalBatch | PageFullReloadRequired

source

The result of ActivePageCompilerSession.update.

interface ActivePageCompileMetrics

source

Deterministic work counters reported by the stateful active-page compiler.

readonly nodesInspected: numbersource

Canonical nodes selected by the validated change set and hierarchy index.

readonly nodesCompiled: numbersource

Selected nodes converted from the canonical model.

readonly nodesReused: numbersource

Reserved for accepted cached records selected without recompilation.

readonly nodesUpserted: numbersource

Node upsert commands encoded into this batch.

readonly resourcesResolved: numbersource

Platform asset resolutions performed during this compilation.

readonly resourcesRegistered: numbersource

Resource payloads encoded into this batch.

readonly resourcesRemoved: numbersource
readonly batchBytes: numbersource

interface ActivePageAuditReport

source

The result of ActivePageCompilerSession.auditAgainstColdCompilation: whether the cached records match a fresh compile, and which ones do not.

readonly matchesColdCompilation: booleansource
readonly nodesInspected: numbersource
readonly mismatchedNodeIds: readonly NodeId[]source
readonly mismatchedResourceIds: readonly string[]source

class RenderDocumentCompileError extends Error

source

A deliberate boundary error; unsupported model data must never disappear.

readonly code: RenderDocumentCompileErrorCodesource
readonly nodeId: NodeId | undefinedsource
new (code: RenderDocumentCompileErrorCode, message: string, nodeId?: NodeId)source

type RenderDocumentCompileErrorCode = | "PAGE_NOT_FOUND" | "PAGE_ROOT_WITHOUT_SIZE" | "NODE_NOT_FOUND" | "UNSUPPORTED_NODE" | "UNSUPPORTED_STYLE" | "INVALID_MEDIA_CROP" | "MEDIA_ASSET_REQUIRED" | "FONT_ASSET_REQUIRED" | "FONT_BYTES_REQUIRED" | "RESOURCE_LIMIT" | "INVALID_COLOR" | "INSTANCE_UNRESOLVED"

source

function compileRetainedPaint( paint: Paint, nodeId: NodeId, label: string, document?: DesignDocument ): RetainedPaintRecord

source

Lowers one canonical paint to its retained record. An image paint must name an image asset of document, because the host attaches its pixels through the media frame contract by asset ID.

function parseRetainedColor(value: string, nodeId: NodeId): RgbaColor

source

Parses the deterministic CSS-color subset accepted by the first shared wire.

function fontSliceResourceId(assetId: string, sliceId: string): string

source

The wire resource ID of one file of a font asset split by Unicode coverage.

function fontResourceAsset( document: DesignDocument, resourceId: string ): Asset | undefined

source

The document asset a wire font resource was compiled from. A resource ID is either an asset ID or fontSliceResourceId(assetId, sliceId) of a “google-fonts” asset or of an automatic fallback asset (see automatic-font-fallback.ts); the compiler rejects documents where the forms collide.

function textNodeCodePoints(node: TextualNode): Set<number>

source

Every code point a text node can draw: its runs, explicit list markers, and the characters of generated markers (“\u2022” and “1.”).

const RETAINED_IDENTITY_TRANSFORM: Transform

source

Connectors

The page compiler draws a connector from its stored ends: an attached end goes on its layer’s edge where it faces the other end, spread apart from other connectors on the same side, and an elbow route goes around the other layers under the same parent. The editor asks for the same line, so a connector’s frame and end handles stay on what is drawn.

function connectorLineGeometry( document: DesignDocument, node: Extract<SceneNode, { type: "connector" }>, parentId: NodeId | undefined ): ConnectorLineGeometry

source

The line node draws, routed exactly as the page compiler draws it: an attached end is placed on its layer’s edge where it faces the other end (spread apart from other automatic ends on the same side), and an elbow route goes around the other layers under the same parent. The editor uses it to keep a connector’s frame and end handles on the drawn line. parentId is the connector’s parent. Throws the compiler’s RenderDocumentCompileError when an attachment cannot be drawn.

interface ConnectorLineGeometry

source

The line a connector draws, in the connector’s own coordinates.

readonly start: { readonly x: number; readonly y: number }source

Where the line starts, after an attached start is placed on its layer’s edge.

readonly end: { readonly x: number; readonly y: number }source

Where the line ends (an arrowhead’s tip), after an attached end is placed.

readonly bounds: { readonly x: number; readonly y: number; readonly width: number; readonly height: number; }source

The box around the whole line, bends and curves included, without stroke or end caps.

function connectorRoutePoints( node: Extract< SceneNode, { type: "connector"; } >, curveSteps = 24, document?: DesignDocument ): readonly { readonly x: number; readonly y: number }[]

source

The route node draws, as points along it in the connector’s own coordinates, from start to end: the corners of a bent route, and each curve split into curveSteps straight pieces. The line is not shortened for arrowheads. The editor draws a selected or hovered connector’s outline along it.

With document, an attached connector is routed as the page compiler draws it in that document (around the other layers, apart from other connectors), so pass the document the drawn scene shows. Without it, or for a connector with no attached end, the route runs between the stored ends (the line’s real ends once the editor has fitted the connector).

Retained scene records

The records the page compiler produces and the encoder writes, one per node. They are plain data close to the wire format: colors are RGBA numbers, IDs are strings, and every paint, stroke and text run is spelled out.

type RetainedNodeRecord = | RetainedPathNode | RetainedTextNode | RetainedContainerNode | RetainedMediaNode

source

interface RetainedNodeBase

source
readonly id: stringsource
readonly parentId?: stringsource
readonly zOrder: numbersource
readonly visible: booleansource
readonly blendMode?: RetainedBlendModesource

Wire version 10 composites a node with a non-normal blend mode through one isolated layer. Omitted means normal.

readonly mask?: booleansource

Wire version 10 lets a node mask its later siblings: they are kept only where the mask’s own painting has alpha, and the mask paints nothing itself. A hidden mask masks nothing.

readonly transform: Transformsource
readonly style: RetainedNodeStylesource

interface RetainedNodeStyle

source
readonly opacity: numbersource
readonly fill?: RgbaColorsource

The compact common-node record carries fill. Wire version 5 can instead append fills to a node upsert for an exact ordered paint list. The two representations are mutually exclusive.

readonly fills?: readonly RetainedPaintRecord[]source
readonly strokes?: readonly RetainedStrokeRecord[]source

Ordered strokes painted after the fills, first entry at the bottom. The first stroke uses the compact common-node record; wire version 7 appends the rest to the appearance tail.

readonly shadows?: readonly RetainedShadowRecord[]source

Wire version 6 appends ordered shadows; wire version 9 adds inner ones.

readonly layerBlur?: numbersource

Wire version 9 appends a Gaussian blur radius applied to the node and its descendants as one isolated layer. Zero or omitted means no blur.

readonly backgroundBlur?: numbersource

Wire version 9 appends a Gaussian blur radius applied to the content behind the node’s shape before its fills paint. Zero or omitted means none.

interface RetainedContainerNode extends RetainedNodeBase

source

A renderer-neutral hierarchy and clipping node introduced by wire version 2.

readonly kind: "container"source
readonly width: numbersource
readonly height: numbersource
readonly clip: booleansource
readonly cornerRadius?: numbersource

Wire version 11 appends the corner radius and the corner smoothing fraction when the radius is positive, flagged in the container’s first reserved byte. A positive radius rounds the container’s own paint, its inner shadows, its background blur, and its child clip; the radius clamps to half the shorter side. Zero or omitted means square corners.

readonly cornerRadii?: readonly [number, number, number, number]source

Wire version 14 flags (bit 2) four independent radii in the order top left, top right, bottom right, bottom left, followed by the smoothing. Each clamps to half the shorter side. Mutually exclusive with a nonzero cornerRadius; the compiler only sends it when the radii differ.

readonly cornerSmoothing?: numbersource

Corner smoothing from 0 (circular arcs) to 1 (continuous curvature). Each smoothed corner consumes up to (1 + smoothing) * radius of edge, capped at half the shorter side. Zero or omitted keeps circular arcs.

interface RetainedPathNode extends RetainedNodeBase

source
readonly kind: "path"source
readonly vectorNetwork: VectorNetworkRecordsource
readonly vertexCornerRadii?: readonly RetainedVertexCornerRadius[]source

Wire version 12 flags the node (bit 2) and appends this table after the network record. Only vertices joining exactly two line or cubic segments round at paint time; the authored vertices stay the editing and query geometry. Empty or omitted means sharp corners.

readonly strokeCaps?: RetainedStrokeCapssource

Wire version 13 flags the node (bit 4) and appends both caps after any vertex corner table. The start cap sits at the start vertex of the first segment and the end cap at the end vertex of the last one; both must be open chain ends. Omitted keeps round caps at every stroke end. Lines and connectors both send their end styles here; since wire version 22 heads point the way the line arrives across a triangle head’s length, and dashes get round ends while heads stay solid.

interface RetainedTextNode extends RetainedNodeBase

source
readonly kind: "text"source
readonly width: numbersource
readonly height: numbersource
readonly wrap: "none" | "word" | "character"source
readonly verticalAlign: "top" | "middle" | "bottom"source
readonly overflow: "clip" | "visible"source
readonly autoResize: "none" | "height" | "width-and-height"source
readonly styles: readonly RichTextStyleRecord[]source
readonly paragraphs: readonly RichTextParagraphRecord[]source

interface RetainedMediaNode extends RetainedNodeBase

source

Scene-only media metadata introduced by wire version 4.

readonly kind: "media"source
readonly mediaKind: "image" | "video"source
readonly fit: "contain" | "cover" | "fill"source
readonly width: numbersource
readonly height: numbersource
readonly assetId: stringsource
readonly crop?: { readonly x: number; readonly y: number; readonly width: number; readonly height: number; }source

type Transform = readonly [ a: number, b: number, c: number, d: number, tx: number, ty: number ]

source

An affine matrix in the same [a, b, c, d, tx, ty] layout as the model’s TransformMatrix. It maps a point as (x′,y′)=(ax+cy+tx, bx+dy+ty)(x', y') = (a x + c y + t_x,\ b x + d y + t_y).

type RgbaColor = readonly [ red: number, green: number, blue: number, alpha: number ]

source

type RetainedBlendMode = | "normal" | "darken" | "multiply" | "color-burn" | "lighten" | "screen" | "color-dodge" | "overlay" | "soft-light" | "hard-light" | "difference" | "exclusion" | "hue" | "saturation" | "color" | "luminosity"

source

Wire version 10 blend modes, in CSS mix-blend-mode terms. The wire codes (0 to 15 in this order) are the C++ BlendMode values.

Native: BlendMode

type RetainedPaintRecord = | RetainedSolidPaintRecord | RetainedLinearGradientPaintRecord | RetainedRadialGradientPaintRecord | RetainedAngularGradientPaintRecord | RetainedImagePaintRecord

source

One fill or stroke paint. The wire kind codes (solid 1, linear 2, radial 3, angular 4, image 5) are the C++ PaintKind values.

Native: PaintKind, RetainedPaint

interface RetainedPaintCompositing

source

Wire version 19 carries two per-paint fields in the paint header’s reserved bytes: byte 1 is the blend mode code (the node blend mode codes, 0 for normal) and byte 2 holds flags, where bit 1 hides the paint. Both are omitted at their defaults.

readonly hidden?: truesource

The renderer skips a hidden paint but keeps its place in the list.

readonly blendMode?: RetainedBlendModesource

Blends the paint with the paints below it on the same node; omitted for normal. The node’s paints are drawn into their own layer when any visible paint blends, so the blend never reaches content behind the node.

interface RetainedSolidPaintRecord extends RetainedPaintCompositing

source
readonly kind: "solid"source
readonly color: RgbaColorsource
readonly opacity: numbersource

interface RetainedLinearGradientPaintRecord extends RetainedPaintCompositing

source
readonly kind: "linear-gradient"source
readonly angle: numbersource
readonly stops: readonly { readonly offset: number; readonly color: RgbaColor; }[]source
readonly opacity: numbersource

interface RetainedRadialGradientPaintRecord extends RetainedPaintCompositing

source

Wire version 8 adds radial and angular gradients keyed to the shape bounds.

readonly kind: "radial-gradient"source
readonly centerX: numbersource

Center as fractions of the shape bounds.

readonly centerY: numbersource
readonly radiusX: numbersource

Ellipse radii as fractions of the shape bounds; both positive.

readonly radiusY: numbersource
readonly stops: readonly { readonly offset: number; readonly color: RgbaColor; }[]source
readonly opacity: numbersource

interface RetainedAngularGradientPaintRecord extends RetainedPaintCompositing

source
readonly kind: "angular-gradient"source
readonly centerX: numbersource
readonly centerY: numbersource
readonly angle: numbersource

Start angle in degrees, clockwise from the positive x axis.

readonly stops: readonly { readonly offset: number; readonly color: RgbaColor; }[]source
readonly opacity: numbersource

interface RetainedImagePaintRecord extends RetainedPaintCompositing

source

Wire version 8 adds image paints. The pixels arrive through the same media frame contract image nodes use, keyed by the logical asset ID.

readonly kind: "image"source
readonly assetId: stringsource
readonly fit: RetainedImagePaintFitsource
readonly opacity: numbersource

type RetainedImagePaintFit = "contain" | "cover" | "fill"

source

How an image paint fits its shape. The wire codes (0, 1, 2 in this order) are the C++ MediaFit values.

Native: MediaFit

interface RetainedStrokeRecord

source
readonly color: RgbaColorsource
readonly width: numbersource
readonly position?: RetainedStrokePositionsource

Wire version 6 appends the stroke position. Omitting it, or naming center, keeps the compact wire-v5 encoding.

readonly dash?: readonly number[]source

Wire version 6 appends dash intervals. An odd-length pattern repeats to an even cycle, matching SVG stroke-dasharray.

readonly paint?: RetainedPaintRecordsource

Wire version 6 can append a full stroke paint. When omitted, color remains the authoritative solid stroke paint. Every stroke after the first always carries a paint because only the first has a compact color.

readonly sides?: readonly [number, number, number, number]source

Wire version 18 gives a container stroke its own weight per side, in the order top, right, bottom, left, flagged in the stroke’s first reserved byte and appended after the dash intervals. width should then hold the largest weight, which bounds and hit tests use. Only containers accept it.

readonly join?: RetainedStrokeJoinsource

Wire version 23 sets the stroke’s join, the ends of its dashes and its miter limit together, flagged in the stroke flags byte (bit 2) and appended after any side weights. Give all three or none. Without them a path strokes with round joins and round dash ends and a container with miter joins (limit 4) and flat dash ends, as before wire version 23. On a path with stroke caps, dashCap sets only the dash ends; the path’s own ends take its caps. Side weights ignore the join.

readonly dashCap?: RetainedDashCapsource

Wire version 23: the ends of every dash; see join.

readonly miterLimit?: numbersource

Wire version 23: the longest a miter join may reach, as a multiple of half the stroke width, before it is beveled (SVG’s stroke-miterlimit); at least 1. Only miter joins use it; see join.

type RetainedStrokePosition = "center" | "inside" | "outside"

source

Retained stroke placement relative to the shape boundary. The wire codes (0, 1, 2 in this order) are the C++ StrokePosition values.

Native: StrokePosition

type RetainedStrokeCap = | "none" | "round" | "square" | "line-arrow" | "triangle-arrow" | "reversed-triangle" | "circle-arrow" | "diamond-arrow"

source

Wire version 13 stroke end caps, in Figma’s terms. The wire codes (0 to 7 in this order) are the C++ StrokeCap values.

Native: StrokeCap

interface RetainedStrokeCaps

source
readonly start: RetainedStrokeCapsource
readonly end: RetainedStrokeCapsource

type RetainedStrokeJoin = "miter" | "bevel" | "round"

source

Wire version 23 stroke joins. The wire codes (0 to 2 in this order) are the C++ StrokeJoin values.

Native: StrokeJoin

type RetainedDashCap = "none" | "round" | "square"

source

Wire version 23 dash ends. The wire codes (0 to 2 in this order) are the C++ DashCap values.

Native: DashCap

interface RetainedShadowRecord

source
readonly inner?: booleansource

Wire version 9 prefixes every shadow with its kind. An inner shadow paints inside the shape over its fills; a drop shadow (the default) paints behind the node.

readonly x: numbersource
readonly y: numbersource
readonly blur: numbersource
readonly spread: numbersource
readonly color: RgbaColorsource

interface RetainedVertexCornerRadius

source

Wire version 12: one rounded vertex of a path, by stable vertex ID.

readonly vertexId: stringsource
readonly radius: numbersource

Positive; the renderer clamps it to half of the shorter adjacent segment.

interface VectorNetworkRecord

source
readonly vertices: readonly VectorVertexRecord[]source
readonly segments: readonly VectorSegmentRecord[]source
readonly constraints?: readonly VectorTangentConstraintRecord[]source
readonly regions: readonly VectorRegionRecord[]source

interface VectorVertexRecord

source
readonly id: stringsource
readonly x: numbersource
readonly y: numbersource

interface VectorHandleRecord

source
readonly id: stringsource
readonly dx: numbersource
readonly dy: numbersource

type VectorSegmentRecord = | VectorLineSegmentRecord | VectorCubicSegmentRecord | VectorEllipticalArcSegmentRecord

source

interface VectorLineSegmentRecord

source
readonly id: stringsource
readonly kind: "line"source
readonly start: stringsource
readonly end: stringsource

interface VectorCubicSegmentRecord

source
readonly id: stringsource
readonly kind: "cubic"source
readonly start: stringsource
readonly end: stringsource
readonly startHandle: VectorHandleRecordsource
readonly endHandle: VectorHandleRecordsource

interface VectorEllipticalArcSegmentRecord

source

Exact compatibility geometry retained for migration and SVG interchange.

readonly id: stringsource
readonly kind: "elliptical-arc"source
readonly start: stringsource
readonly end: stringsource
readonly radiusX: numbersource
readonly radiusY: numbersource
readonly xAxisRotation: numbersource
readonly largeArc: booleansource
readonly sweep: booleansource

interface VectorRegionRecord

source
readonly id: stringsource
readonly windingRule: "nonzero" | "evenodd"source
readonly loops: readonly VectorRegionLoopRecord[]source
readonly fills?: readonly RetainedPaintRecord[]source

Omitted inherits the path fill; an empty list explicitly disables this region’s fill.

interface VectorRegionLoopRecord

source
readonly segments: readonly VectorSegmentReferenceRecord[]source
readonly implicitClose?: truesource

interface VectorSegmentReferenceRecord

source
readonly segmentId: stringsource
readonly direction: "forward" | "reverse"source

interface VectorTangentConstraintRecord

source
readonly vertexId: stringsource
readonly handleIds: readonly [string, string]source
readonly mode: "angle" | "angle-and-length"source

interface RichTextStyleRecord

source
readonly fontResourceIds: readonly string[]source

Primary font first, followed by deterministic shaping fallbacks.

readonly language?: stringsource
readonly fontSize: numbersource
readonly lineHeight: numbersource

Absolute renderer-space line height; styles may use different values.

readonly letterSpacing: numbersource
readonly weight: numbersource
readonly italic?: booleansource
readonly underline?: booleansource
readonly strikethrough?: booleansource
readonly color: RgbaColorsource
readonly openTypeFeatures?: Readonly<Record<string, boolean | number>>source
readonly fontVariations?: Readonly<Record<string, number>>source

interface RichTextParagraphRecord

source
readonly direction: "auto" | "ltr" | "rtl"source
readonly align: "left" | "center" | "right" | "justify"source
readonly spacingBefore: numbersource
readonly spacingAfter: numbersource
readonly indent?: { readonly left: number; readonly right: number; readonly firstLine: number; }source
readonly list?: { readonly kind: "ordered" | "unordered"; readonly level: number; readonly start?: number; readonly marker?: { readonly text?: string; readonly styleIndex?: number; readonly gap?: number; }; }source
readonly runs: readonly RichTextRunRecord[]source

Empty paragraphs and empty style-carrying runs are preserved.

interface RichTextRunRecord

source
readonly text: stringsource

Runs carry their own text so UTF-8 byte boundaries are encoded without UTF-16 ambiguity.

readonly styleIndex: numbersource

type ResourceKind = "font" | "image" | "blob"

source

The kind of a registered resource. The wire codes (font 1, image 2, blob 3) are the C++ ResourceKind values.

Native: ResourceKind

Change batches and wire protocol

A change batch is a header followed by commands such as “upsert path” or “remove node”. The C++ session applies a batch completely or not at all. Standalone vector network payloads use a separate, frozen version for overlays and Boolean results.

class RenderChangeBatchEncoder

source

Builds one retained-scene change batch: a 20-byte header (magic TCR1, RENDER_CORE_WIRE_VERSION, sizes and command count) followed by commands. Each method appends one command and returns the encoder, so calls chain; finish returns the bytes. Every value is checked against WIRE_LIMITS and the node rules as it is added, so a bad node throws here rather than in the renderer. See PROTOCOL.md for the byte layout.

The renderer applies a batch with RenderCoreSession.apply, which calls taco_render_session_apply. RetainedSession.apply reads it with a WireReader and applies all commands or none.

Native: taco_render_session_apply, RetainedSession.apply, WireReader

resetScene(width: number, height: number, clearResources = true): thissource

Removes every node and sets the scene size in document units. With clearResources (the default) it also removes every resource.

resizeScene(width: number, height: number): thissource

Changes the scene size and keeps every node and resource. The editor’s scene is its viewport in document units, so a zoom resizes it; a reset would discard the compiled scene and every paint cache for nothing.

registerResource(id: string, kind: ResourceKind, bytes: Uint8Array): thissource

Adds or replaces a resource, such as a font file, under id.

removeResource(id: string): thissource

Removes one resource.

upsertPath(node: RetainedPathNode): thissource

Adds or replaces a path node: its vector network, style, corner radii and caps.

upsertText(node: RetainedTextNode): thissource

Adds or replaces a text node with its styles and paragraphs.

upsertContainer(node: RetainedContainerNode): thissource

Adds or replaces a frame or group: size, clipping and corner radius.

upsertMedia(node: RetainedMediaNode): thissource

Adds or replaces an image or video node. Its pixels arrive as media frames.

removeNode(id: string): thissource

Removes a node and all of its descendants.

setTransform(id: string, transform: RetainedNodeBase["transform"]): thissource

Changes only the transform of an existing node.

finish(): Uint8Arraysource

Writes the batch header and returns the complete batch bytes.

const WIRE_LIMITS: Readonly< { batchBytes: number; commands: 100000; identifierBytes: 1024; textBytes: number; nodes: 100000; resources: 4096; resourceBytes: number; batchResourceBytes: number; retainedPayloadBytes: number; vectorVertices: 1000000; vectorSegments: 1000000; vectorConstraints: 1000000; vectorRegions: 100000; vectorLoops: 1000000; vectorReferences: 3000000; regionPaints: 100000; nodePaints: 100000; gradientStops: 1000000; textStyles: 65536; textParagraphs: 65536; textRuns: 65536; fontFallbacks: 4096; fontFallbackReferences: 65536; openTypeSettings: 256; strokeDashIntervals: 64; shadows: 16; nodeStrokes: 16; } >

source

Size and count limits for one change batch and for the whole retained scene. The encoder throws a RangeError before it writes past a limit, and the C++ reader rejects any batch that exceeds the same limits (the kMaximum* constants in session.h). retainedPayloadBytes bounds the whole scene: nodes, identifiers, vector and text data, and resources.

const RENDER_CORE_WIRE_VERSION: 24

source

The change batch version RenderChangeBatchEncoder writes.

Native: TACO_RENDER_WIRE_VERSION

const RENDER_CHANGE_COMMAND_HEADER_BYTES: 8

source

The size of each command header: u8 opcode, three reserved bytes, u32 payload length.

const RENDER_CORE_VECTOR_NETWORK_WIRE_VERSION: 5

source

Frozen wire version for headerless standalone VectorNetwork payloads.

Native: TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION

function encodeVectorNetworkRecord(network: VectorNetworkRecord): Uint8Array

source

Encodes one standalone VectorNetwork payload for transient native overlays and Boolean results. Its frozen version is RENDER_CORE_VECTOR_NETWORK_WIRE_VERSION, independent of the current batch version. It deliberately has no batch or command header and uses the same validation, bounds, canonical ordering, and paint representation as a retained path command. The C++ side reads it with decodeVectorNetwork.

Native: decodeVectorNetwork

function decodeVectorNetworkRecord(bytes: Uint8Array): VectorNetworkRecord

source

Decodes exactly one canonical standalone VectorNetwork, such as a Boolean result the C++ side wrote with encodeVectorNetwork. The payload must round-trip byte-for-byte through the shared encoder, so Boolean query results cannot drift from retained path and vector-overlay wire semantics.

Native: encodeVectorNetwork

Native, through encodeVectorNetworkRecord: decodeVectorNetwork

Wire version history

Each raise of the scene wire version, newest first, with the commit that made it.

VersionCommit titleIntroduced in
24fix(render-core): tight paint bounds for lines/connectors; exports cover everything a layer and its children paintd00b191 #121 2026-10-04
23feat(web): Stroke settings panel with dashes, end points and joins, and round Pencil strokesdad417a #88 2026-09-28
22feat(web): add connectors: Connector tool, key-point attach points, pinned and re-attachable ends, and line end stylesf6199e2 #73 2026-09-28
21feat(web): transform a multi-selection through one box, as in Figmab833870 #72 2026-09-27
20refactor(render-core): paint transform handles with the size and angle the editor sends0d6ea45 #71 2026-09-27
19feat(model): hide paints and give each paint a blend modec753479 #59 2026-09-26
18feat(model): give each rectangle and frame stroke its own side weights7382f68 #55 2026-09-26
17feat(web): auto layout gap and padding handles on the canvas, drawn by the renderera292d32 #67 2026-09-26
16feat(web): draw the rulers in the renderer and hit-test rulers and guides7b2e247 #64 2026-09-26
15feat(web): draw frame titles in the renderer and hit-test them to select, move and rename frames555ae8d #62 2026-09-26
14feat(model): round each corner of rectangles and frames on its owne6739c6 #54 2026-09-26
13feat: draw lines, arrows, ellipses, polygons and stars4c61909 #20 2026-09-23
12feat(render-core): round vector vertices with per-vertex corner radii837342a 2026-09-18
11feat(render-core): support corner smoothing and rounded framesa9795ca 2026-09-17
10feat(render-core): support blend modes and sibling masksb90c0f5 2026-09-17
9feat(render-core): support inner shadows, layer blur, and background blurebc9460 2026-09-17
8feat(render-core): support radial, angular, and image paints142ea63 2026-09-17
7feat(render-core): support multiple strokes per noded037d4d 2026-09-17
6feat(render-core): support positioned strokes9bac561 2026-08-18
5feat(render-core): add wire-v5 retained styling5aa0d67 2026-08-12
4feat(render-core): retain media scene metadatad4dee2d 2026-08-12
3feat(renderer): retain rich text in wire v34a4a289 2026-08-10
2feat(renderer): add interactive C++ foundation5e38c7a 2026-08-10

From git log -S on RENDER_CORE_WIRE_VERSION. The text is the title of the commit that raised the version.

Wasm module and sessions

RenderCoreModule and RenderCoreSession are the renderer-neutral session interfaces; the portable Wasm facade implements them over the C ABI. Use createPortableWasmRenderCore for the Skia-free build, createOwnedWasmRenderCoreSession for an extra session in a build that also paints, and attachPortableWasmRenderCoreSession to wrap a session another backend owns.

interface RenderCoreModule

source

A loaded renderer build that can create sessions.

readonly capabilities: RenderCoreCapabilitiessource

What this build supports.

createSession(): RenderCoreSessionsource

Creates a session with an empty scene.

Native: taco_render_session_destroy

Native, in helpers: taco_render_session_create

interface RenderCoreSession

source

Low-level synchronous wire/C-ABI session used behind the editor contract.

One session holds one retained scene. Each method is a thin wrapper over one C function. Queries that take expectedGeneration fail when the scene has changed since then, so a stale result cannot be mistaken for a current one. Queries return undefined when the node or resource does not exist. PortableWasmRenderCoreSession is the Wasm implementation.

apply(batch: Uint8Array): RenderUpdateReportsource

Applies one change batch from RenderChangeBatchEncoder. The batch applies completely or not at all; a rejected batch throws.

Native: taco_render_session_apply

Native, in helpers: taco_render_session_get_last_dirty_bounds, taco_render_session_copy_last_error

applyWithStaticImageFrames( batch: Uint8Array, expectedGeneration: bigint, frames: readonly RenderStaticImageFrame[] ): RenderUpdateReportsource

Atomically publishes the batch and candidate generation’s exact static-image set.

Native: taco_render_session_apply_with_static_image_frames

Native, in helpers: taco_render_session_get_last_dirty_bounds, taco_render_session_copy_last_error

applyWithMediaFrames( batch: Uint8Array, expectedGeneration: bigint, frames: readonly RenderMediaFrame[] ): RenderUpdateReportsource

Atomically publishes a batch and its exact mixed image/video frame set.

Native: taco_render_session_apply_with_media_frames

Native, in helpers: taco_render_session_get_last_dirty_bounds, taco_render_session_copy_last_error

updateVideoFrames( expectedGeneration: bigint, frames: readonly RenderVideoFrame[] ): RenderUpdateReportsource

Replaces only video frames while retaining the exact scene identity.

Native: taco_render_session_update_video_frames

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

getSceneInfo(): RenderSceneInfosource

The scene size, counts, generation and digest.

Native: taco_render_session_get_scene_info

Native, in helpers: taco_render_session_copy_last_error

getNodeGeometry(id: string): RenderNodeGeometry | undefinedsource

The bounds and transform of one node.

Native: taco_render_session_get_node_geometry

Native, in helpers: taco_render_session_copy_last_error

getGeometrySnapshot(): RenderGeometrySnapshotsource

The geometry of every node, back to front.

Native: taco_render_session_get_geometry_snapshot

Native, in helpers: taco_render_session_copy_last_error

Native, through decodeWasmGeometryRecords: TacoRenderGeometryRecord

hitTest(x: number, y: number, worldTolerance?: number): readonly RenderHit[]source

Returns topmost-first geometric hits using an explicit world-space tolerance.

Native: taco_render_session_hit_test

Native, in helpers: taco_render_session_copy_last_error

Native, through decodeWasmHitRecords: TacoRenderHit

getTextCarets( nodeId: string, expectedGeneration?: bigint ): RenderTextCaretSnapshot | undefinedsource

Every caret position in a text node, with its geometry.

Native: taco_render_session_get_text_carets

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

getTextRangeGeometry( nodeId: string, range: RenderTextRange, expectedGeneration?: bigint ): RenderTextRangeGeometry | undefinedsource

The rectangles that cover a text range, one or more per line.

Native: taco_render_session_get_text_range_rects

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

hitTestText( nodeId: string, localX: number, localY: number, expectedGeneration?: bigint ): RenderTextHitResult | undefinedsource

The text position nearest a point in the node’s local coordinates.

Native: taco_render_session_hit_test_text

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

navigateText( nodeId: string, from: RenderTextPosition, operation: RenderTextNavigationOperation, preferredLocalX?: number, expectedGeneration?: bigint ): RenderTextNavigationResult | undefinedsource

Moves a text position by one caret step. preferredLocalX keeps the column when moving up or down across lines.

Native: taco_render_session_navigate_text

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

getTextLayoutMetrics( nodeId: string, expectedGeneration?: bigint ): RenderTextLayoutMetrics | undefinedsource

Line metrics and the suggested auto-size of a text node.

Native: taco_render_session_get_text_layout_metrics

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

getFontMetadata( fontResourceId: string, expectedGeneration?: bigint ): RenderFontMetadataSnapshot | undefinedsource

The OpenType feature tags and variation axes of one font resource.

Native: taco_render_session_get_font_metadata

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

setTextEditingOverlay( nodeId: string, overlay: RenderTextEditingOverlay, expectedGeneration?: bigint ): voidsource

Paints the selection, composition range and caret of the text node being edited. The next scene change clears it.

Native: taco_render_session_set_text_editing_overlay

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

clearTextEditingOverlay(expectedGeneration?: bigint): voidsource

Removes the text editing overlay.

Native: taco_render_session_clear_text_editing_overlay

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

setSelectionChrome(chrome: RenderSelectionChrome | null): booleansource

Replaces the selection outlines and handles the renderer paints over the scene, or clears them with null. Returns false when the loaded module predates native selection chrome, so the host keeps its own.

Native: taco_render_session_set_selection_chrome

Native, in helpers: taco_render_session_copy_last_error

Native, through encodeSelectionChrome: taco_render_session_set_selection_chrome, SessionCache.setSelectionChrome

setCanvasOverlay(overlay: RenderCanvasOverlay | null): voidsource

Replaces the overlay lines the renderer paints over the scene, or clears them with null. Throws when the loaded module cannot paint them.

Native: taco_render_session_set_canvas_overlay

Native, in helpers: taco_render_session_copy_last_error

Native, through encodeCanvasOverlay: taco_render_session_set_canvas_overlay, SessionCache.setCanvasOverlay

setCanvasOverlayFont(bytes: Uint8Array): booleansource

Sets the font file (TrueType, OpenType or WOFF2 bytes) frame titles in the canvas overlay are drawn with. Returns false when the loaded module cannot draw titles.

Native: taco_render_session_set_canvas_overlay_font

Native, in helpers: taco_render_session_copy_last_error

getVectorGeometry( nodeId: string, documentToScreen: Transform, expectedGeneration?: bigint ): RenderVectorGeometrySnapshot | undefinedsource

The vertices, handles, segments and regions of a vector node, with screen positions from documentToScreen.

Native: taco_render_session_get_vector_geometry

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through decodeWasmVectorGeometry: TacoRenderVectorVertex, TacoRenderVectorCubicHandle, TacoRenderVectorSegment, TacoRenderVectorRegion

hitTestVector( nodeId: string, query: RenderVectorHitQuery, expectedGeneration?: bigint ): RenderVectorHitSnapshot | undefinedsource

The vector vertices, handles, segments and regions near a screen point.

Native: taco_render_session_hit_test_vector

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through decodeWasmVectorHits: TacoRenderVectorHit

booleanVectorPaths( request: RenderVectorBooleanPathRequest, expectedGeneration?: bigint ): RenderVectorBooleanPathSnapshotsource

Combines the paths of two nodes and returns the resulting vector network.

Native: taco_render_session_boolean_vector_paths

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through decodeVectorNetworkRecord: encodeVectorNetwork

setVectorEditingOverlay( nodeId: string, overlay: RenderVectorEditingOverlay, expectedGeneration?: bigint ): voidsource

Paints the vector editing overlay (vertices, handles and selection) of one node. The next scene change clears it.

Native: taco_render_session_set_vector_editing_overlay

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through encodeVectorNetworkRecord: decodeVectorNetwork

clearVectorEditingOverlay(expectedGeneration?: bigint): voidsource

Removes the vector editing overlay.

Native: taco_render_session_clear_vector_editing_overlay

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

mapViewport(options: ViewportMappingOptions): ViewportMappingsource

Computes how the scene fits a viewport of a given size.

Native: taco_render_session_map_viewport

Native, in helpers: taco_render_session_copy_last_error

render(target: RgbaRenderTarget): RenderFrameReportsource

Paints the scene into an RGBA pixel buffer. Builds without a CPU raster backend, such as the portable Wasm build, throw.

Native: taco_render_session_render_rgba

Native, in helpers: taco_render_session_copy_last_error

getLastError(): stringsource

Copies the session-owned diagnostic string; no native memory escapes.

Native, in helpers: taco_render_session_copy_last_error

dispose(): voidsource

Releases the session. Later calls throw.

Native: taco_render_session_destroy

interface RenderCoreCapabilities

source

What the loaded renderer build supports, read from the C capability structs. Check a flag with requireRenderCapability before calling the matching session method.

Native: TacoRenderCapabilities, TacoRenderExtendedCapabilities

readonly abiVersion: numbersource
readonly wireVersion: numbersource

Retained scene/change batch wire version.

readonly vectorNetworkWireVersion: numbersource

Headerless VectorNetwork payload version used by overlays and Boolean results.

readonly retainedScene: booleansource
readonly structuredPaths: booleansource
readonly styledTextRecords: booleansource
readonly embeddedResources: booleansource
readonly skia: booleansource
readonly nativeRaster: booleansource
readonly wasm: booleansource
readonly webgpu: booleansource
readonly skparagraph: booleansource
readonly harfbuzz: booleansource
readonly icu: booleansource
readonly geometryQueries: booleansource
readonly hitTesting: booleansource
readonly dirtyBounds: booleansource
readonly textCarets: booleansource
readonly textRangeRects: booleansource
readonly textHitTesting: booleansource
readonly textNavigation: booleansource
readonly textLayoutMetrics: booleansource
readonly textEditingOverlays: booleansource
readonly fontMetadata: booleansource
readonly incrementalRendering: booleansource
readonly renderDiagnostics: booleansource
readonly staticImageFrames: booleansource
readonly videoFrames: booleansource
readonly vectorGeometry: booleansource
readonly vectorHitTesting: booleansource
readonly vectorEditingOverlays: booleansource
readonly vectorBooleanOperations: booleansource

type RequiredRenderCapability = | "skia" | "nativeRaster" | "wasm" | "webgpu" | "skparagraph" | "harfbuzz" | "icu" | "textCarets" | "textRangeRects" | "textHitTesting" | "textNavigation" | "textLayoutMetrics" | "textEditingOverlays" | "fontMetadata" | "incrementalRendering" | "renderDiagnostics" | "staticImageFrames" | "videoFrames" | "vectorGeometry" | "vectorHitTesting" | "vectorEditingOverlays" | "vectorBooleanOperations"

source

A capability flag requireRenderCapability can check.

function requireRenderCapability( capabilities: RenderCoreCapabilities, capability: RequiredRenderCapability ): void

source

Throws RenderCoreCapabilityError unless capabilities has the flag.

class RenderCoreCapabilityError extends Error

source

Thrown by requireRenderCapability when the build lacks a capability.

readonly capability: RequiredRenderCapabilitysource
new (capability: RequiredRenderCapability)source

function createPortableWasmRenderCore( module: PortableWasmExports, options: PortableWasmFacadeOptions = {} ): PortableWasmRenderCoreModule

source

Wraps a loaded portable Wasm module; see PortableWasmRenderCoreModule.

class PortableWasmRenderCoreModule implements RenderCoreModule

source

A RenderCoreModule over the portable Wasm build, which has no Skia. The constructor checks the module’s exports and ABI and wire versions, and rejects a module that carries a renderer; use createOwnedWasmRenderCoreSession for those.

readonly capabilities: RenderCoreCapabilitiessource
new (module: PortableWasmExports, options: PortableWasmFacadeOptions = {})source

Native, in helpers: taco_render_wasm_abi_version, taco_render_wasm_wire_version, taco_render_wasm_vector_network_wire_version, taco_render_wasm_portable_capabilities, taco_render_get_capabilities, taco_render_get_extended_capabilities

createSession(): PortableWasmRenderCoreSessionsource

Native: taco_render_session_destroy

Native, in helpers: taco_render_session_create

function createOwnedWasmRenderCoreSession( module: PortableWasmExports, options: PortableWasmFacadeOptions = {} ): PortableWasmRenderCoreSession

source

Creates a second, facade-owned session in a module that also carries a renderer, such as the Skia WebGPU build. createPortableWasmRenderCore refuses those modules because it certifies the Skia-free foundation; this entry point skips only that certification. Disposing the facade destroys the native session.

Native: taco_render_session_destroy

Native, in helpers: taco_render_wasm_abi_version, taco_render_wasm_wire_version, taco_render_wasm_vector_network_wire_version, taco_render_wasm_portable_capabilities

and 3 moretaco_render_get_capabilities, taco_render_get_extended_capabilities, taco_render_session_create

function attachPortableWasmRenderCoreSession( module: PortableWasmExports, session: number, options: PortableWasmFacadeOptions = {} ): PortableWasmRenderCoreSession

source

Attaches the shared strict Wasm C-ABI facade to a session whose lifetime is owned by a renderer backend. Disposing the facade releases only its staging memory; the backend remains responsible for the native session handle.

Native, in helpers: taco_render_wasm_abi_version, taco_render_wasm_wire_version, taco_render_wasm_vector_network_wire_version, taco_render_wasm_portable_capabilities, taco_render_get_capabilities, taco_render_get_extended_capabilities

interface PortableWasmRenderCoreSession extends RenderCoreSession

source

The Wasm implementation of RenderCoreSession. Each method copies its input into Wasm memory, calls one taco_render_session_* function, checks the result strictly, and copies the output back, so no Wasm memory escapes.

readonly capabilities: RenderCoreCapabilitiessource

What the loaded module supports.

readonly transferMetrics: WasmBatchStagingMetricssource

Counters for the reused buffer that carries batches into Wasm memory.

exportScene(request: RenderExportRequest): RenderExportResultsource

Paints one scene region into a raster, SVG, or PDF target.

Native: taco_render_session_export, taco_render_session_copy_export_bytes

Native, in helpers: taco_render_session_copy_last_error

outlineStroke( request: RenderStrokeOutlineRequest, expectedGeneration?: bigint, budget?: StrokeOutlineBudget ): RenderStrokeOutlineSnapshot | undefinedsource

One stroke of a path or container node as the filled shape it paints (see RenderStrokeOutlineSnapshot), or undefined when the node does not exist. Throws PortableWasmStatusError when the node has no such stroke, is a text or media node, or the stroke cannot be outlined; its detail is the renderer’s reason. Throws a plain error when the renderer build predates the query. With a budget, the outline’s size on the wire is charged to it before the outline is copied out and decoded; one that does not fit throws StrokeOutlineBudgetError.

Native: taco_render_session_outline_stroke

Native, in helpers: taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through decodeVectorNetworkRecord: encodeVectorNetwork

interface PortableWasmFacadeOptions extends WasmBatchStagingOptions

source

Optional caps on how much memory one query may ask the module for. Each value must be positive and at most the facade’s built-in maximum.

readonly maximumQueryRecords?: numbersource
readonly maximumTextQueryRecords?: numbersource
readonly maximumQueryIdentifierBytes?: numbersource
readonly maximumVectorQueryRecords?: numbersource
readonly maximumVectorIdentifierBytes?: numbersource
readonly maximumErrorBytes?: numbersource

interface PortableWasmExports extends WasmBatchMemory

source

The Emscripten module surface the facade needs: the Wasm heap, malloc and free, and the exported taco_render_* C functions (Emscripten prefixes each with an underscore).

_taco_render_get_capabilities(capabilities: number): numbersource
_taco_render_get_extended_capabilities( version: number, capabilities: number ): numbersource
_taco_render_session_create(options: number, sessionOutput: number): numbersource
_taco_render_session_destroy(session: number): voidsource
_taco_render_session_apply( session: number, batch: number, batchSize: number, report: number ): numbersource
_taco_render_session_apply_with_static_image_frames( session: number, batch: number, batchSize: number, frameSet: number, report: number ): numbersource
_taco_render_session_apply_with_media_frames( session: number, batch: number, batchSize: number, frameSet: number, report: number ): numbersource
_taco_render_session_update_video_frames( session: number, frameSet: number, dirtyBounds: number ): numbersource
_taco_render_session_get_scene_info(session: number, info: number): numbersource
_taco_render_session_get_node_geometry( session: number, nodeId: number, nodeIdSize: number, geometry: number ): numbersource
_taco_render_session_get_geometry_snapshot( session: number, records: number, recordCapacity: number, nodeIds: number, nodeIdCapacity: number, generation: number, recordCount: number, nodeIdByteCount: number ): numbersource
_taco_render_session_hit_test( session: number, worldX: number, worldY: number, worldTolerance: number, hits: number, hitCapacity: number, nodeIds: number, nodeIdCapacity: number, hitCount: number, nodeIdByteCount: number ): numbersource
_taco_render_session_get_last_dirty_bounds( session: number, dirtyBounds: number ): numbersource
_taco_render_session_get_text_carets( session: number, nodeId: number, nodeIdSize: number, query: number, carets: number, caretCapacity: number, result: number ): numbersource
_taco_render_session_get_text_range_rects( session: number, nodeId: number, nodeIdSize: number, query: number, rects: number, rectCapacity: number, result: number ): numbersource
_taco_render_session_hit_test_text( session: number, nodeId: number, nodeIdSize: number, query: number, result: number ): numbersource
_taco_render_session_navigate_text( session: number, nodeId: number, nodeIdSize: number, query: number, result: number ): numbersource
_taco_render_session_get_text_layout_metrics( session: number, nodeId: number, nodeIdSize: number, query: number, metrics: number ): numbersource
_taco_render_session_get_font_metadata( session: number, fontResourceId: number, fontResourceIdSize: number, query: number, features: number, featureCapacity: number, axes: number, axisCapacity: number, result: number ): numbersource
_taco_render_session_set_text_editing_overlay( session: number, nodeId: number, nodeIdSize: number, overlay: number ): numbersource
_taco_render_session_clear_text_editing_overlay( session: number, query: number ): numbersource
_taco_render_session_set_selection_chrome?( session: number, bytes: number, size: number ): numbersource

Absent from modules built before native selection chrome.

_taco_render_session_set_canvas_overlay?( session: number, bytes: number, size: number ): numbersource

Absent from modules built before native overlay lines.

_taco_render_session_set_canvas_overlay_font?( session: number, bytes: number, size: number ): numbersource

Absent from modules built before frame titles in the canvas overlay.

_taco_render_session_get_vector_geometry( session: number, nodeId: number, nodeIdSize: number, query: number, vertices: number, vertexCapacity: number, handles: number, handleCapacity: number, segments: number, segmentCapacity: number, regions: number, regionCapacity: number, identifiers: number, identifierCapacity: number, result: number ): numbersource

Absent from modules built before wire version 24.

_taco_render_session_hit_test_vector( session: number, nodeId: number, nodeIdSize: number, query: number, hits: number, hitCapacity: number, identifiers: number, identifierCapacity: number, result: number ): numbersource
_taco_render_session_set_vector_editing_overlay( session: number, nodeId: number, nodeIdSize: number, overlay: number, networkBytes: number, networkByteCount: number, components: number, componentCount: number, identifiers: number, identifierByteCount: number ): numbersource
_taco_render_session_clear_vector_editing_overlay( session: number, query: number ): numbersource
_taco_render_session_boolean_vector_paths( session: number, primaryNodeId: number, primaryNodeIdSize: number, secondaryNodeId: number, secondaryNodeIdSize: number, resultIdNamespace: number, resultIdNamespaceSize: number, query: number, networkBytes: number, networkCapacity: number, result: number ): numbersource
_taco_render_session_map_viewport( session: number, options: number, mapping: number ): numbersource
_taco_render_session_render_rgba(session: number, target: number): numbersource
_taco_render_session_render_rgba_incremental( session: number, target: number, options: number, report: number ): numbersource
_taco_render_session_export?( session: number, request: number, result: number ): numbersource

Optional: builds before scene export omit both export entry points.

_taco_render_session_copy_export_bytes?( session: number, destination: number, destinationSize: number ): numbersource
_taco_render_session_copy_last_error( session: number, destination: number, destinationSize: number ): numbersource
_taco_render_wasm_abi_version(): numbersource
_taco_render_session_outline_stroke?( session: number, nodeId: number, nodeIdSize: number, resultIdNamespace: number, resultIdNamespaceSize: number, query: number, networkBytes: number, networkCapacity: number, result: number ): numbersource

Absent from renderer builds made before stroke outlines; no capability flag announces it, so outlineStroke checks for the export.

_taco_render_wasm_vector_network_wire_version(): numbersource
_taco_render_wasm_wire_version(): numbersource
_taco_render_wasm_portable_capabilities(): numbersource

class PortableWasmAbiError extends Error

source

The Wasm module broke the C ABI contract: a missing export, a wrong version, or a result with bad sizes, reserved bytes or values.

new (message: string)source

class PortableWasmStatusError extends Error

source
readonly operation: stringsource
readonly status: RenderCoreStatussource
readonly statusName: stringsource
readonly detail: stringsource
new (operation: string, status: RenderCoreStatus, detail = "")source

const RENDER_CORE_STATUS: Readonly< { readonly ok: 0; readonly invalidArgument: 1; readonly unsupportedVersion: 2; readonly malformedBatch: 3; readonly limitExceeded: 4; readonly invalidScene: 5; readonly notFound: 6; readonly capabilityUnavailable: 7; readonly bufferTooSmall: 8; readonly internalError: 9; readonly staleGeneration: 10; } >

source

The status codes every C function returns, by name. The values are the TacoRenderStatus constants.

Native: TacoRenderStatus

type RenderCoreStatus = (typeof RENDER_CORE_STATUS)[keyof typeof RENDER_CORE_STATUS]

source

One RENDER_CORE_STATUS value.

const RENDER_CORE_ABI_VERSION: 1

source

The C ABI version this package speaks. The Wasm module must report the same.

Native: TACO_RENDER_ABI_VERSION

const PORTABLE_WASM_ABI_LAYOUT: Readonly< { readonly capabilitiesBytes: 28; readonly sessionOptionsBytes: 32; readonly updateReportBytes: 32; readonly sceneInfoBytes: 48; readonly nodeGeometryBytes: 216; readonly geometryRecordBytes: 184; readonly dirtyBoundsBytes: 112; readonly hitRecordBytes: 64; readonly viewportOptionsBytes: 40; readonly viewportMappingBytes: 136; readonly rgbaTargetBytes: 24; readonly staticImageFrameBytes: 56; readonly staticImageFrameSetBytes: 24; readonly extendedCapabilitiesBytes: 88; readonly textQueryBytes: 16; readonly textPositionBytes: 16; readonly textCaretBytes: 56; readonly textBufferResultBytes: 16; readonly textRangeQueryBytes: 48; readonly textRectBytes: 40; readonly textHitQueryBytes: 32; readonly textHitResultBytes: 32; readonly textNavigationQueryBytes: 48; readonly textNavigationResultBytes: 48; readonly textLayoutMetricsBytes: 80; readonly textEditingOverlayBytes: 80; readonly vectorQueryBytes: 64; readonly vectorVertexBytes: 48; readonly vectorHandleBytes: 64; readonly vectorSegmentBytes: 144; readonly vectorRegionBytes: 96; readonly vectorBufferResultBytes: 40; readonly vectorHitQueryBytes: 88; readonly vectorHitBytes: 40; readonly vectorHitBufferResultBytes: 24; readonly vectorEditingOverlayBytes: 128; readonly vectorEditingComponentBytes: 32; readonly vectorEditingOverlayQueryBytes: 16; readonly vectorBooleanQueryBytes: 24; readonly vectorBooleanResultBytes: 16; readonly strokeOutlineQueryBytes: 24; readonly strokeOutlineResultBytes: 16; readonly fontMetadataQueryBytes: 16; readonly fontFeatureBytes: 8; readonly fontAxisBytes: 40; readonly fontMetadataResultBytes: 24; } >

source

Audited wasm32 layouts for ABI version 1: the byte size of each C struct the facade reads or writes in Wasm memory. Each entry must equal the size of the matching struct in render_core.h (for example updateReportBytes is TacoRenderUpdateReport); the C++ side checks most of them with static_assert.

Native: TacoRenderCapabilities, TacoRenderSessionOptions, TacoRenderUpdateReport, TacoRenderSceneInfo, TacoRenderNodeGeometry, TacoRenderGeometryRecord, TacoRenderDirtyBounds, TacoRenderHit

and 36 moreTacoRenderViewportOptions, TacoRenderViewportMapping, TacoRenderRgbaTarget, TacoRenderStaticImageFrame, TacoRenderStaticImageFrameSet, TacoRenderExtendedCapabilities, TacoRenderTextQuery, TacoRenderTextPosition, TacoRenderTextCaret, TacoRenderTextBufferResult, TacoRenderTextRangeQuery, TacoRenderTextRect, TacoRenderTextHitQuery, TacoRenderTextHitResult, TacoRenderTextNavigationQuery, TacoRenderTextNavigationResult, TacoRenderTextLayoutMetrics, TacoRenderTextEditingOverlay, TacoRenderVectorQuery, TacoRenderVectorVertex, TacoRenderVectorCubicHandle, TacoRenderVectorSegment, TacoRenderVectorRegion, TacoRenderVectorBufferResult, TacoRenderVectorHitQuery, TacoRenderVectorHit, TacoRenderVectorHitBufferResult, TacoRenderVectorEditingOverlay, TacoRenderVectorEditingComponent, TacoRenderVectorEditingOverlayQuery, TacoRenderVectorBooleanQuery, TacoRenderVectorBooleanResult, TacoRenderFontMetadataQuery, TacoRenderFontFeature, TacoRenderFontAxis, TacoRenderFontMetadataResult

const RENDER_FONT_METADATA_MAX_FEATURES: 131070

source

The most OpenType feature tags a font metadata query returns.

Native: TACO_RENDER_FONT_METADATA_MAX_FEATURES

const RENDER_FONT_METADATA_MAX_AXES: 65535

source

The most variation axes a font metadata query returns.

Native: TACO_RENDER_FONT_METADATA_MAX_AXES

function decodeWasmGeometryRecords( recordBytes: Uint8Array, recordCount: number, identifierBytes: Uint8Array ): readonly RenderNodeGeometry[]

source

Decodes the geometry records that taco_render_session_get_geometry_snapshot writes, with their node IDs, without keeping references to Wasm memory.

Native: TacoRenderGeometryRecord

function decodeWasmHitRecords( recordBytes: Uint8Array, hitCount: number, identifierBytes: Uint8Array ): readonly RenderHit[]

source

Decodes topmost-first ABI-v1 hit records without retaining Wasm memory.

Native: TacoRenderHit

function decodeWasmVectorGeometry( vertexBytes: Uint8Array, vertexCount: number, handleBytes: Uint8Array, handleCount: number, segmentBytes: Uint8Array, segmentCount: number, regionBytes: Uint8Array, regionCount: number, identifierBytes: Uint8Array, generation: bigint ): RenderVectorGeometrySnapshot

source

Strictly decodes one generation-stamped vector-geometry payload.

Native: TacoRenderVectorVertex, TacoRenderVectorCubicHandle, TacoRenderVectorSegment, TacoRenderVectorRegion

function decodeWasmVectorHits( hitBytes: Uint8Array, hitCount: number, identifierBytes: Uint8Array, generation: bigint ): RenderVectorHitSnapshot

source

Strictly decodes vector hits in native priority, distance, and order.

Native: TacoRenderVectorHit

type RenderMediaFrame = | ({ readonly kind: "image" } & RenderStaticImageFrame) | ({ readonly kind: "video" } & RenderVideoFrame)

source

One member of a globally asset-ID-sorted mixed image/video frame set.

type RenderStaticImageFrame = | RenderStaticImageFrameReuse | RenderStaticImageFrameRgba8888

source

One member of the complete, logical-asset-ID-deduplicated static image set.

interface RenderStaticImageFrameRgba8888

source

One tightly packed, unpremultiplied RGBA8888 sRGB replacement image.

readonly disposition: "rgba8888"source
readonly assetId: stringsource
readonly contentRevision: bigintsource
readonly width: numbersource
readonly height: numbersource
readonly pixels: Uint8Arraysource

interface RenderStaticImageFrameReuse

source

Reuses the exact active image whose caller-owned content revision matches.

readonly disposition: "reuse-active"source
readonly assetId: stringsource
readonly contentRevision: bigintsource

type RenderVideoFrame = RenderVideoFrameReuse | RenderVideoFrameRgba8888

source

One exact decoded video-frame revision; replacements advance monotonically.

interface RenderVideoFrameRgba8888 extends RenderStaticImageFrameRgba8888

source

One tightly packed decoded video-frame replacement.

interface RenderVideoFrameReuse extends RenderStaticImageFrameReuse

source

Reuses the exact active video frame at one caller-owned revision.

Wasm memory transfer

Batches reach Wasm memory through one reused, grow-only allocation, so a steady stream of edits does not allocate for every batch.

class ReusableWasmBatchStagingBuffer

source

Reuses one grow-only Wasm allocation for successive retained wire batches. A batch is valid at the returned pointer until the next write or dispose.

new (memory: WasmBatchMemory, options: WasmBatchStagingOptions = {})source
get metrics(): WasmBatchStagingMetricssource

The current counters.

write(batch: Uint8Array): StagedWasmBatchsource

Copies a batch into the buffer, growing it when needed.

apply<Result>( batch: Uint8Array, applyBatch: (pointer: number, byteLength: number) => Result ): Resultsource

Writes a batch and passes its pointer and length to applyBatch.

dispose(): voidsource

Frees the Wasm allocation. Later writes throw.

interface WasmBatchMemory

source

Minimal Emscripten memory surface needed to transfer retained wire batches.

readonly HEAPU8: Uint8Arraysource
_malloc(bytes: number): numbersource
_free(pointer: number): voidsource

interface StagedWasmBatch

source

Where a staged batch sits in Wasm memory.

readonly pointer: numbersource
readonly byteLength: numbersource

interface WasmBatchStagingOptions

source

Staging buffer size bounds in bytes. The maximum defaults to WIRE_LIMITS.batchBytes.

readonly minimumCapacityBytes?: numbersource
readonly maximumCapacityBytes?: numbersource

interface WasmBatchStagingMetrics

source

Counters that show how often the staging buffer allocates and copies.

readonly allocations: numbersource

Native allocations made over the lifetime of this staging buffer.

readonly copies: numbersource

JavaScript-to-Wasm HEAPU8.set operations.

readonly bytesCopied: numbersource
readonly capacityBytes: numbersource
readonly peakBatchBytes: numbersource
readonly heapGrowthBytes: numbersource

Observed linear-memory growth since construction, including Wasm work.

readonly peakHeapBytes: numbersource

Fonts

When none of a text style’s fonts can draw a character, the renderer falls back to a built-in Noto family from Google Fonts, so the character never shows as an empty box. The fallback is chosen at render time and never saved in the document.

const AUTOMATIC_FALLBACK_FAMILIES: readonly [ "Noto Sans", "Noto Sans SC", "Noto Sans TC", "Noto Sans HK", "Noto Sans JP", "Noto Sans KR", "Noto Color Emoji" ]

source

The built-in Noto families automatic fallback can draw with.

type AutomaticFallbackFamily = typeof AUTOMATIC_FALLBACK_FAMILIES[number]

source

One of AUTOMATIC_FALLBACK_FAMILIES.

function automaticFallbackFamilies( cjk: CjkFamily, emoji: boolean ): readonly AutomaticFallbackFamily[]

source

The order in which automatic fallback families are tried for one grapheme cluster. Text tries Noto Sans (Latin, Greek, Cyrillic, Devanagari and Vietnamese), then the CJK families starting with cjk, then Noto Color Emoji. An emoji cluster (see isEmojiGrapheme) tries Noto Color Emoji first, so a heart with U+FE0F is drawn in color even though CJK fonts have a black-and-white heart.

function automaticFallbackAsset(family: AutomaticFallbackFamily): FontAsset

source

The font asset that stands for an automatic fallback family. It is not part of any document; the host passes the family’s loaded files for it through CompilePageOptions.getFontSlices, like a “google-fonts” document asset.

function automaticFallbackAssetById(id: string): FontAsset | undefined

source

The automatic fallback asset with this ID, if the ID is one.

const AUTOMATIC_FALLBACK_ASSET_ID_PREFIX: "automatic-fallback/"

source

Asset IDs that start with this prefix are reserved for automatic fallback.

function isAutomaticFallbackFamily( family: string ): family is AutomaticFallbackFamily

source

Whether family is one of AUTOMATIC_FALLBACK_FAMILIES.

function preferredCjkFamily( styleLanguage: string | undefined, nodeCodePoints: Iterable<number>, userLanguage: string | undefined ): CjkFamily

source

Chooses which CJK family draws a Han character that several of them have. The glyph forms differ by region, so the choice follows, in order:

  1. the language set on the text style (“ja”, “zh-TW”, ...);
  2. the text node itself: kana means Japanese and Hangul means Korean;
  3. the user’s language (the browser’s navigator.language);
  4. otherwise Simplified Chinese.

function cjkFamilyForLanguage( language: string | undefined ): CjkFamily | undefined

source

The CJK family whose glyph forms a BCP 47 language tag asks for: “ja” gives JP, “ko” gives KR, and Chinese gives HK for Hong Kong and Macau, TC for traditional script (such as “zh-TW” or “zh-Hant”), and SC otherwise. Other languages and malformed tags give undefined.

function graphemeClusters(text: string): string[]

source

Splits text into grapheme clusters, the units Skia picks a font for.

function isEmojiGrapheme(grapheme: string): boolean

source

Whether a grapheme cluster is an emoji drawn in color: one of the emoji and emoji sequences Unicode recommends for general use (RGI), such as “😀”, “❤️” (with U+FE0F), “1️⃣” or “🇯🇵”. A character that defaults to text style, such as “❤” without U+FE0F, is not.

function essentialCodePoints(grapheme: string): number[]

source

The code points of a grapheme cluster that a font must map to draw it. Default-ignorable characters (joiners, variation selectors) and controls are left out: shaping hides them instead of drawing an empty box.

Feature policy

A capability says what a renderer build contains. The feature policy says what the product does when a document uses a modeled feature: render it, handle it in the app, or reject it with a clear error. Every model feature has one entry.

const RENDER_FEATURE_POLICY_VERSION: 25

source

The renderer policy is intentionally separate from capability probing. A capability says what a particular binary contains; this table says what the product does when a canonical document uses a modeled feature.

interface RenderFeaturePolicy

source
readonly feature: stringsource

Stable semantic identifier used by tests and developer diagnostics.

readonly code: RenderFeaturePolicyCodesource

Stable machine-readable code. Do not rename it when implementation status changes.

readonly scope: RenderFeatureScopesource
readonly disposition: RenderFeatureDispositionsource
readonly note: stringsource

The exact boundary or ownership rule for the current retained renderer.

type RenderFeaturePolicyCode = `TACO_RENDER_POLICY_${string}`

source

type RenderFeatureDisposition = | "rendered" | "deliberate-static-policy" | "application-overlay" | "structured-rejection"

source

type RenderFeatureScope = "node" | "style" | "asset" | "document" | "editor"

source

const RENDER_FEATURE_POLICIES: readonly RenderFeaturePolicy[]

source

Flat registry for diagnostics, documentation generation, and invariant tests.

const RENDER_FEATURE_POLICY_TABLES: Readonly< { sceneNodes: { readonly frame: RenderFeaturePolicy; readonly group: RenderFeaturePolicy; readonly text: RenderFeaturePolicy; readonly code: RenderFeaturePolicy; readonly "shape.rectangle": RenderFeaturePolicy; readonly "shape.ellipse": RenderFeaturePolicy; readonly "shape.polygon": RenderFeaturePolicy; readonly "shape.star": RenderFeaturePolicy; readonly "shape.line": RenderFeaturePolicy; readonly "shape.path": RenderFeaturePolicy; readonly image: RenderFeaturePolicy; readonly video: RenderFeaturePolicy; readonly connector: RenderFeaturePolicy; readonly instance: RenderFeaturePolicy; }; paints: { readonly solid: RenderFeaturePolicy; readonly "linear-gradient": RenderFeaturePolicy; readonly "radial-gradient": RenderFeaturePolicy; readonly "angular-gradient": RenderFeaturePolicy; readonly image: RenderFeaturePolicy; }; strokePositions: { readonly center: RenderFeaturePolicy; readonly inside: RenderFeaturePolicy; readonly outside: RenderFeaturePolicy; }; strokeJoins: { readonly miter: RenderFeaturePolicy; readonly bevel: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; }; strokeDashCaps: { readonly none: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; readonly square: RenderFeaturePolicy; }; layouts: { readonly absolute: RenderFeaturePolicy; readonly vertical: RenderFeaturePolicy; readonly horizontal: RenderFeaturePolicy; readonly grid: RenderFeaturePolicy; }; mediaFits: { readonly contain: RenderFeaturePolicy; readonly cover: RenderFeaturePolicy; readonly fill: RenderFeaturePolicy; }; connectorRouting: { readonly straight: RenderFeaturePolicy; readonly elbow: RenderFeaturePolicy; readonly curve: RenderFeaturePolicy; }; connectorCaps: { readonly none: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; readonly square: RenderFeaturePolicy; readonly "line-arrow": RenderFeaturePolicy; readonly "triangle-arrow": RenderFeaturePolicy; readonly "reversed-triangle": RenderFeaturePolicy; readonly "circle-arrow": RenderFeaturePolicy; readonly "diamond-arrow": RenderFeaturePolicy; }; codeDecorations: { readonly emphasis: RenderFeaturePolicy; readonly addition: RenderFeaturePolicy; readonly deletion: RenderFeaturePolicy; readonly annotation: RenderFeaturePolicy; }; vectorSegments: { readonly line: RenderFeaturePolicy; readonly cubic: RenderFeaturePolicy; readonly "elliptical-arc": RenderFeaturePolicy; }; assetKinds: { readonly image: RenderFeaturePolicy; readonly video: RenderFeaturePolicy; readonly font: RenderFeaturePolicy; }; assetSources: { readonly url: RenderFeaturePolicy; readonly data: RenderFeaturePolicy; }; variableBindings: { readonly visibility: RenderFeaturePolicy; readonly opacity: RenderFeaturePolicy; readonly width: RenderFeaturePolicy; readonly height: RenderFeaturePolicy; readonly cornerRadius: RenderFeaturePolicy; readonly fillColor: RenderFeaturePolicy; readonly strokeColor: RenderFeaturePolicy; readonly strokeWidth: RenderFeaturePolicy; readonly gap: RenderFeaturePolicy; readonly wrapGap: RenderFeaturePolicy; readonly columnGap: RenderFeaturePolicy; readonly rowGap: RenderFeaturePolicy; readonly paddingTop: RenderFeaturePolicy; readonly paddingRight: RenderFeaturePolicy; readonly paddingBottom: RenderFeaturePolicy; readonly paddingLeft: RenderFeaturePolicy; readonly fontFamily: RenderFeaturePolicy; readonly fontSize: RenderFeaturePolicy; readonly fontWeight: RenderFeaturePolicy; readonly lineHeight: RenderFeaturePolicy; readonly letterSpacing: RenderFeaturePolicy; readonly textColor: RenderFeaturePolicy; }; crossCutting: { readonly singleFill: RenderFeaturePolicy; readonly multipleFills: RenderFeaturePolicy; readonly singleStroke: RenderFeaturePolicy; readonly multipleStrokes: RenderFeaturePolicy; readonly dashedStroke: RenderFeaturePolicy; readonly rectangleCornerRadius: RenderFeaturePolicy; readonly frameCornerRadius: RenderFeaturePolicy; readonly otherCornerRadius: RenderFeaturePolicy; readonly vectorCornerRadius: RenderFeaturePolicy; readonly strokeCaps: RenderFeaturePolicy; readonly cornerSmoothing: RenderFeaturePolicy; readonly shadows: RenderFeaturePolicy; readonly innerShadows: RenderFeaturePolicy; readonly layerBlur: RenderFeaturePolicy; readonly backgroundBlur: RenderFeaturePolicy; readonly blendModes: RenderFeaturePolicy; readonly masks: RenderFeaturePolicy; readonly paintCompositing: RenderFeaturePolicy; readonly sceneExport: RenderFeaturePolicy; readonly nodeOpacity: RenderFeaturePolicy; readonly isolatedGroupOpacity: RenderFeaturePolicy; readonly rectangularClip: RenderFeaturePolicy; readonly pathRegionPaints: RenderFeaturePolicy; readonly imageCrop: RenderFeaturePolicy; readonly videoCrop: RenderFeaturePolicy; readonly videoPlayback: RenderFeaturePolicy; readonly connectorCoordinates: RenderFeaturePolicy; readonly connectorAttachments: RenderFeaturePolicy; readonly codeSyntax: RenderFeaturePolicy; readonly codeDecorations: RenderFeaturePolicy; readonly codeLineNumbers: RenderFeaturePolicy; readonly variableResolution: RenderFeaturePolicy; readonly comments: RenderFeaturePolicy; readonly rulerGuides: RenderFeaturePolicy; readonly documentMetadata: RenderFeaturePolicy; }; } >

source

type SceneNodePolicyKey = SceneNodePolicyKeyFor<SceneNode>

source

Adding a SceneNode or shape discriminant requires an explicit policy here.

const SCENE_NODE_FEATURE_POLICIES: { readonly frame: RenderFeaturePolicy; readonly group: RenderFeaturePolicy; readonly text: RenderFeaturePolicy; readonly code: RenderFeaturePolicy; readonly "shape.rectangle": RenderFeaturePolicy; readonly "shape.ellipse": RenderFeaturePolicy; readonly "shape.polygon": RenderFeaturePolicy; readonly "shape.star": RenderFeaturePolicy; readonly "shape.line": RenderFeaturePolicy; readonly "shape.path": RenderFeaturePolicy; readonly image: RenderFeaturePolicy; readonly video: RenderFeaturePolicy; readonly connector: RenderFeaturePolicy; readonly instance: RenderFeaturePolicy; }

source

const PAINT_FEATURE_POLICIES: { readonly solid: RenderFeaturePolicy; readonly "linear-gradient": RenderFeaturePolicy; readonly "radial-gradient": RenderFeaturePolicy; readonly "angular-gradient": RenderFeaturePolicy; readonly image: RenderFeaturePolicy; }

source

const STROKE_POSITION_FEATURE_POLICIES: { readonly center: RenderFeaturePolicy; readonly inside: RenderFeaturePolicy; readonly outside: RenderFeaturePolicy; }

source

const LAYOUT_FEATURE_POLICIES: { readonly absolute: RenderFeaturePolicy; readonly vertical: RenderFeaturePolicy; readonly horizontal: RenderFeaturePolicy; readonly grid: RenderFeaturePolicy; }

source

const MEDIA_FIT_FEATURE_POLICIES: { readonly contain: RenderFeaturePolicy; readonly cover: RenderFeaturePolicy; readonly fill: RenderFeaturePolicy; }

source

const CONNECTOR_ROUTING_FEATURE_POLICIES: { readonly straight: RenderFeaturePolicy; readonly elbow: RenderFeaturePolicy; readonly curve: RenderFeaturePolicy; }

source

const CONNECTOR_CAP_FEATURE_POLICIES: { readonly none: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; readonly square: RenderFeaturePolicy; readonly "line-arrow": RenderFeaturePolicy; readonly "triangle-arrow": RenderFeaturePolicy; readonly "reversed-triangle": RenderFeaturePolicy; readonly "circle-arrow": RenderFeaturePolicy; readonly "diamond-arrow": RenderFeaturePolicy; }

source

const STROKE_JOIN_FEATURE_POLICIES: { readonly miter: RenderFeaturePolicy; readonly bevel: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; }

source

What the renderer does with each stroke join (wire version 23).

const STROKE_DASH_CAP_FEATURE_POLICIES: { readonly none: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; readonly square: RenderFeaturePolicy; }

source

What the renderer does with each dash end of a dashed stroke (wire version 23).

const CODE_DECORATION_FEATURE_POLICIES: { readonly emphasis: RenderFeaturePolicy; readonly addition: RenderFeaturePolicy; readonly deletion: RenderFeaturePolicy; readonly annotation: RenderFeaturePolicy; }

source

const VECTOR_SEGMENT_FEATURE_POLICIES: { readonly line: RenderFeaturePolicy; readonly cubic: RenderFeaturePolicy; readonly "elliptical-arc": RenderFeaturePolicy; }

source

const ASSET_KIND_FEATURE_POLICIES: { readonly image: RenderFeaturePolicy; readonly video: RenderFeaturePolicy; readonly font: RenderFeaturePolicy; }

source

const ASSET_SOURCE_FEATURE_POLICIES: { readonly url: RenderFeaturePolicy; readonly data: RenderFeaturePolicy; }

source

const VARIABLE_BINDING_FEATURE_POLICIES: { readonly visibility: RenderFeaturePolicy; readonly opacity: RenderFeaturePolicy; readonly width: RenderFeaturePolicy; readonly height: RenderFeaturePolicy; readonly cornerRadius: RenderFeaturePolicy; readonly fillColor: RenderFeaturePolicy; readonly strokeColor: RenderFeaturePolicy; readonly strokeWidth: RenderFeaturePolicy; readonly gap: RenderFeaturePolicy; readonly wrapGap: RenderFeaturePolicy; readonly columnGap: RenderFeaturePolicy; readonly rowGap: RenderFeaturePolicy; readonly paddingTop: RenderFeaturePolicy; readonly paddingRight: RenderFeaturePolicy; readonly paddingBottom: RenderFeaturePolicy; readonly paddingLeft: RenderFeaturePolicy; readonly fontFamily: RenderFeaturePolicy; readonly fontSize: RenderFeaturePolicy; readonly fontWeight: RenderFeaturePolicy; readonly lineHeight: RenderFeaturePolicy; readonly letterSpacing: RenderFeaturePolicy; readonly textColor: RenderFeaturePolicy; }

source

Adding a bindable model property requires a deliberate renderer policy.

const CROSS_CUTTING_FEATURE_POLICIES: { readonly singleFill: RenderFeaturePolicy; readonly multipleFills: RenderFeaturePolicy; readonly singleStroke: RenderFeaturePolicy; readonly multipleStrokes: RenderFeaturePolicy; readonly dashedStroke: RenderFeaturePolicy; readonly rectangleCornerRadius: RenderFeaturePolicy; readonly frameCornerRadius: RenderFeaturePolicy; readonly otherCornerRadius: RenderFeaturePolicy; readonly vectorCornerRadius: RenderFeaturePolicy; readonly strokeCaps: RenderFeaturePolicy; readonly cornerSmoothing: RenderFeaturePolicy; readonly shadows: RenderFeaturePolicy; readonly innerShadows: RenderFeaturePolicy; readonly layerBlur: RenderFeaturePolicy; readonly backgroundBlur: RenderFeaturePolicy; readonly blendModes: RenderFeaturePolicy; readonly masks: RenderFeaturePolicy; readonly paintCompositing: RenderFeaturePolicy; readonly sceneExport: RenderFeaturePolicy; readonly nodeOpacity: RenderFeaturePolicy; readonly isolatedGroupOpacity: RenderFeaturePolicy; readonly rectangularClip: RenderFeaturePolicy; readonly pathRegionPaints: RenderFeaturePolicy; readonly imageCrop: RenderFeaturePolicy; readonly videoCrop: RenderFeaturePolicy; readonly videoPlayback: RenderFeaturePolicy; readonly connectorCoordinates: RenderFeaturePolicy; readonly connectorAttachments: RenderFeaturePolicy; readonly codeSyntax: RenderFeaturePolicy; readonly codeDecorations: RenderFeaturePolicy; readonly codeLineNumbers: RenderFeaturePolicy; readonly variableResolution: RenderFeaturePolicy; readonly comments: RenderFeaturePolicy; readonly rulerGuides: RenderFeaturePolicy; readonly documentMetadata: RenderFeaturePolicy; }

source

Policies that describe modeled cardinality, cross-field behavior, and application-owned presentation/editor state rather than a string union.

Other exports

Exports not yet placed in a section above, in alphabetical order.

function compilePageRecords( document: DesignDocument, pageId: PageId, options: CompilePageOptions ): readonly RetainedNodeRecord[]

source

The retained node records a page compiles to, in paint order, before they are encoded; compilePageBatch encodes the same records. For tests and tools that compare compiled scenes.

const compilerApplyStats: { orderPathComputations: number; hierarchyRebuilds: number; resourceMapCopies: number; reusableResourceScans: number; incrementalAncestorWalks: number; }

source

function createStrokeOutlineBudget( maximumNetworkBytes: number, maximumRendererMilliseconds: number ): StrokeOutlineBudget

source

A new budget for one command, with nothing charged yet.

function documentWithEditorInstances( canonical: DesignDocument, renderer: DesignDocument | undefined ): DesignDocument

since 0.26.0source

The canonical document with the instance nodes a renderer projection adds for the editor (the “Show all variants” views of isolation mode, which draw a component with other property values). Such an instance and the layers inside it stand for themselves in hits and geometry, as a page instance does, although no canonical node owns them. The canonical document itself when the projection adds none.

function expandPageInstances( document: DesignDocument, pageId: PageId, resolver: InstanceResolver = resolveInstance, cache?: InstanceResolveCache ): InstanceExpansion

source

Walks a page’s stored tree, expands every instance on it and writes the bound fields of layers under modes other than the active ones.

function exportFromLoadedSession( session: ExportSession, request: Omit< RenderCompiledExportRequest, "batch" | "imageAssets" > ): RenderExportResult

source

Exports from a session that already holds the page: measures the export with measureExportFromLoadedSession, then asks the session to draw the region at the scale that the measurement gave. The size that is checked and the size that is drawn come from that one measurement, so they cannot differ.

With options.maxPixels, a raster export (rgba or png) whose pixel size is over the limit is refused with RenderExportTooLargeError before exportScene is called, so nothing is allocated or drawn for it.

Native, through measureExportFromLoadedSession: taco_render_session_get_scene_info, taco_render_session_get_node_geometry, taco_render_session_copy_last_error

Native, through PortableWasmRenderCoreSession.exportScene: taco_render_session_export, taco_render_session_copy_export_bytes, taco_render_session_copy_last_error

function exportPixelSize( region: Pick< RenderRect, | "width" | "height" >, scale: number ): { readonly pixelWidth: number; readonly pixelHeight: number }

source

The pixel size the renderer gives a raster export of a region at a scale: each side rounded up, as the native export rounds it.

type ExportSession = Pick< PortableWasmRenderCoreSession, "getSceneInfo" | "getNodeGeometry" | "exportScene" >

source

What exportFromLoadedSession uses of a renderer session.

function inheritedVectorRegionFills( document: DesignDocument, nodeId: NodeId ): readonly Paint[] | undefined

source

The fills a path’s regions are drawn with when they take the layer’s fills and the renderer needs those written on each region: several fills, or one that is not a plain solid colour (a gradient, an image, an opacity, a blend mode, a hidden fill). undefined when the layer’s one plain fill is enough, or the layer is not a path. The compiler writes these on the regions of the scene; the vector editing overlay, which draws the path from its own copy of the network, needs the same.

interface InstanceExpansion

source

A page’s document with its instances replaced by plain nodes: each instance becomes a frame whose children are its resolved layers, stored under their virtual IDs. The compiler then compiles it like any page. Layers whose variable bindings read a mode other than the active one (a page, frame, group or instance chose it, design 7.3) are stored with the values of their modes.

readonly document: DesignDocumentsource

The input document itself when the page has no instances and no layer needs other modes.

readonly instances: ReadonlyMap<NodeId, ExpandedInstance>source
readonly inheritedModes: ReadonlyMap<NodeId, VariableModes>source

The variable modes each page node inherits from its parent. A node left out inherits none, so its collections show their active modes.

readonly rebound: ReadonlyMap<NodeId, SceneNode>source

Page layers with their bound fields written for their modes.

readonly instancesByComponent: ReadonlyMap<ComponentId, ReadonlySet<NodeId>>source

The reverse index of design 4.3: every component to the instances that draw it, directly or through nesting (ResolvedInstance.componentIds).

readonly textSizesVersion: numbersource

getDrawnTextSizesVersion when the instances were resolved. A resolved text takes the size the renderer measured for text like it; when a size is measured later, every instance is resolved again on the next update.

interface InstanceExpansionUpdate

source

What updatePageInstances returns.

readonly expansion: InstanceExpansionsource
readonly changes: ChangeSetsource

changes in terms of the expanded documents, virtual layers included.

type InstanceResolver = ( document: DesignDocument, instanceId: NodeId, options?: ResolveInstanceOptions ) => ResolvedInstance

source

Derives the layers an instance shows. The page compiler calls it once per instance on the page; resolveInstance from @taco-slides/model is the default (design 4.1).

interface MeasuredExport

source

What an export will draw, as measureExportFromLoadedSession works it out.

readonly region: RenderRectsource

The part of the page the export shows, in page units.

readonly scale: numbersource

How many output units one page unit becomes.

readonly pixelWidth: numbersource

The size of the output, which for a raster export is its size in pixels: the region times the scale, each side rounded up.

readonly pixelHeight: numbersource

function measureExportFromLoadedSession( session: Pick< ExportSession, "getSceneInfo" | "getNodeGeometry" >, request: Pick< RenderCompiledExportRequest, "rootNodeId" | "scale" | "options" > ): MeasuredExport

source

Measures an export from a session that already holds the page, without drawing: the region (the page, or everything the root layer and its descendants paint, with shadows, blurs and strokes outside the layer’s box), the scale, and the pixel size these give. It only reads geometry the session already has, so it allocates no picture, however large the export would be. Throws when the root layer paints nothing.

Native, through RenderCoreSession.getSceneInfo: taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through RenderCoreSession.getNodeGeometry: taco_render_session_get_node_geometry, taco_render_session_copy_last_error

function outlineStrokesInThrowawaySession( createSession: () => StrokeOutlineSession, request: RenderCompiledStrokeOutlineRequest, modelVectorNetwork: ( network: VectorNetworkRecord ) => VectorNetwork, now: () => number = () => performance.now() ): readonly StrokeOutlineResult[]

source

Outlines the strokes of a request in a session made for it and disposed when it ends, however it ends. A stroke the renderer refuses (PortableWasmStatusError) is answered with the renderer’s reason and the others go on; any other error ends the request with the answers so far dropped.

The renderer works on the calling thread, so with a request.budget the time every step takes is measured with now (milliseconds) and counted down in the budget: making the session, loading the page into it, and each stroke with its decoding, whether the stroke is outlined, refused by the renderer or fails. Before each step, the first included, the time left is checked; when none is left the step is not started and the request fails with StrokeOutlineTimeError. So many slow strokes cannot keep the thread for longer than the budget and one step, however many are asked for and however many of them the renderer refuses. A request whose last step uses up the time is still answered: its work is done. The budget arrives with the time already taken off that compiling the page took (InteractiveRenderSessionCoordinator.outlineDocumentStrokes).

Native, through RenderCoreSession.apply: taco_render_session_apply, taco_render_session_get_last_dirty_bounds, taco_render_session_copy_last_error

Native, through RenderCoreSession.getSceneInfo: taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through PortableWasmRenderCoreSession.outlineStroke: taco_render_session_outline_stroke, taco_render_session_get_scene_info, taco_render_session_copy_last_error

Native, through RenderCoreSession.dispose: taco_render_session_destroy

class RenderExportTooLargeError extends Error

source

A raster export would have more pixels than RenderExportOptions.maxPixels allows. It is thrown before the renderer is asked to draw, so no picture was allocated.

readonly pixelWidth: numbersource
readonly pixelHeight: numbersource
readonly maxPixels: numbersource
new (pixelWidth: number, pixelHeight: number, maxPixels: number)source

interface RenderVectorPendingHandle

source

A curve handle that no segment of the previewed network owns yet: the one a Pen drag pulls out of a point for the segment drawn from it next. The renderer draws it as editing chrome only (a guide line and a knob).

readonly vertexId: stringsource

The point the handle belongs to; it must be in the previewed network.

readonly dx: numbersource

The handle’s offset from its point, in the path’s local units.

readonly dy: numbersource
readonly mirrored: booleansource

Also draw the exact mirror of the handle about its point.

function resetCompilerApplyStats(): void

source

function resetSceneGeometrySnapshotStats(): void

source

function resetWasmGeometryDecodeStats(): void

source

function resolvedVisualStyle( document: DesignDocument, style: VisualStyle | undefined ): VisualStyle

source

The style a node is drawn with: its own style over the shared node style it names (styleId). A fill, stroke or shadow list the node sets itself replaces the shared list whole, also when it is empty. A style ID that names no shared style leaves the node’s own style. Every node compiled here goes through this, so an editor tool that must agree with what is on screen reads the style through it too.

function retainInstances( expansion: InstanceExpansion, drawn: (id: NodeId) => boolean ): InstanceExpansion

source

Drops instances the compiler did not draw, such as ones on another page.

const sceneGeometrySnapshotStats: { nodeArrayCopies: number }

source

interface StrokeOutlineBudget

source

How many bytes of outlines one command may still keep. Every outline of the command (all its strokes, layers and views) is charged its size on the wire before it is copied out of the renderer and decoded, so the outlines held at once never pass maximumNetworkBytes, however many are asked for. The object is shared and counted down as outlines arrive.

readonly maximumNetworkBytes: numbersource
remainingNetworkBytes: numbersource
readonly maximumRendererMilliseconds: numbersource

How long the renderer may work on the command’s strokes, in milliseconds, over all its strokes, layers and views. The renderer outlines on the thread that draws the editor, which cannot answer the user meanwhile. Every step is measured and counted down here (timedStrokeOutlineStep): compiling the page (InteractiveRenderSessionCoordinator.outlineDocumentStrokes), then loading it and each stroke, whether it is outlined or refused (outlineStrokesInThrowawaySession). No step is started once nothing is left; the request fails with StrokeOutlineTimeError instead. A step that has started is not interrupted, so a command can take this long and one step more, and that step can be the compile of a large page.

remainingRendererMilliseconds: numbersource

class StrokeOutlineBudgetError extends Error

source

Thrown by outlineStroke for the outline that would take a command past its StrokeOutlineBudget. The outline was sized and nothing else: it was not copied out of the renderer or decoded, and the budget is unchanged.

readonly networkByteCount: numbersource
new (networkByteCount: number, budget: StrokeOutlineBudget)source

type StrokeOutlineSession = Pick< PortableWasmRenderCoreSession, "apply" | "getSceneInfo" | "outlineStroke" | "dispose" >

source

What outlineStrokesInThrowawaySession uses of a renderer session.

class StrokeOutlineTimeError extends Error

source

Thrown in place of starting a step of a command (compiling a page, loading it into the renderer, or outlining a stroke) when the command has already kept the thread for as long as its StrokeOutlineBudget allows, by InteractiveRenderSessionCoordinator.outlineDocumentStrokes and outlineStrokesInThrowawaySession. outlinedStrokeCount of the request’s strokeCount strokes were answered before it; their answers are dropped with the request.

readonly outlinedStrokeCount: numbersource
readonly strokeCount: numbersource
new ( budget: StrokeOutlineBudget, outlinedStrokeCount: number, strokeCount: number )source

function timedStrokeOutlineStep<Result>( budget: StrokeOutlineBudget | undefined, progress: { readonly outlinedStrokeCount: number; readonly strokeCount: number; }, step: () => Result, now: () => number = () => performance.now() ): Result

source

Runs one step of a command’s work on the thread that draws the editor and counts the time it takes, measured with now in milliseconds, down in the command’s budget, whether the step answers or throws. A step is not started once no time is left: StrokeOutlineTimeError is thrown in its place, with how many of the request’s strokes were answered by then. A step that has started is not interrupted, so it can run past the time; the budget is then left at 0 and the next step is not started. Without a budget the step runs untimed.

function updatePageInstances( previous: InstanceExpansion, current: DesignDocument, changes: ChangeSet, resolver: InstanceResolver = resolveInstance, cache?: InstanceResolveCache ): InstanceExpansionUpdate

source

Brings an expansion up to date after an edit without walking the page. An instance is resolved again when its own node changed, or when any component it draws changed (changedComponentIds, looked up in the reverse index, so editing a component nested in another reaches the outer instances too). Instances found in changes may be on another page; the compiler drops their layers, and retainInstances prunes them after.

const wasmGeometryDecodeStats: { identifierSetInserts: number }

source

Decodes ABI-v1 geometry records from bounded heap slices. The returned objects do not retain the supplied Wasm memory and never contain pointers.

function withCanonicalComponentChanges( rendererChanges: ChangeSet | undefined, canonicalChanges: ChangeSet ): ChangeSet

since 0.23.0source

The change set the compiler updates from: the renderer projection’s, with the changed components of the canonical change set added. An editor projection may draw a component’s root under a frame of its own (the component view of isolation mode, components design 5.5), where a diff of renderer documents no longer sees the component’s layers as component layers; the canonical change set still names the component, so its instances are resolved again.

@taco-design/electron TypeScript

57 of 58 exports documented · outline

@taco-design/electron shows the Taco Design editor inside an Electron app, in a WebContentsView. The package holds the built editor and a small helper for the main process. The helper serves the editor’s files from a custom address, taco-design://app/, makes the view with safe settings, and carries the document and the events between the app and the editor’s page.

Everything here runs in Electron’s main process. The app brings its own Electron, version 42 or later; 44.7.0 on macOS is the one that was tried. The package can be installed from https://packages.luyu.computer/ (stable or next) or from a CI .tgz file.

For a walk through a whole app, from installing the file to closing a window without losing work, read the guide Add Taco Design to an Electron app. The package’s own README lists what does not work yet, and how to build the .tgz file.

Registering the scheme

The editor’s page is loaded from taco-design://app/. Electron only treats that address as a secure page, which WebGPU needs, when the scheme is registered before the app is ready. An app with no custom scheme of its own calls registerTacoDesignScheme. Electron accepts protocol.registerSchemesAsPrivileged once per app, so an app with schemes of its own adds tacoDesignScheme to its own call instead.

function registerTacoDesignScheme(): void

source

Registers the taco-design scheme. Call it before the app is ready.

Electron accepts protocol.registerSchemesAsPrivileged once per app, so an app with schemes of its own must not call this: it adds the exported tacoDesignScheme object to its own call instead.

const tacoDesignScheme: { readonly scheme: "taco-design"; readonly privileges: { readonly standard: true; readonly secure: true; readonly supportFetchAPI: true; readonly stream: true; readonly codeCache: true; }; }

source

The entry for Electron’s protocol.registerSchemesAsPrivileged.

Electron accepts that call once per app, before the app is ready. An app with schemes of its own adds this object to its own single call; an app with none calls registerTacoDesignScheme().

  • standard: addresses such as /assets/app.js resolve, and storage works.
  • secure: the page is a secure context, which WebGPU needs.
  • supportFetchAPI: the renderer file is downloaded with fetch.
  • stream, codeCache: the renderer compiles while it downloads, and compiled scripts are kept between starts.

A scheme handler belongs to one session. createTacoDesignView sets the handler up for its own storage partition, so most apps never call handleTacoDesignScheme.

function handleTacoDesignScheme( session: Session, options: { readonly appDirectory?: string } = {} ): void

source

Serves taco-design://app/ on one session from a folder of editor files.

A scheme handler belongs to a session: a view in its own partition does not see a handler registered on another session. createTacoDesignView calls this for its own partition; call it yourself only when you make the WebContentsView yourself. Calling it again for the same session and folder does nothing. options.appDirectory names another folder of editor files to serve than the package’s own, and must be an absolute path.

const TACO_DESIGN_SCHEME: "taco-design"

source

The custom scheme the editor’s files are served from.

const TACO_DESIGN_HOST: "app"

source

The only host the scheme answers for.

const TACO_DESIGN_ORIGIN: "taco-design://app"

source

The origin of the editor page; permission rules compare against it.

const TACO_DESIGN_APP_URL: "taco-design://app/"

source

The address createTacoDesignView loads.

The package finds its editor files and its preload script next to its own code on disk. An app that bundles its main process keeps @taco-design/electron out of the bundle for that reason.

const appDirectory: string

source

The folder of editor files inside this package, served as taco-design://app/.

const preloadPath: string

source

The preload script of the editor’s view, as a file path.

Creating the view

createTacoDesignView makes the view and starts loading the editor. Add TacoDesignView.view to a window and give it bounds, then wait for TacoDesignView.ready: it resolves when the document is open and the renderer has drawn, and rejects with a TacoDesignError when the editor cannot start. A failed start is never a blank view: the view shows the message too.

function createTacoDesignView( options: TacoDesignViewOptions = {} ): TacoDesignView

source

Makes a WebContentsView that shows the Taco Design editor, with safe settings: sandbox and context isolation on, no Node.js in the page, its own storage partition, a short list of permissions, no navigation to other addresses and no new windows.

Call it after the app is ready, and after the scheme was registered.

interface TacoDesignViewOptions

source

The options of createTacoDesignView. Every one can be left out.

readonly document?: unknownsource

The document to open, as plain JSON (what getDocument() returned earlier and the app stored). Leave it out, or pass null, for a new blank document. A document that cannot be opened rejects ready with invalid-document or schema-too-new, and the view shows why.

readonly requestTimeoutMs?: numbersource

How long one request (getDocument() and the others) waits for its answer. Default 30 000 ms.

readonly partition?: stringsource

The storage partition of the view. Default persist:taco-design.

The package sets its own permission rules on this partition’s session, so do not share the partition with other pages of your app.

readonly appDirectory?: stringsource

A folder of editor files to serve instead of the one inside this package, as an absolute path. Anything else throws app-directory-invalid.

readonly readyTimeoutMs?: numbersource

How long one load of the editor’s page may take, from its beginning to a drawn editor, before it ends with ready-timeout: ready rejects for the first load, reload() for a later one. Default 60 000 ms.

readonly startupProgress?: TacoDesignStartupProgressDrawersource

Who draws the progress while the editor starts. "editor" (the default): the editor shows its own slim bar in the view, from the first drawing of the page until the editor’s first frame. "host": the editor shows none, and the app draws its own from the startupProgress event. The event is sent either way.

readonly onDownload?: (item: DownloadItem) => voidsource

Called for every download the editor starts (an export, for example). Call item.setSavePath() to save without asking. Without this option Electron shows its own save dialog.

readonly shortcuts?: ShortcutOverridessource

Changes to the editor’s keyboard shortcuts: a command ID mapped to its new keys ("CmdOrCtrl+Shift+D"), to a list of keys, or to null to switch that shortcut off. getShortcuts() lists the command IDs. Keys that cannot be read throw invalid-shortcut-keys here; an unknown command ID, a command that cannot be changed, or two commands on the same keys reject ready with the matching error code.

readonly menuShortcutConflict?: MenuShortcutConflictsource

What happens to a key that is an editor shortcut and also an accelerator in the app’s menu, while the editor has the keyboard: "editor" (the default) runs the editor’s command only, "app" runs the menu item only, "both" runs both. The README says how each is done.

interface TacoDesignView

source

What createTacoDesignView returns: the view to put in a window, and the calls and events that reach the editor inside it.

Every call that returns a promise waits for ready first. It rejects with a TacoDesignError when the editor never started or transport fails. Design calls also report domain errors as documented on designRequest.

readonly view: WebContentsViewsource

A plain WebContentsView: add it to a window and give it bounds.

readonly ready: Promise<TacoDesignReadyState>source

Resolves when the editor’s renderer reports ready. Rejects with a TacoDesignError when the page cannot be loaded or the editor cannot start; the view then shows a message instead of staying blank. Always handle the rejection.

getDocument(): Promise<TacoDesignDocumentSnapshot>source

The open document as plain JSON, without undo history, and the revision it was read at. Call it when you save; the editor stores nothing itself.

setDocument( document: unknown, options?: TacoDesignSetDocumentOptions ): Promise<TacoDesignSetDocumentResult>source

Replaces the open document. Undo history, selection and zoom start fresh. Rejects with invalid-document or schema-too-new and leaves the open document as it was when the new one cannot be opened.

With { ifRevision } the document is replaced only while the editor’s document is at that revision, the one getDocument() resolved with. Otherwise the call rejects with revision-changed and nothing changes.

newDocument(options?: TacoDesignSetDocumentOptions): Promise<DocumentInfo>source

Replaces the open document with a new blank one: one empty page, the default fonts and styles, and an ID of its own, like the document a view starts with when it is given none. Everything else is as for setDocument(): undo history, selection and zoom start fresh, the change event has the origin "host", and with { ifRevision } the document is replaced only at that revision (revision-changed otherwise). Resolves with the new document’s ID, title, page count and revision. Read it with getDocument() when you want to store it.

getState(): Promise<EditorState>source

The versions the editor speaks and a few facts about the open document.

designRequest( request: DesignRequest, options?: TacoDesignRequestOptions ): Promise<DesignReply>source

Sends a validated read/planning envelope after startup and version discovery. Preserves the explicit editor/document/epoch target. Domain failures resolve as DesignReply; transport failures reject with TacoDesignError. Local catalog or capability failures reject with DesignError. Use createDesignClient for typed results and throwing domain errors. No public writes are enabled.

undo(): Promise<TacoDesignHistoryResult>source

Undoes one step. done is false when there was nothing to undo.

redo(): Promise<TacoDesignHistoryResult>source

Redoes one step. done is false when there was nothing to redo.

exportLayers( options: TacoDesignExportOptions ): Promise<TacoDesignExportedFile[]>source

Draws one layer (nodeId) or one whole page (pageId) into a PNG, JPEG, SVG or PDF file through the editor’s own export, and resolves with the file’s name, type and bytes. The list has one file. Rejects with unknown-layer, unknown-page, unsupported-format, invalid-request, export-too-large (more than 8192 x 8192 pixels), renderer-failed or export-failed. A document with no page has nothing to export: every target is unknown-page or unknown-layer.

getSelection(): Promise<SelectionSummary>source

The page that is shown and the selected layers, each with its ID, name and kind.

zoomToFit( options?: { readonly target?: "page" | "selection"; } ): Promise<{ done: boolean }>source

Zooms the canvas so that the whole page fits, or the selected layers with { target: "selection" }. done is false when the selection was asked for and nothing is selected, and when the document has no page.

focusCanvas(): Promise<FocusSummary>source

Gives the keyboard to the view and, inside it, to the canvas, so that the editor’s shortcuts work. Resolves with where the keyboard is.

getShortcuts(): Promise<ShortcutCommandInfo[]>source

The editor’s keyboard shortcuts: every command with its stable ID, its title, its default keys and the keys in use now (none when it is switched off).

setShortcuts(overrides: ShortcutOverrides): Promise<ShortcutCommandInfo[]>source

Replaces this app’s changes to the shortcuts and resolves with the list as it is afterwards. overrides is the whole set: a command that is not named goes back to its default keys, and {} resets them all. When a change is wrong nothing is changed, and the promise rejects with unknown-shortcut-command, invalid-shortcut-keys, shortcut-keys-in-use or shortcut-not-customizable; error.path is the command ID. The changes are kept by this object, not by the page: they last as long as the view and survive reload().

readonly menuShortcutConflict: MenuShortcutConflictsource

The setting for keys that are both an editor shortcut and a menu accelerator.

setMenuShortcutConflict(conflict: MenuShortcutConflict): voidsource

Changes that setting. Any other value than the three throws invalid-option.

reload(): Promise<TacoDesignReloaded>source

Loads the editor’s page again without losing work. The page first ends editing and freezes (it accepts no edit, and refuses setDocument(), undo() and redo() with not-ready), then hands over its document; only after that answer is it loaded again, and the new page gets the document back. No edit can fall in between. Use it after a failure event. Resolves when the new page is ready as it is after ready: the editor answers and its renderer has drawn, so an exportLayers() right after it works. handedOver is false when the page did not hand over (its process had ended, or it did not answer in 5 seconds), and the editor then shows the last document this app set or read. The new page is loaded and watched exactly as the first one, so reload() rejects with every error ready can reject with, as soon as it is known (load-failed, app-files-missing, renderer-failed, page-crashed, ready-timeout after readyTimeoutMs, and the others), and the failure listeners hear the same error. It also rejects with disposed when the view is disposed meanwhile, and with load-failed when the page was not loaded again within 10 seconds of its answer (it then accepts edits again). Two calls at the same time are one reload. The undo history starts empty and the revision at 0.

on<Event extends keyof TacoDesignEvents>( event: Event, listener: TacoDesignEvents[Event] ): () => voidsource

Listens for "change" (the document changed), "selection" (the selection changed), "focus" (the keyboard moved), "saveRequested" (the Save keys were pressed in the editor), "startupProgress" (how far the start-up is), "failure" (the editor stopped working after ready, or could not be loaded again) or "reloaded" (the page was loaded again), or "design" (a stamped design invalidation). The returned function removes the listener.

focus(): voidsource

Gives the keyboard to the editor’s view.

dispose(): voidsource

Closes the view’s page. The view cannot be used afterwards.

interface TacoDesignReadyState

source

What ready resolves with.

readonly url: stringsource

The address the editor was loaded from.

readonly build: TacoDesignBuildInfo | nullsource

What the editor was built from, or null when the folder has no build record.

readonly protocolVersion: numbersource

The version of the host protocol the editor and this package agreed on.

readonly documentSchemaVersion: stringsource

The newest document schema version the editor reads, and the one it writes.

readonly rendererWireVersion: numbersource

The version of the format the editor and its renderer talk in.

readonly document: DocumentInfosource

The open document: its id, title, page count, revision, and whether undo and redo are possible.

interface TacoDesignBuildInfo

source

What the assemble script wrote next to the editor files.

readonly documentSchemaVersion: stringsource

The document schema version the editor reads and writes, such as 0.27.0.

readonly rendererWireVersion: numbersource

The version of the format the editor and its renderer talk in.

readonly commit: stringsource

The commit the editor was built from.

const DEFAULT_READY_TIMEOUT_MS: 60000

source

How long ready waits for the editor before it rejects, unless the app says otherwise.

The document

The editor in the view stores no document. It starts from the one the app hands over (TacoDesignViewOptions.document), keeps it in memory, and tells the app when it changes. Saving is the app’s job: read the document with TacoDesignView.getDocument and write it as JSON.

A document holds its pictures, so it can be tens of megabytes. Events therefore carry only a few facts about it (DocumentInfo), never the document itself. The undo history is not part of the document: it lives in the page and starts empty each time a document is handed over.

Read the document once more before a window closes, every time. The page has no “unsaved changes” guard, and TacoDesignView.getDocument is the only call that is never behind the newest edit.

TacoDesignView.setDocument replaces the open document. An app that opens another file saves the open one first, and an edit may arrive between that last read and the replacement, because the page is another process. So pass the revision of the read: setDocument(document, { ifRevision }) replaces the document only while the editor’s document is still at that revision. At any other revision the call rejects with revision-changed and nothing changes; read and write again, then retry. Revisions start at 0 again on every page load, so use a revision read after the last reloaded event.

TacoDesignView.newDocument replaces the open document with a new blank one, like the document a view starts with when it is given none. It takes ifRevision too and is otherwise setDocument. Use it for a “New” command and for a file that does not exist yet. setDocument(null) is refused, so that a file with no document in it never opens as a blank one by accident.

type DocumentJson =

source

A whole document as plain JSON, without its undo history. The host stores it and hands it back; it need not look inside.

readonly schemaVersion: stringsource

The document schema version the document is written in, such as 0.27.0.

readonly id: stringsource

The document’s ID.

readonly title: stringsource

The document’s title.

readonly [key: string]: unknownsource

Everything else in the document: its pages, layers, components and pictures.

interface TacoDesignDocumentSnapshot

source

What getDocument() resolves with.

readonly document: DocumentJsonsource

The whole document as plain JSON, without undo history. Write it with JSON.stringify.

readonly revision: numbersource

The revision the document was read at. It counts every change made so far, also those whose change event has not arrived yet, so a change event’s revision can be lower. Do not compare the two to decide whether to save before closing: always read the document one last time.

interface TacoDesignSetDocumentOptions

source

What setDocument() takes besides the document, and what newDocument() takes.

readonly ifRevision?: numbersource

Replace the document only while the editor’s document is at this revision (the revision that getDocument() resolved with). At any other revision the call rejects with revision-changed and nothing changes: an edit was made since that read. Revisions start at 0 again on every page load, so use one read after the last reloaded event.

interface TacoDesignSetDocumentResult extends DocumentInfo

source

What setDocument() resolves with.

readonly migratedFrom?: stringsource

The schema version the document arrived in, when the editor had to bring it up to date.

interface TacoDesignHistoryResult extends DocumentInfo

source

What undo() and redo() resolve with.

readonly done: booleansource

False when there was nothing to undo or redo.

interface DocumentInfo

source

A few facts about the open document, small enough to send with every change.

readonly id: stringsource

The document’s ID.

readonly title: stringsource

The document’s title.

readonly pageCount: numbersource

How many pages the document has.

readonly revision: numbersource

Goes up by one on every change to the document and on every setDocument. Starts at 0 on each page load.

readonly canUndo: booleansource

True when there is a step to undo.

readonly canRedo: booleansource

True when there is a step to redo.

interface EditorState

source

The versions the editor speaks and a few facts about the open document: the answer to a getState request.

readonly design?: DesignCapabilitysource

Optional read/planning capability; absent in older editors.

readonly protocolVersion: numbersource

The version of the host protocol the editor and the host agreed on.

readonly documentSchemaVersion: stringsource

The newest document schema version the editor reads, and the one it writes.

readonly rendererWireVersion: numbersource

The version of the format the editor and its renderer talk in.

readonly build: stringsource

Names the build of the editor’s page, for error reports.

readonly document: DocumentInfosource

The open document: its ID, title, page count, revision, and whether undo and redo are possible.

Events

TacoDesignView.on takes an event name and a listener and returns a function that removes the listener. A listener that throws does not stop the editor or the other listeners. Events are small: none of them carries the document.

change, selection and focus say what happened in the editor. startupProgress, failure and reloaded say how the page itself is doing. saveRequested says that the person pressed the editor’s Save shortcut (Cmd+S, or Ctrl+S on Windows and Linux). The editor saves nothing itself, so answer it by reading the document and writing it, with the same function the app’s own Save menu item calls.

// Cmd/Ctrl+S in the editor. The editor saves nothing itself.
editor.on("saveRequested", saveNow);
apps/electron-sample/main.mjs, lines 260 to 261

interface TacoDesignEvents

source

The events of a TacoDesignView, each with the listener it calls. The names are what TacoDesignView.on takes.

design: (event: DesignEvent) => voidsource

Stamped invalidations for the current page, not a resumable synchronization log.

change: (change: TacoDesignChange) => voidsource

The document changed. Changes by the person using the editor arrive at most once per 100 ms, and the last one always arrives. Ask for the document with getDocument() when you save.

selection: (selection: SelectionSummary) => voidsource

Other layers are selected, another page is shown, or a selected layer was renamed. Arrives at most once per 100 ms, and the last one always arrives.

focus: (focus: FocusSummary) => voidsource

The keyboard moved: into or out of the editor, or between its canvas, a text field and the rest.

failure: (error: TacoDesignError) => voidsource

The editor stopped working after ready, or could not be loaded again. error.code says how. It worked and stopped: renderer-failed (the canvas stopped drawing; detail is the renderer’s own code), editor-crashed (the editor shows its crash screen) or page-crashed (the page’s process ended). A reload failed: any code ready can reject with (load-failed, app-files-missing, ready-timeout, renderer-failed, page-crashed and the others); a reload() that was waiting rejects with the same error. reload() brings the editor back.

reloaded: (document: TacoDesignReloaded) => voidsource

The editor’s page was loaded again (its crash screen’s Reload button, or reload()) and is ready as it is after ready: the editor answers and its renderer has drawn. handedOver says which document it shows.

saveRequested: () => voidsource

The Save keys were pressed while the editor had the keyboard (the command file.save: Cmd+S on macOS and Ctrl+S elsewhere, unless the shortcut was changed). The editor saves nothing itself: read the document with getDocument() and store it.

startupProgress: (progress: TacoDesignStartupProgress) => voidsource

How far the editor’s start-up is: a fraction, and the step in progress. At most ten arrive per second. They can only start once the page has its connection, so the first one already counts what happened before. The last one is { fraction: 1, step: "done" } and arrives before ready resolves. A start that fails has no last report: ready rejects instead, and no report of that page load follows the rejection, so loading is over when ready settles either way. A page that is loaded again reports its progress again, in the same way: the last report arrives before reloaded is told and reload() resolves, and a reload that fails has no last report and tells failure instead.

interface TacoDesignChange extends DocumentInfo

source

What a change listener gets: a few facts about the document, never the document.

readonly origin: "user" | "host"source

"host": caused by setDocument() or newDocument(). "user": everything else (edits, undo and redo, and changes the editor makes by itself after an edit).

Export

TacoDesignView.exportLayers draws one layer or one whole page through the editor’s own export, so the file is what the editor’s Export section would download. It resolves with a list that holds one file. One PNG or JPEG may have at most 8192 x 8192 pixels.

interface TacoDesignExportOptions

source

What exportLayers() takes: one layer or one page, a format, and the choices of the editor’s own Export section.

readonly nodeId?: stringsource

The layer to export, with everything inside it. Give this or pageId.

readonly pageId?: stringsource

The page to export whole. Give this or nodeId.

readonly format: HostExportFormatsource

The file format: "png", "jpeg", "svg" or "pdf".

readonly scale?: numbersource

PNG and JPEG: pixels per document unit. Default 1. Give at most one of scale, width and height.

readonly width?: numbersource

PNG and JPEG: a fixed width in whole pixels.

readonly height?: numbersource

PNG and JPEG: a fixed height in whole pixels.

readonly quality?: "low" | "medium" | "high"source

JPEG: compression. PDF: resolution of effects that cannot be drawn as shapes. Default: high for JPEG, medium for PDF.

readonly resampling?: "detailed" | "basic"source

How pictures inside the layer are sampled. Default "detailed".

readonly ignoreOverlappingLayers?: booleansource

For a layer: true (default) draws the layer alone, false also what else lies within its bounds.

readonly includeIdAttribute?: booleansource

SVG: id attributes made from layer names. Default false.

readonly suffix?: stringsource

Added to the file name after the layer’s or page’s name.

readonly timeoutMs?: numbersource

How long to wait for the export. Default 120 000 ms.

type HostExportFormat = "png" | "jpeg" | "svg" | "pdf"

source

The file formats an export can be written in.

interface TacoDesignExportedFile

source

One exported file.

readonly fileName: stringsource

<layer or page name><suffix>.<extension>, safe as a file name.

readonly mimeType: stringsource

image/png, image/jpeg, image/svg+xml or application/pdf.

readonly bytes: Uint8Arraysource

The file’s bytes; write them with fs.writeFile.

Selection, zoom and focus

TacoDesignView.getSelection and the selection event say which page is shown and which layers are selected. There is no call that sets the selection. TacoDesignView.zoomToFit does what the editor’s “Zoom to fit” and “Zoom to selection” do. TacoDesignView.focusCanvas gives the keyboard to the canvas, and the focus event says where the keyboard is.

interface SelectionSummary

source

The page that is shown and the selected layers: the answer to a getSelection request, and what a selection event carries.

readonly pageId: string | nullsource

The page that is shown, or null when the document has none.

readonly nodes: readonly SelectedLayer[]source

The selected layers, in the order they were selected. Empty when nothing is selected.

interface SelectedLayer

source

One selected layer.

readonly id: stringsource

The ID the document knows the layer by. A layer inside an instance has an ID made of the instance’s ID and the layer’s place inside it.

readonly name: stringsource

The layer’s name, as the layers panel shows it.

readonly type: stringsource

The layer’s kind, the same word as its type in the document: "frame", "group", "text", "shape", "image", "instance" and so on. An instance is "instance" wherever it is: on a page, inside a component, or inside another instance. A component’s root is the frame it is stored as.

readonly shape?: stringsource

For a layer of type "shape": which shape, the same word as its shape in the document ("rectangle", "ellipse", "line", "path", ...).

readonly componentId?: stringsource

For a layer of type "instance": the ID of the component it shows, as in the document’s components. For an instance inside another instance this is the component in effect after a swap by the outer one.

readonly componentName?: stringsource

For a layer of type "instance": the name of that component.

interface FocusSummary

source

Where the keyboard is: the answer to a focusCanvas request, and what a focus event carries.

readonly focused: booleansource

True while the editor’s page has the keyboard.

readonly target: "canvas" | "text" | "other" | nullsource

What has the keyboard inside the page: "canvas"; "text" (a text field, or text being edited on the canvas), where keys such as copy and undo belong to the text; "other" (a button, a panel, or nothing in particular). Null while the page does not have the keyboard.

Reload and failures

Every failure is a TacoDesignError with a code. Before the editor is ready it rejects TacoDesignView.ready. Afterwards it reaches the failure listeners: the renderer stopped, the editor crashed, or the page’s process ended. TacoDesignView.reload loads the editor again; where the page can still answer, it hands its document over first, so no edit is lost.

A call the editor refuses rejects with a TacoDesignError too, and changes nothing: revision-changed from TacoDesignView.setDocument or TacoDesignView.newDocument with ifRevision, and the shortcut codes from TacoDesignView.setShortcuts (unknown-shortcut-command, invalid-shortcut-keys, shortcut-keys-in-use, shortcut-not-customizable). TacoDesignErrorCode lists every code.

There is no automatic retry. When TacoDesignView.ready rejected, dispose the view and make a new one. Do not call TacoDesignView.reload from a failure listener without a limit, because a failure that comes back at once would reload for ever.

interface TacoDesignReloaded extends DocumentInfo

source

What a reloaded listener gets, and what reload() resolves with.

readonly handedOver: booleansource

True: the page handed over its document right before it was loaded again, so the editor shows the latest work. False: it could not (its process had ended, for example), and the editor shows the last document this app set or read; edits made after that are lost.

class TacoDesignError extends Error

source

The error ready rejects with, and the package’s functions throw.

readonly code: TacoDesignErrorCodesource

What went wrong. Decide by this, never by the message, which is written for a person.

readonly url: string | undefinedsource

The address that was being loaded.

readonly errorCode: number | undefinedsource

Chromium’s network error number, such as -2 for ERR_FAILED.

readonly errorDescription: string | undefinedsource

Chromium’s network error name, such as ERR_FAILED.

readonly httpStatus: number | undefinedsource

The HTTP status the page answered with.

readonly rendererErrorCode: string | undefinedsource

The editor’s own renderer error code, such as WEBGPU_UNAVAILABLE.

readonly path: string | undefinedsource

Where a document is wrong, as a JSON path such as $.nodes.n1.opacity.

readonly detail: string | undefinedsource

The document model’s own error code, such as INVALID_SCHEMA; for renderer-failed after ready, the renderer’s own code.

new ( code: TacoDesignErrorCode, message: string, details: TacoDesignErrorDetails = {} )source

Makes an error from a code, a message written for a person, and the extra facts that are known.

type TacoDesignErrorCode = | "app-files-missing" | "load-failed" | "scheme-not-registered" | "scheme-registered-too-late" | "scheme-handler-conflict" | "app-directory-invalid" | "renderer-failed" | "editor-failed" | "page-crashed" | "ready-timeout" | "disposed" | "invalid-document" | "schema-too-new" | "protocol-mismatch" | "not-ready" | "unknown-method" | "request-aborted" | "request-timeout" | "connection-closed" | "editor-crashed" | "invalid-request" | "unknown-layer" | "unknown-page" | "unsupported-format" | "export-too-large" | "export-failed" | "revision-changed" | "unknown-shortcut-command" | "invalid-shortcut-keys" | "shortcut-keys-in-use" | "shortcut-not-customizable" | "invalid-option" | "internal"

source

Why the editor could not be shown.

  • app-files-missing: the folder of editor files has no index.html (the package was not assembled, or appDirectory is wrong).
  • load-failed: Electron could not load the page (errorCode and errorDescription are Chromium’s), or the page answered with an HTTP error status (httpStatus).
  • scheme-not-registered: the page is not a secure context, which means tacoDesignScheme was not registered before the app was ready.
  • scheme-registered-too-late: registerTacoDesignScheme() ran after the app was ready.
  • scheme-handler-conflict: the session already serves the scheme from another folder.
  • app-directory-invalid: the appDirectory option is not an absolute path of a folder, or is the root of a disk.
  • renderer-failed: the page loaded but the WebGPU renderer did not start (rendererErrorCode is the editor’s own code, such as WEBGPU_UNAVAILABLE).
  • editor-failed: the page loaded but the editor showed its start-up error screen (for example its stored data could not be opened).
  • page-crashed: the page’s process ended. Before ready it rejects ready; afterwards it reaches the failure listeners.
  • ready-timeout: the editor did not report ready in time. reload() rejects with it when the page that was loaded again did not connect, or did not report ready, in that time.
  • disposed: dispose() was called, or the view was destroyed, first.

From the editor, about a document or a request (see the host protocol):

  • invalid-document: the document handed over is not one Taco Design can open. path says where it is wrong (a JSON path such as $.title) and detail holds the document model’s own code.
  • schema-too-new: the document was written by a newer Taco Design.
  • protocol-mismatch: this package and the editor files speak no common version of the host protocol (they come from different releases).
  • not-ready: the editor is not shown (it failed, or is reloading), so the request was not run.
  • unknown-method: the editor files are older than this package and do not know the request.
  • request-timeout: the editor did not answer a request in time.
  • request-aborted: the caller stopped waiting for a design request.
  • connection-closed: the editor’s page went away (it reloaded or crashed) before it answered.
  • internal: the editor failed in a way it has no code for.

From exportLayers():

  • unknown-layer, unknown-page: the document has no such layer on a page, or no such page.
  • unsupported-format: the format is not PNG, JPEG, SVG or PDF.
  • invalid-request: an option has a value the editor cannot use (two sizes at once, a scale of 0, ...). Other requests use it too.
  • export-too-large: the picture would have more than 8192 x 8192 pixels.
  • export-failed: the renderer could not draw or encode the export; the message says why (fonts still loading, for example).

To the failure listeners, after ready:

  • renderer-failed: the canvas stopped drawing (detail is the renderer’s own code).
  • editor-crashed: the editor threw and shows its crash screen.
  • page-crashed: see above.

interface TacoDesignErrorDetails

source

The extra facts a TacoDesignError can carry besides its code and its message. Which of them are set depends on the code.

readonly url?: stringsource

The address that was being loaded.

readonly errorCode?: numbersource

Chromium’s network error number, such as -2 for ERR_FAILED.

readonly errorDescription?: stringsource

Chromium’s network error name, such as ERR_FAILED.

readonly httpStatus?: numbersource

The HTTP status the page answered with.

readonly rendererErrorCode?: stringsource

The editor’s own renderer error code, such as WEBGPU_UNAVAILABLE.

readonly path?: stringsource

Where a document is wrong, as a JSON path such as $.nodes.n1.opacity.

readonly detail?: stringsource

The document model’s own error code, such as INVALID_SCHEMA.

Start-up progress

While the editor starts it shows a slim progress bar of its own, and it tells the app the same thing with the startupProgress event. An app that draws the progress itself, in a splash screen for example, passes TacoDesignViewOptions.startupProgress as "host". Loading is over when TacoDesignView.ready settles: a start that fails sends no last report.

interface TacoDesignStartupProgress

source

What a startupProgress listener gets.

readonly fraction: numbersource

The share of start-up that is finished, 0 to 1. It never goes down within one page load.

readonly step: stringsource

The step in progress, in order: page-code, fonts, document, module-download, module-create, creating-session, installing-page, and done in the last report. Later versions may add steps, so show label and do not switch on this.

readonly label: stringsource

A few plain words for the step, such as “Downloading the renderer”.

readonly indeterminate: booleansource

True while the step in progress cannot say how long it is (a download without a known size). fraction then stays where it was: show a bar that moves without a percentage.

type TacoDesignStartupProgressDrawer = "editor" | "host"

source

The option that says who draws the progress while the editor starts.

Keyboard and menus

While the editor has the keyboard, the editor’s shortcuts are the editor’s: Cmd+Z undoes on the canvas, Cmd+C and Cmd+V copy and paste layers, Cmd+D duplicates, the letter keys choose tools, even when an item of the app’s menu has an accelerator on the same keys. (Cmd is Ctrl on Windows and Linux.) An app can change that in three ways: give a command other keys, switch a command’s shortcut off, and choose who gets a key that its menu wants too.

/** The override the sample's "Duplicate on Cmd/Ctrl+Shift+D" item switches on. */
export const DUPLICATE_REMAP = /** @type {const} */ ({ "edit.duplicate": "CmdOrCtrl+Shift+D" });
apps/electron-sample/appMenu.mjs, lines 30 to 31
const editor = createTacoDesignView({
  document: startDocument,
  startupProgress: process.argv.includes("--host-progress") ? "host" : "editor",
  menuShortcutConflict: startConflict,
  shortcuts: startRemapped ? DUPLICATE_REMAP : {},
});
apps/electron-sample/main.mjs, lines 136 to 141

Changing the editor’s shortcuts

The editor’s shortcuts are a list of commands. Each has a stable ID such as edit.duplicate, a title, its default keys and the keys in use now (ShortcutCommandInfo). TacoDesignView.getShortcuts returns the list; the package’s README has it as a table. The IDs are part of the package’s API: they are not renamed or reused.

TacoDesignViewOptions.shortcuts and TacoDesignView.setShortcuts take the same object (ShortcutOverrides): a command ID mapped to its new keys, to a list of keys, or to null to switch the shortcut off. It is always the whole set of the app’s changes: a command that is not named has its default keys, and setShortcuts({}) puts every command back. Keys are written like Electron accelerators, for example CmdOrCtrl+Shift+D.

type ShortcutOverrides =

source

An embedding app’s changes to the editor’s shortcuts: a command ID mapped to its new key combination, to a list of them, or to null to switch the shortcut off. Commands that are not named keep their default keys.

readonly [commandId: string]: string | readonly string[] | nullsource

interface ShortcutCommandInfo

source

One command of the editor’s shortcut list.

Key combinations are written like Electron accelerators, for example CmdOrCtrl+Shift+D; shortcuts.ts describes the spelling.

readonly id: stringsource

The stable name of the command, for example edit.duplicate. Part of the public API.

readonly title: stringsource

What the command does, in a few words of English.

readonly defaultKeys: readonly string[]source

The keys the command has when the embedding app changes nothing.

readonly keys: readonly string[]source

The keys in use now. Empty when the shortcut is switched off.

readonly customizable: booleansource

False for a command whose keys cannot be changed or switched off.

A key that is an editor shortcut and a menu accelerator

TacoDesignViewOptions.menuShortcutConflict, and TacoDesignView.setMenuShortcutConflict at run time, say what happens to a key press that runs an editor command and is also the accelerator of an enabled, visible item of the application menu, while the editor has the keyboard. With "editor" (the default) the editor’s command runs and the menu item does not. With "app" the menu item runs and the editor does not see the key. With "both" the menu item runs first, then the editor’s command.

type MenuShortcutConflict = "editor" | "app" | "both"

source

What happens to a key press that is an editor shortcut and also an accelerator of an item in the app’s menu, while the editor has the keyboard:

  • "editor" (the default): the editor runs its command; the menu item does not run.
  • "app": the menu item runs; the editor does not see the key.
  • "both": the menu item runs and the editor runs its command.

const MENU_SHORTCUT_CONFLICTS: readonly MenuShortcutConflict[]

source

Every value menuShortcutConflict can have.

A menu for an app that shows the editor

The File, Edit and View items of the sample app’s menu template:

{
  label: "File",
  submenu: [
    { id: SAVE_MENU_ID, label: "Save", accelerator: "CmdOrCtrl+S", click: () => options.save() },
    { type: "separator" },
    mac ? { role: "close" } : { role: "quit" },
  ],
},
{
  label: "Edit",
  submenu: [
    { id: UNDO_MENU_ID, label: "Undo", accelerator: "CmdOrCtrl+Z", click: history("undo") },
    { id: REDO_MENU_ID, label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", click: history("redo") },
    { type: "separator" },
    { role: "cut" },
    { role: "copy" },
    { role: "paste" },
    { role: "selectAll" },
  ],
},
// No zoom roles here: they would scale the whole view.
{ label: "View", submenu: [{ role: "togglefullscreen" }] },
apps/electron-sample/appMenu.mjs, lines 89 to 110

The guide’s step Keyboard and menus builds the whole menu. The package’s README says how each setting is carried out in Electron, and what is not proven on Windows and Linux.

Security settings and versions

createTacoDesignView sets all of this, so the app does not have to: the page runs in a sandbox with context isolation and without Node.js, in its own storage partition; it may only move inside taco-design://app/ and may open no window; every answer carries the content security policy below; and only the listed permissions are granted, only to the editor’s own page.

const CONTENT_SECURITY_POLICY: string

source

Sent with every answer. 'wasm-unsafe-eval' lets the renderer compile; 'unsafe-inline' styles are for the editor’s inline styles. There is no 'unsafe-eval' and no inline script. https://fonts.gstatic.com is the only other site, used to fetch a Google font the first time a document needs it.

const ALLOWED_PERMISSIONS: readonly string[]

source

The only permissions the editor page is given: listing installed fonts, the clipboard, and keeping its storage from being cleared under disk pressure. Everything else (camera, microphone, location, notifications, ...) is refused.

const DEFAULT_PARTITION: "persist:taco-design"

source

The partition used when the app names none. persist: keeps it on disk.

const HOST_PROTOCOL_VERSION: 1

source

Counts up by one when a change would confuse an older host or page.

These additions do not count, so they keep the number: a new request (a page that does not know it answers unknown-method), a new event (a host skips events it does not know), a new optional parameter or result field, and a new error code (it arrives as a string with its message). Changing or removing a message, or changing what an existing one means, counts.

Still 1 after the export, selection, view and reload messages were added, and after the shortcut requests (getShortcuts, setShortcuts), the saveRequested event and the four shortcut error codes, and after the newDocument request.

Design reads and plans

TacoDesignView.designRequest uses the explicit binding advertised by getState().design. createDesignClient validates operation arguments and results with the shared catalog. Public mutations remain disabled; geometry is stored model geometry. design events are stamped invalidations, not a resumable log.

const DESIGN_PROTOCOL_VERSION: "taco.design/1.0.0"

source

Version of the design envelope, independent of document schema and renderer wire versions.

function createDesignClient( send: (request: DesignRequest) => Promise<DesignReply>, target: DesignBinding, requestId: () => string )

source

A typed transport adapter; the host supplies the binding returned by discovery.

class DesignError extends Error

source
readonly committed: falsesource

Read and planning failures never commit a document change.

new (readonly code: string, message: string, readonly path = "$")source

type DesignArguments< K extends DesignOperation > = Infer<(typeof operations)[K]["input"]>

source

Arguments validated by the catalog for an operation.

type DesignBinding = Infer<typeof bindingSchema>

source

Identifies one editor and one document opening; never substitute a new epoch on retry.

interface DesignCapability

source

Capability advertised by getState; public mutation remains disabled.

readonly versions: readonly string[]source
readonly binding: DesignBindingsource
readonly mutationEnabled: falsesource

type DesignCommand = Infer<typeof commandSchema>

source

A duplicate or translate command accepted by the read-only planner.

interface DesignEvent

source

Stamped invalidation; not a resumable synchronization log.

readonly binding: DesignBindingsource
readonly revision: numbersource
readonly kind: "reset" | "transition"source

type DesignOperation = keyof typeof operations

source

Operations supported by this version of the read and planning catalog.

type DesignReply = | { readonly ok: true; readonly requestId: string; readonly result: unknown } | { readonly ok: false; readonly requestId: string; readonly error: { readonly code: string; readonly message: string; readonly path: string; readonly committed: false; }; }

source

Domain errors retain their code, path and committed flag, separate from transport failures.

type DesignRequest = { [K in DesignOperation]: { readonly protocol: typeof DESIGN_PROTOCOL_VERSION; readonly requestId: string; readonly target: DesignBinding; readonly operation: K; readonly arguments: DesignArguments<K>; } }[DesignOperation]

source

A versioned request with an explicit document-opening target.

type DesignResult< K extends DesignOperation > = Infer<(typeof operations)[K]["output"]>

source

Result validated by the catalog for an operation.

type DesignTarget = Infer<typeof targetReferenceSchema>

source

A canonical node or a layer within a component instance.

interface TacoDesignRequestOptions

source

Limits the entire design call, including startup and capability discovery.

readonly timeoutMs?: numbersource

Default 30 seconds. Must be a positive integer no greater than 2,147,483,647.

readonly signal?: AbortSignalsource

Stops waiting and prevents unstarted requests; does not undo a completed remote plan.

C++ renderer C++

157 of 310 entries documented · outline

The C++ renderer in packages/render-core/cpp has one public surface: the C functions and types in include/taco/render_core.h. Everything else on this page is internal C++ that those functions call. The C ABI is plain C, so the same header serves the native build, the tests and the Wasm builds.

All work happens on a session. taco_render_session_create makes an empty one, taco_render_session_apply feeds it binary scene batches (see PROTOCOL.md), and the query, render and overlay functions read it. Every function that can fail returns a TacoRenderStatus; most failures also leave a message for taco_render_session_copy_last_error. Output structs start with a struct_size field that the caller sets, and calls that fill caller buffers report the sizes they need, returning TACO_RENDER_STATUS_BUFFER_TOO_SMALL when a buffer is short. Calls that target an editing state take the scene generation they expect and return TACO_RENDER_STATUS_STALE_GENERATION when the scene has moved on.

The WebGPU Wasm build exports these taco_render_* functions through Emscripten, along with malloc, free and its own taco_render_webgpu_* helpers. The TypeScript package calls them as module._taco_render_session_apply and so on, passing pointers into the Wasm heap. A smaller portable Wasm build has no Skia and no painting; it exports the taco_render_wasm_* functions listed under “Wasm bridge” instead.

Behind the C functions, RetainedSession holds the scene and its computed geometry, and SessionCache holds the Skia side: fonts, text layouts, media frames, overlays and incremental-render damage. Builds without Skia keep the scene and geometry queries but return TACO_RENDER_STATUS_CAPABILITY_UNAVAILABLE from anything that paints or lays out text.

Versions and capabilities

Version constants for the ABI and the wire formats, and the two calls that report what this build can do. Extended capabilities grow by appending fields, so callers ask for the version they understand.

#define TACO_RENDER_ABI_VERSION 1u

source

Version of this C ABI. TacoRenderSessionOptions.abi_version must equal it, and TacoRenderCapabilities.abi_version reports it.

TypeScript: RENDER_CORE_ABI_VERSION

#define TACO_RENDER_WIRE_VERSION 24u

source

Newest retained scene/change batch wire version. A batch may declare any version from 1 up to this one; newer versions fail with UNSUPPORTED_VERSION.

TypeScript: RENDER_CORE_WIRE_VERSION

#define TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION 5u

source

Headerless standalone VectorNetwork payload version.

TypeScript: RENDER_CORE_VECTOR_NETWORK_WIRE_VERSION

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION 8u

source

Newest version accepted by taco_render_get_extended_capabilities. Each version N has a matching _VERSION_N and _VERSION_N_SIZE (the struct size that version needs).

TacoRenderStatus taco_render_get_capabilities( TacoRenderCapabilities *capabilities ) noexcept

wasmsource

Fills the version-1 capability struct. The caller sets struct_size; it must be at least sizeof(TacoRenderCapabilities), or the call returns INVALID_ARGUMENT. Needs no session.

Called from TS, in helpers of: PortableWasmRenderCoreModule.constructor, createOwnedWasmRenderCoreSession, attachPortableWasmRenderCoreSession

struct TacoRenderCapabilities

source

What this build supports, written by taco_render_get_capabilities. The caller sets struct_size. Each uint8_t field is 1 when supported. The Skia fields (skia, native_raster, skparagraph, harfbuzz, icu) are 1 only in Skia builds; wasm and webgpu are always written as 0 by this call. Newer features are reported by TacoRenderExtendedCapabilities instead.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, RenderCoreCapabilities

uint32_t struct_sizesource
uint32_t abi_versionsource
uint32_t wire_versionsource
uint8_t retained_scenesource
uint8_t structured_pathssource
uint8_t styled_text_recordssource
uint8_t embedded_resourcessource
uint8_t skiasource
uint8_t native_rastersource
uint8_t wasmsource
uint8_t webgpusource
uint8_t skparagraphsource
uint8_t harfbuzzsource
uint8_t icusource
uint8_t geometry_queriessource
uint8_t hit_testingsource
uint8_t dirty_boundssource
uint8_t[2] reservedsource

TacoRenderStatus taco_render_get_extended_capabilities( uint32_t capabilities_version, TacoRenderExtendedCapabilities *capabilities ) noexcept

wasmsource

Fills the extended capabilities up to capabilities_version (1 to TACO_RENDER_EXTENDED_CAPABILITIES_VERSION). Only the first TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_N_SIZE bytes are written, so an older caller with a smaller struct stays safe. Returns UNSUPPORTED_VERSION for an unknown version and INVALID_ARGUMENT when struct_size is smaller than that version’s size. Needs no session.

Called from TS, in helpers of: PortableWasmRenderCoreModule.constructor, createOwnedWasmRenderCoreSession, attachPortableWasmRenderCoreSession

struct TacoRenderExtendedCapabilities

source

Append-only capabilities that are intentionally separate from the frozen ABI-version-1 TacoRenderCapabilities layout. Callers request one declared version and provide struct_size; all reserved bytes are written as zero.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, RenderCoreCapabilities

uint32_t struct_sizesource
uint32_t capabilities_versionsource
uint32_t text_query_versionsource
uint8_t text_caretssource
uint8_t text_range_rectssource
uint8_t text_hit_testingsource
uint8_t text_navigationsource
uint8_t text_layout_metricssource
uint8_t text_editing_overlayssource
uint8_t[14] reservedsource
uint32_t vector_query_versionsource

Version 2 append-only fields. Version 1 callers expose only the first 32 bytes.

uint8_t vector_geometrysource
uint8_t vector_hit_testingsource
uint8_t[2] reserved_v2source
uint32_t vector_editing_overlay_versionsource

Version 3 append-only fields. Version 2 callers expose only the first 40 bytes.

uint8_t vector_editing_overlayssource
uint8_t[3] reserved_v3source
uint32_t vector_boolean_versionsource

Version 4 append-only fields. Version 3 callers expose only the first 48 bytes.

uint8_t vector_boolean_operationssource
uint8_t[3] reserved_v4source
uint32_t font_metadata_versionsource

Version 5 append-only fields. Version 4 callers expose only the first 56 bytes.

uint8_t font_metadatasource
uint8_t[3] reserved_v5source
uint32_t incremental_render_versionsource

Version 6 append-only fields. Version 5 callers expose only the first 64 bytes.

uint8_t incremental_renderingsource
uint8_t render_diagnosticssource
uint8_t[2] reserved_v6source
uint32_t static_image_frame_versionsource

Version 7 append-only fields. Version 6 callers expose only the first 72 bytes.

uint8_t static_image_framessource
uint8_t[3] reserved_v7source
uint32_t video_frame_versionsource

Version 8 append-only fields. Version 7 callers expose only the first 80 bytes.

uint8_t video_framessource
uint8_t[3] reserved_v8source

Sessions and status codes

A session owns one retained scene. Create it, destroy it, and read the message behind a failed status.

struct TacoRenderSession

opaquesource

An opaque renderer session: one retained scene plus, in Skia builds, the caches, frames and overlays used to paint it. Create it with taco_render_session_create and free it with taco_render_session_destroy. A session is not thread-safe; call it from one thread at a time.

TacoRenderStatus taco_render_session_create( const TacoRenderSessionOptions *options, TacoRenderSession **session ) noexcept

wasmsource

Creates an empty session and stores it in *session (NULL on failure). options may be NULL. When given, a different abi_version returns UNSUPPORTED_VERSION and a struct_size smaller than sizeof(TacoRenderSessionOptions) returns INVALID_ARGUMENT.

Called from TS: InteractiveRetainedBackend.exportCompiledScene, InteractiveRetainedBackend.outlineCompiledStrokes

Called from TS, in helpers of: RenderCoreModule.createSession, PortableWasmRenderCoreModule.createSession, createOwnedWasmRenderCoreSession

struct TacoRenderSessionOptions

source

Optional settings for taco_render_session_create. Set struct_size to sizeof(TacoRenderSessionOptions) and abi_version to TACO_RENDER_ABI_VERSION. There are no other options yet.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t abi_versionsource
uint32_t[6] reservedsource

void taco_render_session_destroy(TacoRenderSession *session) noexcept

wasmsource

Frees a session and everything it owns. NULL is allowed.

Called from TS: RenderCoreSession.dispose, RenderCoreModule.createSession, PortableWasmRenderCoreModule.createSession, createOwnedWasmRenderCoreSession

Called from TS, through RenderCoreSession.dispose: outlineStrokesInThrowawaySession

enum TacoRenderStatus

source

The result of every C function that can fail. Most failures also store a message that taco_render_session_copy_last_error returns; taco_render_status_name gives the constant’s name.

TypeScript: RENDER_CORE_STATUS

TACO_RENDER_STATUS_OK = 0source
TACO_RENDER_STATUS_INVALID_ARGUMENT = 1source

A null pointer, a struct_size that is too small, or a bad value.

TACO_RENDER_STATUS_UNSUPPORTED_VERSION = 2source

An ABI, wire or payload version this build does not accept.

TACO_RENDER_STATUS_MALFORMED_BATCH = 3source

Batch bytes that are truncated or do not follow the wire format.

TACO_RENDER_STATUS_LIMIT_EXCEEDED = 4source

A size, count or byte limit was exceeded.

TACO_RENDER_STATUS_INVALID_SCENE = 5source

The scene, or data the call needs from it, is not valid.

TACO_RENDER_STATUS_NOT_FOUND = 6source

The named node or resource does not exist.

TACO_RENDER_STATUS_CAPABILITY_UNAVAILABLE = 7source

This build cannot do it, for example painting without Skia.

TACO_RENDER_STATUS_BUFFER_TOO_SMALL = 8source

An output buffer is too small; the required sizes are still written.

TACO_RENDER_STATUS_INTERNAL_ERROR = 9source

A native failure, such as an allocation failure or an exception.

TACO_RENDER_STATUS_STALE_GENERATION = 10source

The call’s expected_generation is not the scene’s current generation.

size_t taco_render_session_copy_last_error( const TacoRenderSession *session, char *destination, size_t destination_size ) noexcept

wasmsource

Copies the session’s last error message as a NUL-terminated string and returns the required byte count including the trailing NUL. The copy is cut to destination_size - 1 bytes; pass 0 to ask for the size. Most successful calls clear the message, but not every failure sets one, so check the status first. A NULL session gives an empty message.

Called from TS, in helpers of: RenderCoreSession.apply, RenderCoreSession.applyWithStaticImageFrames, RenderCoreSession.applyWithMediaFrames, RenderCoreSession.updateVideoFrames, RenderCoreSession.getSceneInfo, RenderCoreSession.getNodeGeometry, RenderCoreSession.getGeometrySnapshot, PortableWasmRenderCoreSession.exportScene

and 21 moreRenderCoreSession.hitTest, RenderCoreSession.getTextCarets, RenderCoreSession.getTextRangeGeometry, RenderCoreSession.hitTestText, RenderCoreSession.navigateText, RenderCoreSession.getTextLayoutMetrics, RenderCoreSession.getFontMetadata, RenderCoreSession.setTextEditingOverlay, RenderCoreSession.setSelectionChrome, RenderCoreSession.setCanvasOverlayFont, RenderCoreSession.setCanvasOverlay, RenderCoreSession.clearTextEditingOverlay, RenderCoreSession.getVectorGeometry, RenderCoreSession.hitTestVector, RenderCoreSession.setVectorEditingOverlay, RenderCoreSession.clearVectorEditingOverlay, PortableWasmRenderCoreSession.outlineStroke, RenderCoreSession.booleanVectorPaths, RenderCoreSession.mapViewport, RenderCoreSession.render, RenderCoreSession.getLastError

Called from TS, through RenderCoreSession.apply: outlineStrokesInThrowawaySession

Called from TS, through RenderCoreSession.getSceneInfo: outlineStrokesInThrowawaySession, measureExportFromLoadedSession

Called from TS, through PortableWasmRenderCoreSession.outlineStroke: outlineStrokesInThrowawaySession

Called from TS, through RenderCoreSession.getNodeGeometry: measureExportFromLoadedSession

Called from TS, through measureExportFromLoadedSession: exportFromLoadedSession

Called from TS, through PortableWasmRenderCoreSession.exportScene: exportFromLoadedSession

const char *taco_render_status_name(TacoRenderStatus status) noexcept

source

Returns a static name for a status, such as “OK” or “STALE_GENERATION”, or “UNKNOWN_STATUS” for a value outside TacoRenderStatus.

Scene updates

A batch changes the scene all at once or not at all. Scenes with images or videos also pass their decoded frames, so pixels and scene always match the same generation.

TacoRenderStatus taco_render_session_apply( TacoRenderSession *session, const uint8_t *batch, size_t batch_size, TacoRenderUpdateReport *report ) noexcept

wasmsource

Applies one complete retained-scene batch (the “TCR1” wire format in PROTOCOL.md). The batch is all or nothing: on failure the scene is unchanged, the report is not written, and the status is usually MALFORMED_BATCH, UNSUPPORTED_VERSION, LIMIT_EXCEEDED or INVALID_SCENE. report may be NULL. A batch with at least one command raises the generation by one.

In Skia builds a successful apply also drops every installed image and video frame, so scenes with media use taco_render_session_apply_with_media_frames instead, and a generation change clears the text and vector editing overlays.

Called from TS: InteractiveRetainedBackend.apply, InteractiveRetainedBackend.exportCompiledScene, InteractiveRetainedBackend.outlineCompiledStrokes, RenderChangeBatchEncoder, RenderCoreSession.apply

Called from TS, through InteractiveRetainedBackend.apply: InteractiveRenderSession.loadDocument, InteractiveRenderSessionCoordinator.loadDocument, InteractiveRenderSession.applyDocumentUpdate, InteractiveRenderSessionCoordinator.applyDocumentUpdate, InteractiveRenderSession.setActivePage, InteractiveRenderSessionCoordinator.setActivePage

Called from TS, through RenderCoreSession.apply: outlineStrokesInThrowawaySession

Implemented by: RetainedSession.state, RetainedSession.apply, SessionCache.recordSuccessfulApply, SessionCache.invalidateMediaFrames, SessionCache.recordSceneUpdateTime

struct TacoRenderUpdateReport

source

Written after a successful batch apply. commands_applied counts the batch’s commands; node_count and resource_count describe the scene after the batch. generation goes up by one for every batch with at least one command, and scene_digest is a hash of the retained scene contents.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, RenderUpdateReport

uint32_t struct_sizesource
uint32_t commands_appliedsource
uint32_t node_countsource
uint32_t resource_countsource
uint64_t generationsource
uint64_t scene_digestsource

TacoRenderStatus taco_render_session_apply_with_static_image_frames( TacoRenderSession *session, const uint8_t *batch, size_t batch_size, const TacoRenderStaticImageFrameSet *frame_set, TacoRenderUpdateReport *report ) noexcept

wasmsource

Atomically applies a retained-scene batch and installs the candidate generation’s exact static image set. The scene, image cache, pending damage, and report remain unchanged unless the entire transaction succeeds. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

frame_set->expected_generation must be the generation before the batch (else STALE_GENERATION). The frames must name every image asset the new scene uses, once each, in ascending asset-ID order; missing or extra IDs return INVALID_SCENE. A scene with video returns CAPABILITY_UNAVAILABLE; use taco_render_session_apply_with_media_frames for it.

Called from TS: InteractiveRetainedBackend.exportCompiledScene, RenderCoreSession.applyWithStaticImageFrames

Implemented by: RetainedSession.state, SessionCache.applyWithStaticImageFrames, SessionCache.recordSceneUpdateTime, isValidUtf8

TacoRenderStatus taco_render_session_apply_with_media_frames( TacoRenderSession *session, const uint8_t *batch, size_t batch_size, const TacoRenderMediaFrameSet *frame_set, TacoRenderUpdateReport *report ) noexcept

wasmsource

Atomically applies a retained-scene batch and installs that candidate generation’s exact mixed image/video frame set. No scene, frame-cache, damage, or report state is published unless the entire transaction succeeds.

The rules match taco_render_session_apply_with_static_image_frames, but frames may be images or videos (each record’s kind) and together name every media asset the new scene uses. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Called from TS: RenderCoreSession.applyWithMediaFrames

Implemented by: RetainedSession.state, SessionCache.applyWithMediaFrames, SessionCache.recordSceneUpdateTime

struct TacoRenderStaticImageFrameSet

source

expected_generation is the current generation before applying the batch.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource
const TacoRenderStaticImageFrame *framessource
size_t frame_countsource

struct TacoRenderStaticImageFrame

source

One member of a complete, logical-asset-ID-deduplicated static image set. content_revision is a caller-owned, stable, nonzero content identity.

REUSE_ACTIVE supplies only the identifier, kind, and revision; all pixel fields are zero/null and the active cache must contain the same revision. RGBA8888 supplies tightly packed, unpremultiplied RGBA8888 sRGB pixels. Version 1 requires this exact record size, row_bytes == width * 4, and an exact byte_length; the surrounding array has no independent record stride.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t dispositionsource
uint32_t kindsource
uint32_t reservedsource
const char *asset_idsource
size_t asset_id_sizesource
uint64_t content_revisionsource
uint32_t widthsource
uint32_t heightsource
uint32_t row_bytessource
uint32_t reserved_v1source
const uint8_t *pixelssource
size_t byte_lengthsource

TacoRenderStatus taco_render_session_update_video_frames( TacoRenderSession *session, const TacoRenderMediaFrameSet *frame_set, TacoRenderDirtyBounds *dirty_bounds ) noexcept

wasmsource

Replaces the exact video-frame set without changing retained scene identity. generation, digest, history, and editing authority remain unchanged. On success dirty_bounds contains the union of every visible user of a changed video asset. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Every frame must be a video frame, and the set must name each video asset of the scene once. It needs a current mixed set installed by taco_render_session_apply_with_media_frames for this generation; without one it returns INVALID_SCENE.

Called from TS: RenderCoreSession.updateVideoFrames

Implemented by: RetainedSession.state, SessionCache.updateVideoFrames

Scene queries

Read the scene back: its size and counts, one node’s values and geometry, every node’s geometry at once, point hits, the area the last update changed, and how the scene maps into a viewport.

TacoRenderStatus taco_render_session_get_scene_info( const TacoRenderSession *session, TacoRenderSceneInfo *info ) noexcept

wasmsource

Writes the scene’s size, counts, generation and digest.

Called from TS: InteractiveRetainedBackend.getSceneInfo, RenderCoreSession.getSceneInfo

Called from TS, in helpers of: RenderCoreSession.updateVideoFrames, RenderCoreSession.getTextCarets, RenderCoreSession.getTextRangeGeometry, RenderCoreSession.hitTestText

and 11 moreRenderCoreSession.navigateText, RenderCoreSession.getTextLayoutMetrics, RenderCoreSession.getFontMetadata, RenderCoreSession.setTextEditingOverlay, RenderCoreSession.clearTextEditingOverlay, RenderCoreSession.getVectorGeometry, RenderCoreSession.hitTestVector, RenderCoreSession.setVectorEditingOverlay, RenderCoreSession.clearVectorEditingOverlay, PortableWasmRenderCoreSession.outlineStroke, RenderCoreSession.booleanVectorPaths

Called from TS, through InteractiveRetainedBackend.getSceneInfo: InteractiveRenderSession.getSceneGeometrySnapshot, InteractiveRenderSession.getVectorGeometry, InteractiveRenderSession.hitTestVector, InteractiveRenderSession.loadDocument, InteractiveRenderSessionCoordinator.loadDocument, InteractiveRenderSession.applyDocumentUpdate, InteractiveRenderSessionCoordinator.applyDocumentUpdate, InteractiveRenderSession.setActivePage

and 18 moreInteractiveRenderSessionCoordinator.setActivePage, InteractiveRenderSessionCoordinator.getSceneGeometrySnapshot, InteractiveRenderSession.getTextGeometry, InteractiveRenderSessionCoordinator.getTextGeometry, InteractiveRenderSession.hitTestText, InteractiveRenderSessionCoordinator.hitTestText, InteractiveRenderSession.navigateText, InteractiveRenderSessionCoordinator.navigateText, InteractiveRenderSessionCoordinator.getVectorGeometry, InteractiveRenderSessionCoordinator.hitTestVector, InteractiveRenderSession.booleanVectorPaths, InteractiveRenderSessionCoordinator.booleanVectorPaths, InteractiveRenderSession.renderFrame, InteractiveRenderSessionCoordinator.renderFrame, InteractiveRenderSessionCoordinator.getSceneInfo, InteractiveRenderSessionCoordinator.getTextLayoutMetrics, InteractiveRenderSessionCoordinator.getFontMetadata, InteractiveRenderSessionCoordinator.resolveAccessibilityActionRouteV1

Called from TS, through RenderCoreSession.getSceneInfo: outlineStrokesInThrowawaySession, measureExportFromLoadedSession

Called from TS, through PortableWasmRenderCoreSession.outlineStroke: outlineStrokesInThrowawaySession

Called from TS, through measureExportFromLoadedSession: exportFromLoadedSession

Implemented by: RetainedSession.state, RetainedSession.digest

struct TacoRenderSceneInfo

source

The scene’s current size in scene units, its node and resource counts, generation and digest. Written by taco_render_session_get_scene_info.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, RenderSceneInfo

uint32_t struct_sizesource
uint32_t node_countsource
uint32_t resource_countsource
uint32_t reservedsource
double widthsource
double heightsource
uint64_t generationsource
uint64_t scene_digestsource

TacoRenderStatus taco_render_session_get_node_info( const TacoRenderSession *session, const char *node_id, size_t node_id_size, TacoRenderNodeInfo *info ) noexcept

source

Writes one node’s authored kind, z order, visibility, transform, opacity and widest stroke. node_id is node_id_size bytes of UTF-8, not NUL-terminated, and must not be empty. Returns NOT_FOUND for an unknown node.

Implemented by: RetainedSession.findNode, Node.kind, NodeStyle.maximumStrokeWidth

struct TacoRenderNodeInfo

source

A node’s authored values, written by taco_render_session_get_node_info. kind is a TacoRenderNodeKind. transform is the node’s own local transform as (a, b, c, d, e, f), where x’ = a*x + c*y + e and y’ = b*x + d*y + f. stroke_width is the widest of the node’s strokes, or 0 without strokes.

uint32_t struct_sizesource
uint32_t kindsource
int32_t z_ordersource
uint8_t visiblesource
uint8_t[3] reservedsource
double[6] transformsource
double opacitysource
double stroke_widthsource

TacoRenderStatus taco_render_session_get_node_geometry( const TacoRenderSession *session, const char *node_id, size_t node_id_size, TacoRenderNodeGeometry *geometry ) noexcept

wasmsource

Writes one node’s computed geometry: world transform, local, world and visible bounds, painter order and effective opacity. Returns NOT_FOUND for an unknown node.

Called from TS: InteractiveRetainedBackend.getNodeGeometry, RenderCoreSession.getNodeGeometry

Called from TS, through InteractiveRetainedBackend.getNodeGeometry: InteractiveRenderSession.getNodeGeometry, InteractiveRenderSessionCoordinator.getNodeGeometry

Called from TS, through RenderCoreSession.getNodeGeometry: measureExportFromLoadedSession

Called from TS, through measureExportFromLoadedSession: exportFromLoadedSession

Implemented by: RetainedSession.findNode, RetainedSession.geometrySnapshot

struct TacoRenderNodeGeometry

source

A node’s computed geometry, written by taco_render_session_get_node_geometry. Each has_* byte says whether the matching bounds are present. world_bounds are in scene coordinates; visible_world_bounds are also clipped by clipping ancestors and are absent when the node is not effectively visible. painter_order is the node’s back-to-front paint position, and effective_opacity and effectively_visible include every ancestor. subtree_visible_bounds joins the node’s visible bounds with those of all its descendants, the way they are painted: children of a clipping frame are clipped to it, a masked sibling is clipped to its mask, and a node’s layer blur spreads around its descendants too. So it covers everything the subtree paints. Callers built before it was appended may pass the shorter struct_size; the tail is then left unwritten.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, RenderNodeGeometry

uint32_t struct_sizesource
uint32_t kindsource
uint8_t has_local_boundssource
uint8_t has_world_boundssource
uint8_t has_visible_world_boundssource
uint8_t effectively_visiblesource
uint64_t painter_ordersource
double effective_opacitysource
double[6] world_transformsource
TacoRenderRect local_boundssource
TacoRenderRect world_boundssource
TacoRenderRect visible_world_boundssource
uint8_t has_subtree_visible_boundssource
uint8_t[7] reservedsource
TacoRenderRect subtree_visible_boundssource

TacoRenderStatus taco_render_session_get_geometry_snapshot( const TacoRenderSession *session, TacoRenderGeometryRecord *records, size_t record_capacity, char *node_id_bytes, size_t node_id_capacity, uint64_t *generation, size_t *record_count, size_t *node_id_byte_count ) noexcept

wasmsource

Geometry records are returned back-to-front in painter order. Generation and both required counts are written even when the supplied buffers are too small. No record or identifier bytes are written until both buffers fit.

Returns BUFFER_TOO_SMALL when either buffer is short; pass capacities of 0 to ask for the sizes first. Node identifiers are packed back to back in node_id_bytes, and each record points into them.

Called from TS: InteractiveRetainedBackend.getGeometrySnapshot, RenderCoreSession.getGeometrySnapshot

Called from TS, through InteractiveRetainedBackend.getGeometrySnapshot: InteractiveRenderSession.getSceneGeometrySnapshot, InteractiveRenderSessionCoordinator.getSceneGeometrySnapshot

Implemented by: RetainedSession.geometrySnapshot, RetainedSession.state

struct TacoRenderGeometryRecord

source

One node in taco_render_session_get_geometry_snapshot. It holds the same values as TacoRenderNodeGeometry, with the has_* bytes packed into flags (TacoRenderGeometryFlags). The node identifier is the node_id_size bytes at node_id_offset in the call’s identifier buffer.

TypeScript: decodeWasmGeometryRecords, PORTABLE_WASM_ABI_LAYOUT, RenderNodeGeometry

TypeScript, through decodeWasmGeometryRecords: RenderCoreSession.getGeometrySnapshot

uint32_t struct_sizesource
uint32_t node_id_offsetsource
uint32_t node_id_sizesource
uint32_t kindsource
uint32_t flagssource
uint32_t reservedsource
uint64_t painter_ordersource
double effective_opacitysource
double[6] world_transformsource
TacoRenderRect local_boundssource
TacoRenderRect world_boundssource
TacoRenderRect visible_world_boundssource

TacoRenderStatus taco_render_session_hit_test( const TacoRenderSession *session, double world_x, double world_y, double world_tolerance, TacoRenderHit *hits, size_t hit_capacity, char *node_id_bytes, size_t node_id_capacity, size_t *hit_count, size_t *node_id_byte_count ) noexcept

wasmsource

Finds the visible nodes under the scene point (world_x, world_y); paths also count within world_tolerance scene units. Ancestor clips apply. Hits are returned topmost first. hit_count and node_id_byte_count always receive the required sizes; when either buffer is too small nothing else is written and the call returns BUFFER_TOO_SMALL. The inputs must be finite and the tolerance non-negative.

Called from TS: InteractiveRetainedBackend.hitTest, RenderCoreSession.hitTest

Called from TS, through InteractiveRetainedBackend.hitTest: InteractiveRenderSession.hitTest, InteractiveRenderSessionCoordinator.hitTest, InteractiveRenderSessionCoordinator.hitTestIncludingLocked

Implemented by: RetainedSession.state, RetainedSession.hitTest, toRect

struct TacoRenderHit

source

One node under a point, written by taco_render_session_hit_test. The node identifier is the node_id_size bytes at node_id_offset in the call’s identifier buffer. kind is a TacoRenderNodeKind.

TypeScript: decodeWasmHitRecords, PORTABLE_WASM_ABI_LAYOUT, RenderHit

TypeScript, through decodeWasmHitRecords: RenderCoreSession.hitTest

uint32_t struct_sizesource
uint32_t node_id_offsetsource
uint32_t node_id_sizesource
uint32_t kindsource
uint32_t reservedsource
uint64_t painter_ordersource
TacoRenderRect visible_world_boundssource

TacoRenderStatus taco_render_session_get_last_dirty_bounds( const TacoRenderSession *session, TacoRenderDirtyBounds *dirty_bounds ) noexcept

wasmsource

Writes the scene-space area changed by the most recent successful batch apply, stamped with that generation.

Called from TS, in helpers of: RenderCoreSession.apply, RenderCoreSession.applyWithStaticImageFrames, RenderCoreSession.applyWithMediaFrames

Called from TS, through RenderCoreSession.apply: outlineStrokesInThrowawaySession

Implemented by: RetainedSession.lastDirtyBounds, toRect

struct TacoRenderDirtyBounds

source

The scene-space area an update changed: old_bounds covers what the changed nodes painted before, new_bounds what they paint after, and combined_bounds both. Each has_* byte says whether that rectangle is set.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, RenderDirtyBounds

uint32_t struct_sizesource
uint8_t has_old_boundssource
uint8_t has_new_boundssource
uint8_t has_combined_boundssource
uint8_t reservedsource
uint64_t generationsource
TacoRenderRect old_boundssource
TacoRenderRect new_boundssource
TacoRenderRect combined_boundssource

TacoRenderStatus taco_render_session_map_viewport( const TacoRenderSession *session, const TacoRenderViewportOptions *options, TacoRenderViewportMapping *mapping ) noexcept

wasmsource

Computes how the scene’s width and height map into a viewport by fit, fill or stretch, with the given alignment. It only computes; it changes nothing. Returns INVALID_ARGUMENT for bad options and INVALID_SCENE when the scene has no positive size or the result is not finite.

Called from TS: RenderCoreSession.mapViewport

Implemented by: RetainedSession.state

struct TacoRenderViewportOptions

source

Input to taco_render_session_map_viewport. mode is a TacoRenderViewportMode. The viewport size must be finite and positive. align_x and align_y, each from 0 to 1, place the scene in the leftover space: 0 is left or top, 0.5 is centered, 1 is right or bottom.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, ViewportMappingOptions

uint32_t struct_sizesource
uint32_t modesource
double viewport_widthsource
double viewport_heightsource
double align_xsource
double align_ysource

struct TacoRenderViewportMapping

source

Output of taco_render_session_map_viewport: the scene-to-viewport affine, its inverse (both as a, b, c, d, e, f), and where the scene lands in the viewport.

TypeScript: PORTABLE_WASM_ABI_LAYOUT, ViewportMapping

uint32_t struct_sizesource
uint32_t reservedsource
double[6] scene_to_viewportsource
double[6] viewport_to_scenesource
TacoRenderRect scene_viewport_boundssource

Rendering and export

Paint the scene into caller-owned RGBA pixels, fully or only where it changed, or export a region as RGBA, PNG, SVG or PDF bytes.

TacoRenderStatus taco_render_session_render_rgba( TacoRenderSession *session, const TacoRenderRgbaTarget *target ) noexcept

wasmsource

Paints the whole scene into the target, scaling the scene’s width and height to the target’s width and height. Every pixel is repainted, on a white background. Editing overlays, the canvas overlay and the selection chrome paint over the scene. Returns CAPABILITY_UNAVAILABLE in portable/no-Skia builds, and also when the scene has media without a current installed frame set.

Called from TS: RenderCoreSession.render

Implemented by: renderRgba

struct TacoRenderRgbaTarget

source

Caller-owned premultiplied RGBA8888 pixels for the render calls. row_bytes must be at least width * 4, and byte_length at least row_bytes * height.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t widthsource
uint32_t heightsource
uint32_t row_bytessource
uint8_t *pixelssource
size_t byte_lengthsource

TacoRenderStatus taco_render_session_render_rgba_incremental( TacoRenderSession *session, const TacoRenderRgbaTarget *target, const TacoRenderIncrementalRenderOptions *options, TacoRenderIncrementalFrameReport *report ) noexcept

wasmsource

Like taco_render_session_render_rgba, but repaints only the area changed since the last successful render when the target’s pixel pointer, size and row stride are unchanged. Otherwise, or with TACO_RENDER_INCREMENTAL_RENDER_FORCE_FULL, it repaints everything. Returns STALE_GENERATION when options->expected_generation is not current. On success, including a frame with nothing to repaint, it writes the report. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Implemented by: renderRgbaIncremental

struct TacoRenderIncrementalRenderOptions

source

Input to taco_render_session_render_rgba_incremental. flags is a bitwise OR of TacoRenderIncrementalRenderFlags; expected_generation must be the scene’s current generation.

uint32_t struct_sizesource
uint32_t flagssource
uint64_t expected_generationsource

struct TacoRenderIncrementalFrameReport

source

Timings are finite milliseconds measured by the native core. A CPU render reports submit_ms as zero. A future incremental WebGPU bridge must add its queue-submission time before exposing the frame to TypeScript. The report is written only after a successful render (including a successful no-damage frame).

uint32_t struct_sizesource
uint32_t flagssource
uint32_t nodes_paintedsource
uint32_t nodes_consideredsource
uint64_t generationsource
uint64_t scene_digestsource
TacoRenderDeviceRect device_damagesource
double scene_update_mssource
double layout_mssource
double paint_mssource
double submit_mssource
uint64_t retained_payload_bytessource
uint32_t decoded_font_entriessource
uint32_t text_layout_entriessource

TacoRenderStatus taco_render_session_export( TacoRenderSession *session, const TacoRenderExportRequest *request, TacoRenderExportResult *result ) noexcept

wasmsource

Exports one scene region. The bytes stay owned by the session until the next export or its destruction; copy them out with taco_render_session_copy_export_bytes. A failed export keeps no bytes. result is written only on success; result->byte_length is the size to copy. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Called from TS: InteractiveRetainedBackend.exportCompiledScene, PortableWasmRenderCoreSession.exportScene

Called from TS, through PortableWasmRenderCoreSession.exportScene: exportFromLoadedSession

Implemented by: exportScene

struct TacoRenderExportRequest

source

Input to taco_render_session_export; format is a TacoRenderExportFormat.

TypeScript: RenderExportRequest

uint32_t struct_sizesource
uint32_t formatsource
double xsource

The exported region in scene coordinates.

double ysource
double widthsource
double heightsource
double scalesource

Output units per scene unit: raster pixels, SVG user units, PDF points.

const char *root_node_idsource

Optional. When nonempty, only this node and its descendants paint, so overlapping siblings and ancestors stay out of the export.

size_t root_node_id_sizesource
uint32_t image_samplingsource

How image fills and media frames are resampled; a TacoRenderExportImageSampling value. Zero keeps the screen’s bilinear sampling.

uint32_t flagssource

A bitwise OR of TacoRenderExportFlags values.

double pdf_raster_dpisource

PDF only: the resolution, in dots per inch, at which effects PDF cannot express (blurs, shadows) are rasterized. Zero means 72.

struct TacoRenderExportResult

source

Output of a successful taco_render_session_export.

TypeScript: RenderExportResult

uint32_t struct_sizesource
uint32_t pixel_widthsource

Output size: ceil(region * scale). Raster formats use it as pixels.

uint32_t pixel_heightsource
uint32_t row_bytessource

Bytes per row for TACO_RENDER_EXPORT_RGBA; zero for every other format.

size_t byte_lengthsource
uint32_t request_struct_sizesource

The request size this build read, so a host can tell that the image sampling, flags and PDF resolution fields were honored.

enum TacoRenderExportFormat

source

Scene export. A rectangular region of the retained scene is painted through the same painter as the screen into one of these targets. Editing overlays never export and the background stays transparent.

TypeScript: RenderExportFormat

TACO_RENDER_EXPORT_RGBA = 1source

Tightly packed, unpremultiplied RGBA8888 rows of pixel_width * 4 bytes.

TACO_RENDER_EXPORT_PNG = 2source

PNG, when the build carries a PNG encoder; otherwise unavailable.

TACO_RENDER_EXPORT_SVG = 3source

SVG with text converted to outlines.

TACO_RENDER_EXPORT_PDF = 4source

Single-page PDF whose page is the scaled region.

size_t taco_render_session_copy_export_bytes( const TacoRenderSession *session, uint8_t *destination, size_t destination_size ) noexcept

wasmsource

Copies up to destination_size bytes of the last successful export and returns the count copied. Returns 0 when session or destination is NULL.

Called from TS: PortableWasmRenderCoreSession.exportScene

Called from TS, through PortableWasmRenderCoreSession.exportScene: exportFromLoadedSession

Text queries and text editing overlay

Carets, selection rectangles, hit testing, caret movement and size metrics for one text node, read from the same layout that is painted. The overlay paints the selection, composition and caret while the user edits.

TacoRenderStatus taco_render_session_get_text_carets( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextQuery *query, TacoRenderTextCaret *carets, size_t caret_capacity, TacoRenderTextBufferResult *result ) noexcept

wasmsource

Text queries. They read the same cached layout that is painted, work in the text node’s local coordinates, and share these rules: the node must be a text node (else INVALID_ARGUMENT, or NOT_FOUND when missing), the query’s expected_generation must be current (else STALE_GENERATION), and portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

This one writes every caret position of the text. With too small a caret_capacity (pass 0 to ask) it returns BUFFER_TOO_SMALL and writes only result->required_count. More than TACO_RENDER_TEXT_QUERY_MAX_RECORDS carets return LIMIT_EXCEEDED.

Called from TS: InteractiveRetainedBackend.getTextGeometry, RenderCoreSession.getTextCarets

Implemented by: SessionCache.getTextCaretCount, RetainedSession.state, SessionCache.getTextCarets

TacoRenderStatus taco_render_session_get_text_range_rects( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextRangeQuery *query, TacoRenderTextRect *rects, size_t rect_capacity, TacoRenderTextBufferResult *result ) noexcept

wasmsource

Writes the selection rectangles between query->anchor and query->focus. Sizing works as in taco_render_session_get_text_carets. A position outside the text returns INVALID_ARGUMENT.

Called from TS: InteractiveRetainedBackend.getTextGeometry, RenderCoreSession.getTextRangeGeometry

Implemented by: RetainedSession.state, SessionCache.getTextRangeRects

TacoRenderStatus taco_render_session_hit_test_text( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextHitQuery *query, TacoRenderTextHitResult *result ) noexcept

wasmsource

Finds the caret position nearest to a node-local point. result->inside is 1 when the point is over the laid-out text.

Called from TS: InteractiveRetainedBackend.hitTestText, RenderCoreSession.hitTestText

Implemented by: SessionCache.hitTestText

TacoRenderStatus taco_render_session_navigate_text( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextNavigationQuery *query, TacoRenderTextNavigationResult *result ) noexcept

wasmsource

Moves a caret position by one TacoRenderTextNavigationOperation. When the caret cannot move, result->has_result is 0. For line up and down, pass back the returned preferred_local_x to keep the caret’s column.

Called from TS: InteractiveRetainedBackend.navigateText, RenderCoreSession.navigateText

Implemented by: SessionCache.navigateText

TacoRenderStatus taco_render_session_get_text_layout_metrics( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextQuery *query, TacoRenderTextLayoutMetrics *metrics ) noexcept

wasmsource

Writes the text’s frame and content sizes, the frame size its auto-resize mode suggests, and its paragraph, line and caret counts.

Called from TS: InteractiveRetainedBackend.getTextLayoutMetrics, RenderCoreSession.getTextLayoutMetrics

Implemented by: SessionCache.getTextLayoutMetrics

struct TacoRenderTextBufferResult

source

For bounded queries, result is written on OK and BUFFER_TOO_SMALL. On BUFFER_TOO_SMALL required_count is exact and the record buffer is untouched. Every other failure leaves both the result and record buffers untouched.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t required_countsource
uint64_t generationsource

struct TacoRenderTextPosition

source

Paragraph-relative offsets count authored UTF-16 code units.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t paragraph_indexsource
uint32_t utf16_offsetsource
uint32_t affinitysource
uint32_t reservedsource

TacoRenderStatus taco_render_session_set_text_editing_overlay( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextEditingOverlay *overlay ) noexcept

wasmsource

Paints a text editing overlay on one text node: the selection from selection_anchor to selection_focus, an optional IME composition range (flag HAS_COMPOSITION) and the caret (flag CARET_VISIBLE). It replaces any earlier text overlay, is never exported, and is cleared by the next batch apply that changes the generation. Checks follow the text queries; unknown flags or positions outside the text return INVALID_ARGUMENT.

Called from TS: InteractiveRetainedBackend.setOverlays, RenderCoreSession.setTextEditingOverlay

Called from TS, through InteractiveRetainedBackend.setOverlays: InteractiveRenderSession.setOverlays, InteractiveRenderSessionCoordinator.setOverlays

Implemented by: SessionCache.setTextEditingOverlay

TacoRenderStatus taco_render_session_clear_text_editing_overlay( TacoRenderSession *session, const TacoRenderTextQuery *query ) noexcept

wasmsource

Removes the text editing overlay. Returns STALE_GENERATION when query->expected_generation is not current, and CAPABILITY_UNAVAILABLE in portable/no-Skia builds.

Called from TS: InteractiveRetainedBackend.setOverlays, RenderCoreSession.clearTextEditingOverlay

Called from TS, through InteractiveRetainedBackend.setOverlays: InteractiveRenderSession.setOverlays, InteractiveRenderSessionCoordinator.setOverlays

Implemented by: RetainedSession.state, SessionCache.clearTextEditingOverlay

struct TacoRenderTextEditingOverlay

source

Input to taco_render_session_set_text_editing_overlay. flags is a bitwise OR of TacoRenderTextEditingOverlayFlags; the composition range is read only with HAS_COMPOSITION.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t flagssource
uint64_t expected_generationsource
TacoRenderTextPosition selection_anchorsource
TacoRenderTextPosition selection_focussource
TacoRenderTextPosition composition_anchorsource
TacoRenderTextPosition composition_focussource

Vector geometry, vector editing overlay, booleans and stroke outlines

Read a path’s vector network in screen space, hit test its parts, preview an edited network over the scene, combine two paths with a Boolean operation, and turn a stroke into the filled shape it paints.

TacoRenderStatus taco_render_session_get_vector_geometry( const TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderVectorQuery *query, TacoRenderVectorVertex *vertices, size_t vertex_capacity, TacoRenderVectorCubicHandle *handles, size_t handle_capacity, TacoRenderVectorSegment *segments, size_t segment_capacity, TacoRenderVectorRegion *regions, size_t region_capacity, char *identifier_bytes, size_t identifier_capacity, TacoRenderVectorBufferResult *result ) noexcept

wasmsource

Reads a path node’s vector network as vertices, cubic handles, segments and regions, each with local and screen coordinates. Screen coordinates use query->document_to_screen, which must be finite and invertible. Identifiers are packed into identifier_bytes and each record points into them. result holds every count on OK and BUFFER_TOO_SMALL (pass capacities of 0 to ask). Returns NOT_FOUND for a missing node, INVALID_ARGUMENT for a node that is not a path, STALE_GENERATION, and LIMIT_EXCEEDED past TACO_RENDER_VECTOR_QUERY_MAX_RECORDS. Works in every build.

Called from TS: InteractiveRetainedBackend.getVectorGeometry, RenderCoreSession.getVectorGeometry

Implemented by: RetainedSession.state, RetainedSession.findNode, RetainedSession.geometrySnapshot, queryVectorGeometry

struct TacoRenderVectorQuery

source

Vector geometry is evaluated in CSS-screen space using this caller-supplied document-to-screen affine. The affine must be finite and nonsingular.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource
double[6] document_to_screensource

struct TacoRenderVectorBufferResult

source

On OK and BUFFER_TOO_SMALL this result is written with exact requirements. Record and identifier buffers are written only on OK after every buffer fits. Other failures leave all outputs untouched.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t vertex_countsource
uint32_t handle_countsource
uint32_t segment_countsource
uint32_t region_countsource
uint32_t identifier_byte_countsource
uint32_t[2] reservedsource
uint64_t generationsource

TacoRenderStatus taco_render_session_hit_test_vector( const TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderVectorHitQuery *query, TacoRenderVectorHit *hits, size_t hit_capacity, char *identifier_bytes, size_t identifier_capacity, TacoRenderVectorHitBufferResult *result ) noexcept

wasmsource

Finds the vector components (vertices, handles, segments, regions) of a path node near a screen point, within tolerance_css_px CSS pixels. Status and sizing rules match taco_render_session_get_vector_geometry.

Called from TS: InteractiveRetainedBackend.hitTestVector, RenderCoreSession.hitTestVector

Implemented by: RetainedSession.state, RetainedSession.findNode, RetainedSession.geometrySnapshot, hitTestVectorComponents

TacoRenderStatus taco_render_session_set_vector_editing_overlay( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderVectorEditingOverlay *overlay, const uint8_t *network_bytes, size_t network_byte_count, const TacoRenderVectorEditingComponent *components, size_t component_count, const char *identifier_bytes, size_t identifier_byte_count ) noexcept

wasmsource

Installs a transient, generation-stamped full-network preview. The accepted overlay is independent of canonical retained state and replaces prior vector-overlay state only after every byte, component, and owner check succeeds. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

node_id names the path node being edited. network_bytes is the edited network; components lists the selected parts, whose identifiers are packed into identifier_bytes. component_count and identifier_byte_count must equal the overlay’s fields. overlay_version is read first, from the 8 bytes every version of the record starts with: a different version returns UNSUPPORTED_VERSION whatever the record’s size, so a 104-byte version-1 record does. A struct_size under those 8 bytes, or under sizeof(TacoRenderVectorEditingOverlay) in a record of the current version, returns INVALID_ARGUMENT. A different network_wire_version returns UNSUPPORTED_VERSION once the rest of the record is valid; an old expected_generation returns STALE_GENERATION. The overlay is never exported and is cleared by the next apply that changes the generation.

Called from TS: InteractiveRetainedBackend.setOverlays, RenderCoreSession.setVectorEditingOverlay

Called from TS, through InteractiveRetainedBackend.setOverlays: InteractiveRenderSession.setOverlays, InteractiveRenderSessionCoordinator.setOverlays

Implemented by: RetainedSession.state, SessionCache.setVectorEditingOverlay

struct TacoRenderVectorEditingOverlay

source

The network byte payload uses TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION and is bounded by TACO_RENDER_VECTOR_EDITING_OVERLAY_MAX_NETWORK_BYTES. The CSS viewport values describe the exact viewport installed by the caller; native code derives the owner node’s local-to-document transform from the retained scene and composes this affine for all preview geometry, selection emphasis, editing chrome, and relevant ancestor clips. Every affine coefficient has magnitude at most 16,777,216. CSS dimensions and density are in the inclusive range [1e-28, 16,777,216].

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t overlay_versionsource
uint64_t expected_generationsource
double[6] document_to_screensource
double screen_width_css_pxsource
double screen_height_css_pxsource
double screen_pixel_densitysource
uint32_t network_wire_versionsource
uint32_t component_countsource
uint32_t identifier_byte_countsource
uint32_t pending_handle_flagssource

TacoRenderVectorEditingPendingHandleFlags; zero when no handle is pending.

uint32_t pending_handle_vertex_indexsource

Position of the handle’s vertex in the preview network’s vertex table.

uint32_t reservedsource
double pending_handle_dxsource

The handle’s offset from its vertex, in the owner’s local units.

double pending_handle_dysource

TacoRenderStatus taco_render_session_clear_vector_editing_overlay( TacoRenderSession *session, const TacoRenderVectorEditingOverlayQuery *query ) noexcept

wasmsource

Removes the vector editing overlay. Returns STALE_GENERATION when query->expected_generation is not current, and CAPABILITY_UNAVAILABLE in portable/no-Skia builds.

Called from TS: InteractiveRetainedBackend.setOverlays, RenderCoreSession.clearVectorEditingOverlay

Called from TS, through InteractiveRetainedBackend.setOverlays: InteractiveRenderSession.setOverlays, InteractiveRenderSessionCoordinator.setOverlays

Implemented by: RetainedSession.state, SessionCache.clearVectorEditingOverlay

TacoRenderStatus taco_render_session_boolean_vector_paths( const TacoRenderSession *session, const char *primary_node_id, size_t primary_node_id_size, const char *secondary_node_id, size_t secondary_node_id_size, const char *result_id_namespace, size_t result_id_namespace_size, const TacoRenderVectorBooleanQuery *query, uint8_t *network_bytes, size_t network_capacity, TacoRenderVectorBooleanResult *result ) noexcept

wasmsource

Combines two different path nodes with a TacoRenderVectorBooleanOperation and writes the result as one standalone vector network in the primary path’s local coordinates. New component identifiers start with result_id_namespace (1 to TACO_RENDER_VECTOR_BOOLEAN_MAX_NAMESPACE_BYTES of UTF-8). With too small a network_capacity (pass 0 to ask) it returns BUFFER_TOO_SMALL and writes only result. Returns NOT_FOUND for a missing node, INVALID_ARGUMENT when either node is not a path, and STALE_GENERATION. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Called from TS: InteractiveRetainedBackend.booleanVectorPaths, RenderCoreSession.booleanVectorPaths

Implemented by: RetainedSession.state, RetainedSession.findNode, RetainedSession.geometry, booleanVectorPaths, encodeVectorNetwork

struct TacoRenderVectorBooleanQuery

source

One renderer-authoritative Boolean operation over two retained paths. The output uses TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION in the primary path’s local coordinate space. The query never mutates retained state.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t operationsource
uint64_t expected_generationsource
uint32_t[2] reservedsource

struct TacoRenderVectorBooleanResult

source

On OK and BUFFER_TOO_SMALL this result is written with the exact standalone network byte requirement. The payload buffer is written only on OK after it fits in full. Other failures leave both outputs untouched.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t network_byte_countsource
uint64_t generationsource

TacoRenderStatus taco_render_session_outline_stroke( const TacoRenderSession *session, const char *node_id, size_t node_id_size, const char *result_id_namespace, size_t result_id_namespace_size, const TacoRenderStrokeOutlineQuery *query, uint8_t *network_bytes, size_t network_capacity, TacoRenderStrokeOutlineResult *result ) noexcept

wasmsource

Writes one stroke of a path or container node as a standalone vector network of the area the stroke paints, in the node’s local coordinates: closed loops grouped into even-odd regions, a ring being one region with a hole and each dash a region of its own. The geometry comes from the code that paints the stroke, so it follows the stroke’s width, position (inside, center, outside), join, miter limit, dashes and dash ends, per-side weights, rounded and smoothed corners, and a path’s end shapes. Offset curves lie within 1/32 of a local unit of the exact ones, and circle arcs become cubics within 0.01 of a local unit. The regions carry no fills. A stroke that paints nothing (no width, or a path without segments) gives a network without components.

New component identifiers start with result_id_namespace (1 to TACO_RENDER_STROKE_OUTLINE_MAX_NAMESPACE_BYTES of UTF-8). With too small a network_capacity (pass 0 to ask) it returns BUFFER_TOO_SMALL and writes only result. Returns NOT_FOUND for a missing node, INVALID_ARGUMENT for a text or media node or a stroke index past the node’s strokes, INVALID_SCENE when the stroke cannot be prepared or Skia PathOps cannot resolve its outline, LIMIT_EXCEEDED when the outline is too large for a network, and STALE_GENERATION. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE. Since no capability flag announces it, a caller of an older library must check that the symbol exists.

Called from TS: InteractiveRetainedBackend.outlineCompiledStrokes, PortableWasmRenderCoreSession.outlineStroke

Called from TS, through PortableWasmRenderCoreSession.outlineStroke: outlineStrokesInThrowawaySession

Implemented by: RetainedSession.state, RetainedSession.findNode, outlineNodeStroke, encodeVectorNetwork

struct TacoRenderStrokeOutlineQuery

source

Asks for one stroke of a retained path or container as the filled shape it paints. stroke_index counts the node’s strokes in paint order, hidden ones included. The query never mutates retained state.

A call whose buffer is too small (the sizing call) keeps the outline it made, and the next call for the same node, stroke, namespace and generation is answered from it, so the path is stroked and resolved once for the two calls. Any other outline query drops what was kept. Ask for the bytes right after sizing: a font or image that arrives between the two calls without a new generation is not seen by the second.

TypeScript: RenderStrokeOutlineRequest

uint32_t struct_sizesource
uint32_t stroke_indexsource
uint64_t expected_generationsource
uint32_t[2] reservedsource

struct TacoRenderStrokeOutlineResult

source

Written as TacoRenderVectorBooleanResult is: on OK and BUFFER_TOO_SMALL with the exact standalone network byte requirement; other failures leave both outputs untouched.

TypeScript: RenderStrokeOutlineSnapshot

uint32_t struct_sizesource
uint32_t network_byte_countsource
uint64_t generationsource

Selection chrome and canvas overlay

Editor drawing painted over the scene but never part of it: selection outlines and handles, and snap lines, guides, measurements and the pixel grid. Both take a small binary format and resolve against nodes at paint time. Canvas overlay formats 2 to 4 add frame titles, then the rulers, then the boxes, handles and badges of the auto layout spacing handles; their text is drawn with the font set by taco_render_session_set_canvas_overlay_font.

TacoRenderStatus taco_render_session_set_selection_chrome( TacoRenderSession *session, const uint8_t *bytes, size_t size ) noexcept

wasmsource

Replaces the selection outlines and transform handles painted over the scene; size 0 clears them. They resolve against retained nodes at paint time, so they share a frame with the pixels they describe. Items naming a missing node are skipped, which lets the chrome outlive scene updates.

Little-endian bytes: u32 version (3 since wire version 20), u32 count, f32 device pixel ratio. Then per item: u32 kind (0 outline of a rect, 1 square handle, 2 round handle), u32 flags (1 hover, 2 dashed), u32 stroke RGBA, u32 fill RGBA (handles only), f64 x, y, width, height, f64 scene offset x, y, six f64 of a matrix, f64 angle, u32 node id size, node id bytes. x and y map through the item’s matrix, then (since wire version 21) through the named node’s transform; with an empty node id the matrix is local-to-scene, so identity makes them scene coordinates. A multi-selection box is anchored to a selected node this way, so it moves with the node. An outline is the rect (x, y, width, height) through its node’s transform, so a rotated node gets a rotated outline, and its angle is 0. A handle is finished by the editor, which also hit-tests it: (x, y) is its centre, width and height its size on screen in CSS pixels (width along the turned x direction) and angle its turn in radians, clockwise on screen. The renderer rounds the size to whole device pixels and keeps a handle at a whole number of quarter turns on whole device pixels.

Limits: at most 4096 items, a pixel ratio in (0, 16], handle sides in (0, 64] CSS pixels and node ids of at most 1024 bytes. Bytes that break these rules, are truncated or have bytes left over return INVALID_ARGUMENT and keep the old chrome. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Called from TS: encodeSelectionChrome, InteractiveRetainedBackend.setOverlays, RenderSelectionChrome, RenderCoreSession.setSelectionChrome

Called from TS, through encodeSelectionChrome: RenderCoreSession.setSelectionChrome

Called from TS, through InteractiveRetainedBackend.setOverlays: InteractiveRenderSession.setOverlays, InteractiveRenderSessionCoordinator.setOverlays

Implemented by: SessionCache.setSelectionChrome

TacoRenderStatus taco_render_session_set_canvas_overlay( TacoRenderSession *session, const uint8_t *bytes, size_t size ) noexcept

wasmsource

Replaces the editor’s overlay lines painted over the scene: snap and alignment lines with their x marks, ruler guides, option-drag outlines and measurements, and the pixel grid. Size 0 clears them. They are never document content, never exported, and never change the retained scene, so updating them costs no scene encode. The grid is painted under the selection chrome, every line and mark over it.

Every straight line is a fill of whole device pixels, max(1, round(dpr)) wide, rounded with std::round like the selection outline: side 0 (after) covers [round(p), round(p) + w), side 1 (before) [round(p) - w, round(p)), side 2 (centered) starts at round(p - w / 2). A line’s ends round to round(start) and round(end). An x mark is centered on the middle of the pixels its two crossing lines cover and drawn with anti-aliasing.

Little-endian bytes, version 1 to 3: u32 version, f32 device pixel ratio, u32 anchor count, then per anchor u32 node id size (1 to 1024) and the id bytes. Then u32 grid (0 or 1); with a grid: u32 anchor index, u32 RGBA, f64 origin x, y, f64 step. Then u32 line count, u32 mark count. Per line (56 bytes): u32 axis (0 vertical, 1 horizontal), u32 side, u32 flags (1 spans the whole target, 2 scene coordinates, 4 spans the anchor’s own box from 0 to its width or height, ignoring start and end), u32 RGBA, u32 anchor index, u32 zero, f64 position, f64 start, f64 end, f32 dash and f32 gap in CSS pixels (both 0 for a solid line). Per mark (40 bytes): u32 RGBA, u32 sides (x side in bits 0-1, y side in bits 2-3), u32 flags (2 scene coordinates), u32 anchor index, f32 half size in CSS pixels, u32 zero, f64 x, f64 y.

An item’s coordinates are local to its anchor node and map through the node’s world transform at paint time, so items follow the node through a pan, a move or a resize preview in the same frame as its pixels; a frame’s own ruler guides anchor to the frame and span its box. Anchors must be axis aligned. Items whose anchor is missing from the scene are skipped.

Version 2 appends frame titles: u32 title count (at most 4096), then per title u32 anchor index, u32 RGBA, u32 flags (0), f32 font size and f32 gap in CSS pixels, u32 UTF-8 text size (1 to 4096) and the text bytes. A title is the name drawn above a top-level frame. It sits at the top-left corner of the anchor’s device-space bounding box, its line box (as tall as the font size) ending gap CSS pixels above that box, and it is cut to the box’s width with an ellipsis; a title whose ellipsis alone does not fit is not drawn. Titles use the font set with taco_render_session_set_canvas_overlay_font, falling back per character to the document’s decoded fonts, and are not drawn before that font is set.

Version 3 appends the rulers: u32 flag (0 or 1); with rulers: u32 anchor index, u32 flags (0), f32 thickness (1 to 256), f32 font size (1 to 256) and f32 minimum major tick spacing (1 to 4096) in CSS pixels, then u32 RGBA for the background, border, ticks and numbers, selection extent, and selection values. Then, for x (top ruler) and then y (left ruler), u32 extent count (at most 65536) and per extent f64 start and end in anchor units, sorted and apart (a zero-length extent marks one value). Then u32 label count (at most 16) and per label u32 axis (0 x, 1 y), u32 background RGBA, u32 text RGBA, u32 reserved (0), f64 position in anchor units: a guide’s position, drawn on its ruler as a rounded tag. The rulers are laid out when the frame is painted from the anchor’s world transform (a 1-2-5 major step at least the minimum spacing apart, five minor ticks per step), so they move with the page in the same frame. They are drawn over everything else, with numbers in the font set with taco_render_session_set_canvas_overlay_font (no numbers before it is set). A missing or rotated anchor leaves them blank.

Version 4 appends boxes, handles and badges, used for the auto layout spacing handles. Their coordinates are local to their anchor and map through the anchor’s whole world transform, so, unlike lines, they also follow a rotated anchor. u32 box count (at most 4096), then per box (56 bytes) u32 anchor index, u32 flags (1 fill, 2 hatching, 4 outline), u32 fill RGBA, u32 hatching RGBA, u32 outline RGBA, u32 reserved (0), f64 x, y, width and height (sizes zero or more). Hatching is diagonal stripes one CSS pixel wide every 4 CSS pixels, clipped to the box; the outline is one CSS pixel inside the box. Then u32 handle count (at most 4096) and per handle (40 bytes) u32 anchor index, u32 axis (0 along the anchor’s local x, 1 along its local y), u32 RGBA, u32 border RGBA, f32 length (0 to 64] and f32 thickness (0 to 16] in CSS pixels, f64 x and y of its centre: a tick with round ends and a one-CSS-pixel border. Then u32 badge count (at most 16) and per badge u32 anchor index, u32 background RGBA, u32 text RGBA, u32 reserved (0), f64 x and y, f32 x and y offsets in CSS pixels to the badge’s top-left corner, f32 font size (1 to 64), u32 UTF-8 text size (1 to 256) and the text bytes: a rounded pill with one line of text in the overlay font (not drawn before that font is set). Boxes are drawn over the selection outline, handles over the boxes and badges over the handles, all under the frame titles and rulers.

Bytes that do not follow this layout return INVALID_ARGUMENT and keep the old overlay. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Called from TS: encodeCanvasOverlay, InteractiveRetainedBackend.setOverlays, RenderCoreSession.setCanvasOverlay

Called from TS, through encodeCanvasOverlay: RenderCoreSession.setCanvasOverlay

Called from TS, through InteractiveRetainedBackend.setOverlays: InteractiveRenderSession.setOverlays, InteractiveRenderSessionCoordinator.setOverlays

Implemented by: SessionCache.setCanvasOverlay

TacoRenderStatus taco_render_session_set_canvas_overlay_font( TacoRenderSession *session, const uint8_t *bytes, size_t size ) noexcept

wasmsource

Sets the font the canvas overlay draws frame titles with: the bytes of one font file (TrueType, OpenType or WOFF2), copied. Size 0 removes it, after which titles are not drawn. Bytes Skia cannot decode return INVALID_ARGUMENT and keep the old font. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Called from TS: RenderCoreSession.setCanvasOverlayFont

Implemented by: SessionCache.setCanvasOverlayFont

Fonts

Read the features and axes of a font in the scene, or copy a table out of font bytes before any text uses them.

TacoRenderStatus taco_render_session_get_font_metadata( TacoRenderSession *session, const char *font_resource_id, size_t font_resource_id_size, const TacoRenderFontMetadataQuery *query, TacoRenderFontFeature *features, size_t feature_capacity, TacoRenderFontAxis *axes, size_t axis_capacity, TacoRenderFontMetadataResult *result ) noexcept

wasmsource

Lists the OpenType features (from GSUB and GPOS) and variation axes of a font resource in the scene. Returns NOT_FOUND when no font resource has that ID and STALE_GENERATION when query->expected_generation is not current. Sizing works as in the text queries: result holds both counts on OK and BUFFER_TOO_SMALL. Portable/no-Skia builds return CAPABILITY_UNAVAILABLE.

Called from TS: InteractiveRetainedBackend.getFontMetadata, RenderCoreSession.getFontMetadata

Implemented by: SessionCache.getFontMetadata

struct TacoRenderFontMetadataResult

source

On OK and BUFFER_TOO_SMALL this result contains exact record counts and the queried retained generation. Feature and axis buffers are written only on OK after both fit. Every other failure leaves all outputs untouched.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t feature_countsource
uint32_t axis_countsource
uint32_t reservedsource
uint64_t generationsource

TacoRenderStatus taco_render_copy_font_table( const uint8_t *font_bytes, size_t font_byte_count, uint32_t table_tag, uint8_t *table_bytes, size_t table_capacity, size_t *table_size ) noexcept

wasmsource

Copies one OpenType table, such as ‘cmap’ (0x636d6170), out of font bytes in any format the renderer decodes: TrueType, OpenType/CFF, WOFF and WOFF2. It needs no session, so a host can read which characters a font maps before any text uses it. *table_size receives the table’s byte count; when table_capacity is smaller (pass 0 to ask for the size) nothing is copied and the call returns BUFFER_TOO_SMALL. Returns NOT_FOUND when the font has no such table, INVALID_SCENE when the bytes are not a decodable font, and CAPABILITY_UNAVAILABLE in Portable/no-Skia builds.

Implemented by: copyFontTable

Wasm bridge

The portable Wasm build, without Skia, exports these instead of the C ABI. It applies batches and returns a JSON summary of the scene and its geometry. The WebGPU build also exports the four version and capability functions.

uint32_t taco_render_wasm_abi_version() noexcept

wasmsource

Returns TACO_RENDER_ABI_VERSION.

Called from TS, in helpers of: PortableWasmRenderCoreModule.constructor, createOwnedWasmRenderCoreSession, attachPortableWasmRenderCoreSession

uint32_t taco_render_wasm_wire_version() noexcept

wasmsource

Returns TACO_RENDER_WIRE_VERSION.

Called from TS, in helpers of: PortableWasmRenderCoreModule.constructor, createOwnedWasmRenderCoreSession, attachPortableWasmRenderCoreSession

uint32_t taco_render_wasm_vector_network_wire_version() noexcept

wasmsource

Returns TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION.

Called from TS, in helpers of: PortableWasmRenderCoreModule.constructor, createOwnedWasmRenderCoreSession, attachPortableWasmRenderCoreSession

uint32_t taco_render_wasm_portable_capabilities() noexcept

wasmsource

Returns capability bits: retained scene, structured paths, styled text and embedded resources (bits 0 to 3). This build never paints.

Called from TS, in helpers of: PortableWasmRenderCoreModule.constructor, createOwnedWasmRenderCoreSession, attachPortableWasmRenderCoreSession

PortableWasmSession *taco_render_wasm_session_create() noexcept

wasmsource

Creates an empty portable session, or returns null when allocation fails.

void taco_render_wasm_session_destroy(PortableWasmSession *session) noexcept

wasmsource

Frees a portable session. Null is allowed.

uint32_t taco_render_wasm_session_apply( PortableWasmSession *session, const uint8_t *batch, size_t batch_size ) noexcept

wasmsource

Applies one batch, all or nothing, and returns a TacoRenderStatus.

const char *taco_render_wasm_session_snapshot( PortableWasmSession *session ) noexcept

wasmsource

Returns a JSON summary of the scene, its geometry and painter order, or null on failure. The string lives until the next snapshot or apply.

const char *taco_render_wasm_session_last_error( const PortableWasmSession *session ) noexcept

wasmsource

Returns the last error message; empty after a successful apply.

Retained session

The C++ scene behind every session. It decodes batches, keeps nodes and resources, and computes geometry for queries and painting.

class taco::render::RetainedSession

source

The retained scene behind a C session, with no painting. It decodes wire batches, keeps the scene and its derived geometry, and answers geometry and hit queries. Both the Skia builds and the portable Wasm build use it.

RetainedSession()source
ApplyResult apply(std::span<const uint8_t> batch, ApplyTransaction *transaction)source

Applies one complete batch. It is all or nothing: on failure the scene, geometry and generation are unchanged. A batch with at least one command raises the generation by one. With a transaction, the change stays undoable until the transaction commits.

Called from TS: RenderChangeBatchEncoder

C ABI: taco_render_session_apply

const SceneState &state() constsource

C ABI: taco_render_session_apply, taco_render_session_apply_with_static_image_frames, taco_render_session_apply_with_media_frames, taco_render_session_update_video_frames, taco_render_session_get_scene_info, taco_render_session_get_geometry_snapshot, taco_render_session_hit_test, taco_render_session_map_viewport

and 9 moretaco_render_session_get_text_carets, taco_render_session_get_text_range_rects, taco_render_session_clear_text_editing_overlay, taco_render_session_get_vector_geometry, taco_render_session_hit_test_vector, taco_render_session_set_vector_editing_overlay, taco_render_session_clear_vector_editing_overlay, taco_render_session_boolean_vector_paths, taco_render_session_outline_stroke

const Node *findNode(std::string_view id) constsource

The node with this ID, or null.

C ABI: taco_render_session_get_node_info, taco_render_session_get_node_geometry, taco_render_session_get_vector_geometry, taco_render_session_hit_test_vector, taco_render_session_boolean_vector_paths, taco_render_session_outline_stroke

uint64_t digest() constsource

A hash of the scene contents, cached until the next apply.

C ABI: taco_render_session_get_scene_info

size_t retainedPayloadBytes() constsource
const SessionApplyStats &applyStats() constsource
const ApplyChangeSummary &lastApplyChanges() constsource
bool sceneContainsMedia() constsource
bool hasUnboundedVisibleText() constsource
void resetApplyStats()source
const SceneGeometry &geometry() constsource

C ABI: taco_render_session_boolean_vector_paths

std::shared_ptr<const SceneGeometry> geometrySnapshot() constsource

C ABI: taco_render_session_get_node_geometry, taco_render_session_get_geometry_snapshot, taco_render_session_get_vector_geometry, taco_render_session_hit_test_vector

const PaintedStrokeCache &paintedStrokes() constsource

What hit tests have kept for path strokes, for tests and measurements.

void setMaximumPaintedStrokeBytes(size_t maximum_bytes)source

Replaces the limit on what hit tests keep (kMaximumPaintedStrokeBytes). Tests use a small one.

std::vector<HitRecord> hitTest( double world_x, double world_y, double world_tolerance ) constsource

Visible nodes under a scene point, topmost first (see pointHitsNode).

C ABI: taco_render_session_hit_test

const DirtyBounds &lastDirtyBounds() constsource

The area changed by the most recent successful apply.

C ABI: taco_render_session_get_last_dirty_bounds

class taco::render::ApplyTransaction

source

Holds a batch apply open so a caller can check more state before keeping it. SessionCache.applyWithMediaFrames uses one to install frames and scene together, or neither.

ApplyTransaction()source
~ApplyTransaction()source
void commit() noexceptsource

Keep the applied session state. A transaction that is still active is rolled back automatically when it leaves scope.

void rollback() noexceptsource

struct taco::render::ApplyResult

source

The outcome of RetainedSession.apply: a status with an error message on failure, the command count and the dirty bounds on success.

TacoRenderStatus statussource
uint32_t commands_appliedsource
std::string errorsource
DirtyBounds dirty_boundssource

struct taco::render::SceneState

source

The retained scene itself: size, generation, nodes and resources by ID.

double widthsource
double heightsource
uint64_t generationsource
std::map<std::string_view, std::shared_ptr<const Node>, std::less<>> nodessource

Keys view the ID owned by each immutable value. Copying these maps for a transaction copies only tree nodes, string views, and shared pointers; it never copies retained identifiers, vector topology, text, or resource data.

std::map<std::string_view, std::shared_ptr<const Resource>, std::less<>> resourcessource

struct taco::render::Node

source

One retained node: its identity, parent, stacking, style and the payload for its kind (path, text, container or media).

std::string idsource
std::string parent_idsource
int32_t z_ordersource
bool visiblesource
BlendMode blend_modesource

Wire version 10 composites a non-normal blend mode through one isolated layer, and lets a node mask its later siblings until the parent ends or another mask starts a new range. A hidden mask masks nothing.

bool masksource
bool vertex_cornerssource

Wire version 12 node flag bit 2: a path appends its vertex corner table.

bool stroke_capssource

Wire version 13 node flag bit 4: a path appends its two stroke caps.

Transform transformsource

Transform-only wire updates intentionally mutate this field while the immutable node identity and payload remain shared with paint caches.

NodeStyle stylesource
uint16_t source_wire_versionsource
std::variant<PathData, TextData, ContainerData, MediaData> datasource
void setTransform(const Transform &value) constsource
TacoRenderNodeKind kind() constsource

C ABI: taco_render_session_get_node_info

struct taco::render::SceneGeometry

source

The derived geometry of a whole scene at one generation. A snapshot is immutable and shared, so readers can keep it while the session moves on.

uint64_t generationsource
size_t painter_order_id_bytessource
std::vector<std::shared_ptr<const Node>> retained_nodessource

Geometry identifiers are views into retained nodes. Ownership is kept here so a geometry result remains valid after its session is updated. A transform-only update may mutate a retained node’s live transform after this snapshot is built; use ComputedNodeGeometry::world_transform for the transform captured by this geometry result.

std::map<std::string_view, ComputedNodeGeometry, std::less<>> nodessource
std::vector<std::string_view> painter_ordersource
SceneHitIndex hit_indexsource
std::map<std::string_view, std::string_view, std::less<>> mask_ownerssource

Masked node ID to the visible sibling mask that clips it. Views into retained nodes, recomputed in full on every geometry build.

struct taco::render::ComputedNodeGeometry

source

Geometry derived for one node: bounds in local, world and clipped visible space, the world transform, back-to-front painter order and the opacity and visibility after every ancestor.

Bounds local_boundssource
Bounds world_boundssource
Bounds visible_world_boundssource
Bounds subtree_visible_boundssource

Everything this node and its descendants paint, after masks and with this node’s layer blur tail around all of it. The renderer uses this box as it is for the node’s compositing layer and for the area a layer blur repaints, so neither adds the tail a second time.

bool subtree_paintedsource

Whether the renderer paints this node or any of its descendants. A subtree can be painted and still show nothing, when a mask inside it hides all of it, so this is not the same as a valid subtree box.

Transform world_transformsource
uint64_t painter_ordersource
bool effectively_visiblesource
double effective_opacitysource

struct taco::render::DirtyBounds

source

The scene-space area one update changed, before and after. The C ABI copies it into TacoRenderDirtyBounds.

Bounds old_boundssource
Bounds new_boundssource
Bounds combined_boundssource
uint64_t generationsource

struct taco::render::HitRecord

source

One node hit by RetainedSession.hitTest, with its geometry.

std::shared_ptr<const Node> nodesource
ComputedNodeGeometry geometrysource

Stroke settings the session reads from the wire: end caps, corner joins and dash ends, in Figma’s terms.

enum class taco::render::StrokeCap : uint8_t

source

Wire version 13 stroke end caps, in Figma’s terms. A path without explicit caps keeps the historical round caps at both ends.

TypeScript: RetainedStrokeCap

kNone = 0source
kRound = 1source
kSquare = 2source
kLineArrow = 3source
kTriangleArrow = 4source
kReversedTriangle = 5source
kCircleArrow = 6source
kDiamondArrow = 7source

enum class taco::render::StrokeJoin : uint8_t

source

Wire version 23 stroke joins and dash ends, in Figma’s terms.

TypeScript: RetainedStrokeJoin

kMiter = 0source
kBevel = 1source
kRound = 2source

enum class taco::render::DashCap : uint8_t

source

TypeScript: RetainedDashCap

kNone = 0source
kRound = 1source
kSquare = 2source

Skia adapter

SessionCache keeps the Skia state of one session, and the functions after it paint the retained scene through it.

class taco::render::skia_adapter::SessionCache

source

Owns backend-independent shaping resources for one retained renderer session. The implementation intentionally stays private to the Skia adapter so text queries reuse the exact paragraphs painted on screen without exposing Skia types through the shared C ABI.

Each C session holds one next to its RetainedSession. It keeps decoded fonts, text layouts and prepared paths, the installed image and video frames, the text and vector editing overlays, the selection chrome and canvas overlay, and the damage waiting for the next incremental render. The render functions below paint through it.

SessionCache()
SessionCache(const SessionCache &) = delete
source
~SessionCache()source
TacoRenderStatus applyWithStaticImageFrames( RetainedSession *session, std::span<const uint8_t> batch, uint64_t expected_generation, std::span<const StaticImageFrame> frames, ApplyResult *result, std::string *error )source

Applies through a transaction on the retained session, validates and prepares the complete image set, then commits both session and cache in one step. Rejected frame sets roll the session back automatically.

C ABI: taco_render_session_apply_with_static_image_frames

TacoRenderStatus applyWithMediaFrames( RetainedSession *session, std::span<const uint8_t> batch, uint64_t expected_generation, std::span<const MediaFrame> frames, ApplyResult *result, std::string *error )source

Like applyWithStaticImageFrames, for a mixed set of image and video frames that must match every media asset of the new scene.

C ABI: taco_render_session_apply_with_media_frames

TacoRenderStatus updateVideoFrames( const RetainedSession &session, uint64_t expected_generation, std::span<const MediaFrame> frames, DirtyBounds *dirty_bounds, std::string *error )source

Replaces the video frames of the current generation without applying a batch, and writes the scene area whose pixels change. Needs the mixed set installed by applyWithMediaFrames for this generation.

C ABI: taco_render_session_update_video_frames

uint32_t lastVideoFrameUpdateIndexedAssetCountForTesting() const noexceptsource

Native smoke diagnostics for the most recent successful video tick. They remain on the private C++ adapter surface and do not extend the public ABI.

uint32_t lastVideoFrameUpdateSubmittedFrameCountForTesting() const noexceptsource
uint32_t lastRgbaRollbackAllocationsForTesting() const noexceptsource
TacoRenderStatus getTextCarets( const RetainedSession &session, std::string_view node_id, const TacoRenderTextQuery &query, std::vector<TacoRenderTextCaret> *carets, struct RenderStatistics *statistics, std::string *error )source

Text queries behind the taco_render_session_*text* functions. Each checks the query’s expected generation, lays the node out (or reuses the cached layout) and answers in node-local coordinates.

Builds every caret record of a text node.

C ABI: taco_render_session_get_text_carets

TacoRenderStatus getTextCaretCount( const RetainedSession &session, std::string_view node_id, const TacoRenderTextQuery &query, uint32_t *caret_count, struct RenderStatistics *statistics, std::string *error )source

Counts the carets without building them, so the C function can report BUFFER_TOO_SMALL first.

C ABI: taco_render_session_get_text_carets

TacoRenderStatus getTextRangeRects( const RetainedSession &session, std::string_view node_id, const TacoRenderTextRangeQuery &query, std::vector<TacoRenderTextRect> *rects, struct RenderStatistics *statistics, std::string *error )source

Builds the selection rectangles between two text positions.

C ABI: taco_render_session_get_text_range_rects

TacoRenderStatus hitTestText( const RetainedSession &session, std::string_view node_id, const TacoRenderTextHitQuery &query, TacoRenderTextHitResult *result, struct RenderStatistics *statistics, std::string *error )source

Finds the caret position nearest to a node-local point.

C ABI: taco_render_session_hit_test_text

TacoRenderStatus navigateText( const RetainedSession &session, std::string_view node_id, const TacoRenderTextNavigationQuery &query, TacoRenderTextNavigationResult *result, struct RenderStatistics *statistics, std::string *error )source

Moves a text position by one navigation operation.

C ABI: taco_render_session_navigate_text

TacoRenderStatus getTextLayoutMetrics( const RetainedSession &session, std::string_view node_id, const TacoRenderTextQuery &query, TacoRenderTextLayoutMetrics *metrics, struct RenderStatistics *statistics, std::string *error )source

Reports content size, suggested frame size and line counts.

C ABI: taco_render_session_get_text_layout_metrics

TacoRenderStatus getFontMetadata( const RetainedSession &session, std::string_view font_resource_id, const TacoRenderFontMetadataQuery &query, std::vector<TacoRenderFontFeature> *features, std::vector<TacoRenderFontAxis> *axes, struct RenderStatistics *statistics, std::string *error )source

Lists a font resource’s OpenType features and variation axes. The result is cached with the decoded font.

C ABI: taco_render_session_get_font_metadata

TacoRenderStatus setTextEditingOverlay( const RetainedSession &session, std::string_view node_id, const TacoRenderTextEditingOverlay &overlay, struct RenderStatistics *statistics, std::string *error )source

Installs the selection, composition and caret painted on one text node, replacing any earlier text overlay.

C ABI: taco_render_session_set_text_editing_overlay

void clearTextEditingOverlay(const RetainedSession &session) noexceptsource

Removes the text editing overlay and marks its area for repaint.

C ABI: taco_render_session_clear_text_editing_overlay

TacoRenderStatus setVectorEditingOverlay( const RetainedSession &session, std::string_view node_id, const TacoRenderVectorEditingOverlay &overlay, std::span<const uint8_t> network_bytes, std::span<const TacoRenderVectorEditingComponent> components, std::string_view identifier_bytes, struct RenderStatistics *statistics, std::string *error )source

Installs the preview of an edited vector network on one path node, with its selected components, replacing any earlier vector overlay.

C ABI: taco_render_session_set_vector_editing_overlay

void clearVectorEditingOverlay(const RetainedSession &session) noexceptsource

Removes the vector editing overlay and marks its area for repaint.

C ABI: taco_render_session_clear_vector_editing_overlay

TacoRenderStatus setSelectionChrome( std::span<const uint8_t> bytes, std::string *error )source

Replaces the selection outlines and handles painted over the scene. Empty bytes clear them. Items that name a missing node are skipped at paint time, so the chrome survives scene updates without a generation check.

Called from TS: encodeSelectionChrome, RenderSelectionChrome

Called from TS, through encodeSelectionChrome: RenderCoreSession.setSelectionChrome

C ABI: taco_render_session_set_selection_chrome

TacoRenderStatus setCanvasOverlay( std::span<const uint8_t> bytes, std::string *error )source

Replaces the editor’s overlay lines (snap lines, ruler guides, option-drag measurements, pixel grid) painted over the scene. Empty bytes clear them.

Called from TS: encodeCanvasOverlay

Called from TS, through encodeCanvasOverlay: RenderCoreSession.setCanvasOverlay

C ABI: taco_render_session_set_canvas_overlay

TacoRenderStatus setCanvasOverlayFont( std::span<const uint8_t> bytes, std::string *error )source

Sets the font frame titles in the canvas overlay are drawn with. Empty bytes remove it, and titles are then not drawn.

C ABI: taco_render_session_set_canvas_overlay_font

void invalidateStaticImageFrames() noexceptsource

Same as invalidateMediaFrames.

void invalidateMediaFrames() noexceptsource

Drops every installed image and video frame. Called after a plain batch apply, which carries no frames.

C ABI: taco_render_session_apply

void recordSuccessfulApply( const RetainedSession &session, const DirtyBounds &dirty_bounds, bool generation_changed ) noexceptsource

Updates the caches after a successful batch apply: records the damage, drops text layouts and paths of changed nodes, drops fonts when font resources changed, and clears both editing overlays when the generation changed.

C ABI: taco_render_session_apply

void recordSceneUpdateTime(double scene_update_ms) noexceptsource

Adds apply time to the scene_update_ms of the next incremental frame report. Negative or non-finite values are ignored.

C ABI: taco_render_session_apply, taco_render_session_apply_with_static_image_frames, taco_render_session_apply_with_media_frames

struct taco::render::skia_adapter::RenderStatistics

source
uint32_t nodes_consideredsource
uint32_t nodes_paintedsource
uint32_t paths_paintedsource
uint32_t vector_paths_preparedsource
uint32_t vector_path_cache_hitssource
uint32_t vector_region_tight_bounds_computessource
uint32_t vector_regions_visitedsource
uint32_t vector_regions_paintedsource
uint32_t vector_region_fills_paintedsource
uint32_t inherited_vector_region_fills_paintedsource
uint32_t explicit_vector_region_fills_paintedsource
uint32_t vector_region_gradient_fills_paintedsource
uint32_t vector_network_strokes_paintedsource
uint32_t vector_segments_strokedsource
uint32_t vector_stroke_contourssource
uint32_t authored_paragraphs_paintedsource

Count of authored text paragraphs whose node was painted. Empty authored paragraphs count here even though their private line-box text is not exposed to queries.

uint32_t paragraphs_paintedsource

Count of shaped SkParagraph fragments painted. List markers and split first-line indents make this larger than authored_paragraphs_painted.

uint32_t unresolved_glyphssource
uint32_t font_collection_cache_hitssource
uint32_t font_collections_builtsource
uint32_t fonts_decodedsource
uint32_t decoded_font_cache_hitssource
uint32_t unicode_cache_hitssource
uint32_t unicode_services_builtsource
uint32_t text_layout_cache_hitssource
uint32_t text_layout_font_set_rebuildssource
uint32_t rgba_rollback_allocationssource
uint32_t opacity_layer_unbounded_savessource
uint32_t ancestor_walk_vector_allocationssource
uint32_t text_paragraph_copiessource
uint32_t text_mapping_validationssource
uint32_t text_layouts_builtsource
uint32_t text_layout_cache_invalidationssource
uint32_t paragraphs_builtsource
double layout_mssource
double paint_mssource

TacoRenderStatus taco::render::skia_adapter::renderRgba( const RetainedSession &session, SessionCache *cache, const TacoRenderRgbaTarget &target, RenderStatistics *statistics, std::string *error )

source

Paints the retained scene into a caller-owned, premultiplied RGBA8888 CPU buffer. No platform font manager or GPU context is used.

C ABI: taco_render_session_render_rgba

TacoRenderStatus taco::render::skia_adapter::renderRgbaIncremental( const RetainedSession &session, SessionCache *cache, const TacoRenderRgbaTarget &target, const TacoRenderIncrementalRenderOptions &options, TacoRenderIncrementalFrameReport *report, std::string *error )

source

Reuses caller-owned pixels and repaints only accumulated scene/overlay damage when the target identity, dimensions, stride, and scene mapping are unchanged. A successful call, including one with no visible damage, acknowledges the accumulated work and writes one exact frame report.

C ABI: taco_render_session_render_rgba_incremental

TacoRenderStatus taco::render::skia_adapter::renderCanvas( const RetainedSession &session, SessionCache *cache, SkCanvas *canvas, uint32_t width, uint32_t height, RenderStatistics *statistics, std::string *error )

source

Paints the retained scene into an existing Skia canvas. The caller chooses whether that canvas is CPU- or GPU-backed; scene traversal, path building, text shaping, clipping, and paint semantics remain shared.

TacoRenderStatus taco::render::skia_adapter::renderCanvasIncremental( const RetainedSession &session, SessionCache *cache, SkCanvas *canvas, uint32_t width, uint32_t height, uintptr_t target_identity, const TacoRenderIncrementalRenderOptions &options, TacoRenderIncrementalFrameReport *report, RenderStatistics *statistics, std::string *error )

source

Reuses one caller-identified SkCanvas target and paints only accumulated device damage. The target identity must remain stable while its pixels are preserved; changing it forces a full repaint. The caller must invalidate the target if work recorded after this call cannot be submitted successfully. A successful paint is only prepared; the caller acknowledges it after its backend submission succeeds so pending damage and diagnostics are not lost.

void taco::render::skia_adapter::acknowledgeIncrementalRenderTarget( const RetainedSession &session, SessionCache *cache, uintptr_t target_identity, uint32_t width, uint32_t height ) noexcept

source

Commits a prepared incremental canvas render after backend submission.

void taco::render::skia_adapter::invalidateIncrementalRenderTarget( SessionCache *cache ) noexcept

source

Makes the next incremental canvas render repaint the complete target.

TacoRenderStatus taco::render::skia_adapter::exportScene( const RetainedSession &session, SessionCache *cache, const TacoRenderExportRequest &request, std::vector<uint8_t> *bytes, TacoRenderExportResult *result, std::string *error )

source

Paints one scene region through the shared painter into a raster, SVG, or PDF target and returns the encoded bytes. Editing overlays never export, the background stays transparent, and a nonempty root keeps only that node’s subtree. A format the build cannot encode is capability-unavailable.

C ABI: taco_render_session_export

TacoRenderStatus taco::render::skia_adapter::copyFontTable( std::span<const uint8_t> font_bytes, uint32_t table_tag, std::span<uint8_t> out, size_t *table_size )

source

Decodes font bytes (TrueType, OpenType/CFF, WOFF or WOFF2) and copies one OpenType table into out. See taco_render_copy_font_table for the statuses.

C ABI: taco_render_copy_font_table

TacoRenderStatus taco::render::skia_adapter::preflightInstalledMediaFrames( const RetainedSession &session, const SessionCache *cache ) noexcept

source

Verifies the exact retained media-frame set without allocating, painting, recording Graphite commands, or mutating cache/target state. WebGPU calls this before it arms error scopes or enters recorder cleanup paths.

TacoRenderStatus taco::render::skia_adapter::buildVectorRegionPaths( const PathData &network, std::vector<SkPath> *region_paths, std::string *error )

source

Builds one SkPath for every canonical VectorRegion. Region paint is ignored, while every authored loop uses the same topology conversion as retained painting. Edges outside regions deliberately do not contribute.

VectorBooleanResult taco::render::skia_path_ops::booleanVectorPaths( const PathData &primary, const Transform &primary_world, const PathData &secondary, const Transform &secondary_world, TacoRenderVectorBooleanOperation operation, std::string_view result_id_namespace )

source

Applies pinned Skia PathOps to canonical VectorRegion geometry. The result is expressed in primary-local coordinates and owns deterministic IDs under result_id_namespace. Region paint and edges outside regions do not contribute.

C ABI: taco_render_session_boolean_vector_paths

struct taco::render::skia_path_ops::VectorBooleanResult

source
TacoRenderStatus statussource
PathData networksource
std::string errorsource

TacoRenderStatus taco::render::skia_adapter::buildNodeStrokeOutline( const Node &node, size_t stroke_index, SkPath *outline, std::string *error )

source

Builds the area one stroke of a path or container covers, in the node’s local coordinates, as the non-overlapping closed contours Skia PathOps returns (even-odd). stroke_index counts the node’s strokes in paint order, hidden ones included. The geometry comes from the code painting uses: the stroke’s width, position, join, miter limit, dashes and dash ends, per-side weights, rounded and smoothed corners, and a path’s end shapes. Offset curves are within 1/32 of a local unit of the exact ones; a stroke painted from a prepared outline (a path with end shapes) returns that outline. A stroke that paints nothing (no width, or no segments) leaves outline empty. Returns INVALID_ARGUMENT for a text or media node or an index past the strokes, and INVALID_SCENE when the stroke cannot be prepared or PathOps cannot resolve it.

VectorNetworkResult taco::render::skia_path_ops::vectorNetworkFromPath( const SkPath &path, std::string_view id_namespace )

source

Converts a filled Skia path into a vector network: the one conversion from a Skia path to the editable model, for stroke outlines now and for any later command that ends in a PathOps result (flatten, Boolean groups).

path must hold the non-overlapping closed contours Skia PathOps (Op, Simplify) returns, filled even-odd; run Simplify first on any other path. Each contour becomes one closed loop of vertices and segments:

  • lines stay lines and cubics stay cubics;
  • a quadratic becomes the cubic that draws the same curve exactly;
  • a conic of weight below one (a circle or ellipse arc) becomes the

fewest cubics within 0.01 of a path unit of it (one for a quarter circle of radius up to about 36), and a conic of weight above one becomes thirty-two quadratics.

Loops are grouped into even-odd regions by nesting: a contour inside an even number of others bounds its own region, and one inside an odd number is a hole in the region right around it. A ring is one region with two loops; separate pieces (the dashes of a stroke) are separate regions. Regions carry no fills, so they take the node’s. Identifiers start with id_namespace (ns:v0, ns:s0, ns:s0:start, ns:r0) and the order of contours is deterministic. An empty path gives an empty network.

Grouping compares every contour with every other, so a path of more than kMaximumOutlineContours contours is refused with TACO_RENDER_STATUS_LIMIT_EXCEEDED before any comparison, and so is one whose nesting would walk more than kMaximumOutlineContainmentSegments segments. A path that would become more segments than maximumOutlineNetworkSegments allows for the length of id_namespace is refused the same way, counted from its verbs before any arc is cut into cubics and before anything of the network is built.

VectorNetworkResult taco::render::skia_path_ops::outlineNodeStroke( const Node &node, size_t stroke_index, std::string_view result_id_namespace )

source

One stroke of a retained path or container as a vector network of the area it paints, in the node’s local coordinates: buildNodeStrokeOutline followed by vectorNetworkFromPath. A stroke that paints nothing gives an empty network.

C ABI: taco_render_session_outline_stroke

struct taco::render::skia_path_ops::VectorNetworkResult

source
TacoRenderStatus statussource
PathData networksource
std::string errorsource

Wire reader and geometry helpers

Bounded reading of wire bytes, and the geometry functions the session and queries share.

class taco::render::WireReader

source

Reads little-endian values from a byte span with bounds checks. A read past the end returns false and records MALFORMED_BATCH with a message; status() and error() keep the first failure. The batch decoder and the overlay parsers use it.

TypeScript: RenderChangeBatchEncoder

WireReader(std::span<const uint8_t> bytes)source
size_t remaining() constsource
size_t offset() constsource
bool atEnd() constsource
TacoRenderStatus status() constsource
const std::string &error() constsource
bool readU8(uint8_t *value)source
bool readU16(uint16_t *value)source
bool readU32(uint32_t *value)source
bool readI32(int32_t *value)source
bool readU64(uint64_t *value)source
bool readF32(float *value)source
bool readF64(double *value)source
bool readBytes(size_t count, std::span<const uint8_t> *value)source
bool readString(size_t maximum_bytes, bool allow_empty, std::string *value)source
bool readReservedZero(size_t count)source
bool readSubreader(size_t count, WireReader *value)source
bool requireCountFits( uint64_t count, size_t minimum_item_bytes, const char *context )source
bool requireEnd(const char *context)source
bool fail(TacoRenderStatus status, std::string message)source

bool taco::render::isValidUtf8(std::span<const uint8_t> bytes)

source

C ABI: taco_render_session_apply_with_static_image_frames

SceneGeometry taco::render::computeSceneGeometry( const SceneState &state, GeometryBuildStats *stats )

source

Computes the geometry of every node from scratch: world transforms, bounds, clips, painter order, masks and the hit index.

SceneGeometry taco::render::computeIncrementalSceneGeometry( const SceneState &state, const SceneGeometry &previous, const std::set<std::string, std::less<>> &changed_ids, const ChildMap &children, GeometryBuildStats *stats, SceneGeometry *reusable_previous )

source

Like computeSceneGeometry, but recomputes only the changed nodes and reuses the rest from previous.

bool taco::render::pointHitsNode( const SceneState &state, const SceneGeometry &geometry, std::string_view node_id, double world_x, double world_y, double world_tolerance, HitTestStats *stats, PaintedStrokeCache *painted_strokes )

source

Whether a scene point hits a visible node, after ancestor clips: a path within world_tolerance of its fill or stroke, the box of a text or media node, or a container that has a fill or stroke.

A point outside the node’s visible bounds, which include its stroke, is a miss before anything is built from the path. With painted_strokes, the rounded path and the stroke contours a path’s miter joins and square ends are read from are kept there and built once for a node; without it they are built for this call alone.

TacoRenderStatus taco::render::queryVectorGeometry( const PathData &path, const ComputedNodeGeometry &geometry, const Transform &document_to_screen, VectorGeometryQueryResult *result )

source

Builds the records for taco_render_session_get_vector_geometry.

C ABI: taco_render_session_get_vector_geometry

TacoRenderStatus taco::render::hitTestVectorComponents( const SceneState &state, const SceneGeometry &geometry, const Node &node, const Transform &document_to_screen, double screen_x, double screen_y, double tolerance_css_px, VectorHitQueryResult *result )

source

Builds the records for taco_render_session_hit_test_vector.

C ABI: taco_render_session_hit_test_vector

EncodeVectorNetworkResult taco::render::encodeVectorNetwork( const PathData &network, size_t maximum_bytes )

source

Encodes one exact standalone wire-v4 VectorNetwork within the supplied cap.

Called from TS: decodeVectorNetworkRecord

Called from TS, through decodeVectorNetworkRecord: PortableWasmRenderCoreSession.outlineStroke, RenderCoreSession.booleanVectorPaths

C ABI: taco_render_session_boolean_vector_paths, taco_render_session_outline_stroke

DecodeVectorNetworkResult taco::render::decodeVectorNetwork( std::span<const uint8_t> bytes, uint16_t wire_version )

source

Decodes one exact standalone wire-v4 VectorNetwork without mutating a session.

Called from TS: encodeVectorNetworkRecord

Called from TS, through encodeVectorNetworkRecord: decodeVectorNetworkRecord, RenderCoreSession.setVectorEditingOverlay

Other exports

Entries not yet placed in a section above, in alphabetical order.

bool taco::render::anyRoundedVertex(const PathData &path)

source

Whether any vertex of path has a corner radius.

struct taco::render::ApplyChangeSummary

source
bool all_nodessource
bool resources_changedsource
std::set<std::string, std::less<>> node_idssource

enum class taco::render::BlendMode : uint8_t

source

Wire version 10 blend modes, in CSS mix-blend-mode order. Nodes carry one since wire version 10 and paints since wire version 19.

TypeScript: RetainedBlendMode

kNormal = 0source
kDarken = 1source
kMultiply = 2source
kColorBurn = 3source
kLighten = 4source
kScreen = 5source
kColorDodge = 6source
kOverlay = 7source
kSoftLight = 8source
kHardLight = 9source
kDifference = 10source
kExclusion = 11source
kHue = 12source
kSaturation = 13source
kColor = 14source
kLuminosity = 15source

struct taco::render::Bounds

source
bool validsource
double min_xsource
double min_ysource
double max_xsource
double max_ysource

bool taco::render::branchJoinPair( const std::vector<double> &angles, size_t *first, size_t *second )

source

Which two of the segment ends that meet at a vertex a solid stroke joins when three or more meet there: the two neighbours, going round the vertex, that leave more than half a turn open between them. Every other pair of neighbours overlaps on the inside of its corner, so that open side is the only place a join is missing. angles holds the direction each end leaves the vertex in, in radians as std::atan2 gives them; first and second receive the two ends’ places in it. False when no two ends are joined: fewer than three ends, or no side that open. buildStrokeContours joins the two this chooses; the renderer paints those contours and the hit test reads its joins from them. It passes only the ends that have a direction (segmentEndDirection).

ChildMap taco::render::buildChildMap( const SceneState &state, bool sort_siblings )

source

bool taco::render::buildStrokeContours( const PathData &path, bool joins_open_runs, StrokeContourOrder order, std::vector<StrokeContour> *contours, std::string *error )

source

Fills contours with the contours a stroke of path is painted along, in the order the renderer adds them to its path. path is the path as painted, with its rounded corners worked in (roundPathVertices).

Region loops come first, in both orders. A loop none of whose segments an earlier loop used is one contour in loop order, closed unless the loop is stored open. Otherwise each run of its segments that no earlier loop used is a contour. Then come the segments outside every loop, as order says; joins_open_runs is strokesJoinOpenRuns for the path’s style.

This is the only place that decides which segment ends a stroke joins: the renderer builds its stroke paths from these contours and the hit test reads each join from them, so a join’s miter point is hit exactly where the stroke is painted with a join, and a square end exactly where a contour stops. False, with error set, when the path cannot be stroked: a loop that is empty or not connected, a missing segment or vertex, or a segment that would be painted twice or not at all.

typedef std::map<std::string_view, std::vector<const Node *>, std::less<>> taco::render::ChildMap

source

struct taco::render::Color

source
float redsource
float greensource
float bluesource
float alphasource

struct taco::render::ContainerData

source
double widthsource
double heightsource
bool clipsource
std::array<double, 4> corner_radiisource

Wire version 11 flags a rounded container and appends one corner radius and Figma-style corner smoothing in 0..1; wire version 14 can instead append four radii. Radii are in the order top left, top right, bottom right, bottom left, and each clamps to half the shorter side when used. Zero radius means a square corner.

double corner_smoothingsource
bool rounded() constsource
bool uniformCorners() constsource
void setUniformCornerRadius(double radius)source
double effectiveCornerRadius(size_t corner) constsource

The radius actually used at one corner: a radius beyond half the shorter side clamps.

struct taco::render::DecodeVectorNetworkResult

source
TacoRenderStatus statussource
PathData networksource
std::string errorsource

struct taco::render::DrawnPoint

source

One point of a path as the renderer draws it.

float xsource
float ysource

struct taco::render::DrawnSegment

source

One segment as the renderer draws it. The renderer builds its path from exactly these points, in this order or backwards when a contour runs through the segment from its end, and the hit test builds a join and a square end at the segment’s ends from the same points: at the end as drawn, along the direction the drawn segment leaves it in. So the two cannot work from different numbers there, and a segment is the same shape whichever way a contour runs through it. The rest of the hit test reads the stored numbers; prepareDrawnSegment says how far apart the two are.

Shape shapesource
std::array<DrawnPoint, 9> pointssource

From the segment’s start to its end: a line’s two ends; a cubic’s start, two control points and end; an arc’s start, then a control point and an end for each of its one to four pieces.

size_t point_countsource
float arc_piece_weightsource

Each piece of an arc is a conic: a curve from one point to the next that bends toward a control point between them, by an amount this weight sets. Unlike a cubic, a conic can follow an ellipse exactly.

enum class taco::render::DrawnSegment::Shape : uint8_t

source
kLinesource
kCubicsource
kArcPiecessource

enum class taco::render::DrawnSegmentProblem : uint8_t

source

Why a segment cannot be drawn.

kNonesource
kVertexOutOfRangesource

A number is not finite or is too large for single precision:

kControlPointOutOfRangesource

A number is not finite or is too large for single precision:

kArcOutOfRangesource

A number is not finite or is too large for single precision:

kUnsupportedKindsource

A number is not finite or is too large for single precision:

struct taco::render::EncodeVectorNetworkResult

source
TacoRenderStatus statussource
std::vector<uint8_t> bytessource
std::string errorsource

struct taco::render::FontVariation

source
std::array<char, 4> tagsource
double valuesource

void taco::render::forEachImageAssetReference( const Node &node, const std::function<void (std::string_view)> &visit )

source

Visits every image paint asset ID the node references, media nodes included; visit receives each ID once per reference.

struct taco::render::GeometryBuildStats

source
uint64_t nodes_computedsource
uint64_t path_bounds_index_buildssource
std::vector<std::string_view> computed_node_idssource

struct taco::render::GradientStop

source
double offsetsource
Color colorsource

struct taco::render::HitTestStats

source
uint64_t path_index_buildssource
uint64_t segment_flatten_callssource
uint64_t clip_parent_string_copiessource
uint64_t rounded_path_buildssource

Calls of roundPathVertices and of buildStrokeContours.

uint64_t stroke_contour_buildssource

bool taco::render::isUtf8Boundary(std::string_view text, size_t offset)

source

uint64_t taco::render::maximumOutlineNetworkSegments(size_t namespace_bytes)

source

The most segments a stroke outline’s network may have when its identifiers start with a namespace of namespace_bytes bytes.

double taco::render::maximumTransformScale(const Transform &transform)

source

struct taco::render::MediaCrop

source
double xsource
double ysource
double widthsource
double heightsource

struct taco::render::MediaData

source
MediaKind kindsource
MediaFit fitsource
bool has_cropsource
double widthsource
double heightsource
std::string asset_idsource
MediaCrop cropsource

enum class taco::render::MediaFit : uint8_t

source

TypeScript: RetainedImagePaintFit

kContain = 0source
kCover = 1source
kFill = 2source

typedef StaticImageFrame taco::render::skia_adapter::MediaFrame

source

typedef StaticImageFrameDisposition taco::render::skia_adapter::MediaFrameDisposition

source

The mixed-media ABI intentionally keeps the static-image physical record.

enum class taco::render::MediaKind : uint8_t

source
kImage = 1source
kVideo = 2source

bool taco::render::nodeReferencesImageAsset(const Node &node)

source

True when any fill, stroke paint, or explicit vector-region fill of the node is an image paint, so the node needs a decoded image frame.

struct taco::render::NodeStyle

source
double opacitysource
bool has_fillsource
Color fillsource
std::vector<RetainedPaint> fillssource
std::vector<RetainedStroke> strokessource

Ordered strokes painted after the fills, first entry at the bottom. The first uses the compact common-node record; wire version 7 appends the rest to the appearance tail.

std::vector<RetainedShadow> shadowssource
double layer_blursource

Wire version 9 appends Gaussian blur radii: a layer blur applies to the node and its descendants as one isolated layer, a background blur to the content already painted behind the node’s shape. Zero means none.

double background_blursource
bool hasStroke() constsource
bool hasDropShadow() constsource
bool hasInnerShadow() constsource
double maximumStrokeWidth() constsource

The widest authored stroke width; zero without strokes.

C ABI: taco_render_session_get_node_info

struct taco::render::OpenTypeFeature

source
std::array<char, 4> tagsource
uint32_t valuesource

class taco::render::PaintedStrokeCache

source

What hit tests keep for each path node whose stroke has miter joins or square ends: the path as painted, with its rounded corners worked in (roundPathVertices), and the contours its strokes are painted along (buildStrokeContours). Working these out visits every vertex and segment of the path, so it is done once for a node and not on every pointer move.

Both are read from the node’s path and style alone. A retained Node never changes either (an update that changes them puts a new Node in the scene; an update of the transform alone keeps the same one), so what is kept belongs to one Node object and is used only for that object. Each entry holds its node, which keeps the object and the segments its contours point at alive.

What is kept has a size limit in bytes. Each entry is measured after the hit test that built it or added to it, by the capacity of what it holds. When the entries together would pass the limit, the ones used longest ago are dropped first. An entry that is larger than the limit on its own is not kept: the hit test that built it uses it once, and the next hit test on that node builds it again.

PaintedStrokeCache(size_t maximum_bytes)source
~PaintedStrokeCache()source
Entry &entryFor(const std::shared_ptr<const Node> &node)source

The entry for node, empty when nothing is kept for this object yet, and now the most recently used one. An entry kept for another object with the same ID is dropped first. The entry stays in place until settle is called for it or for another entry.

void settle(Entry *entry)source

Call once a hit test has built something into entry and no longer reads it. Measures the entry, then drops the entries used longest ago until all of them fit the limit, or entry itself when it alone is larger than the limit.

void dropUnless(std::string_view id, const Node *current)source

Drops the entry for the node with this ID unless it was built from current, the object now in the scene (null when the node is gone).

void clear()source
void setMaximumBytes(size_t maximum_bytes)source

Changes the limit and drops the entries used longest ago until the rest fit it.

size_t maximumBytes() constsource
size_t size() constsource
bool holds(std::string_view id) constsource
size_t bytes() constsource

The size of all entries, as added up when each was last settled.

size_t measuredBytes() constsource

The same size, found by measuring every entry again. Tests compare the two.

enum class taco::render::PaintKind : uint8_t

source

TypeScript: RetainedPaintRecord

kSolid = 1source
kLinearGradient = 2source
kRadialGradient = 3source

Wire version 8 adds the three kinds below.

kAngularGradient = 4source

Wire version 8 adds the three kinds below.

kImage = 5source

Wire version 8 adds the three kinds below.

struct taco::render::PathData

source
std::vector<VectorVertex> verticessource
std::vector<VectorSegment> segmentssource
std::vector<VectorTangentConstraint> constraintssource
std::vector<VectorRegion> regionssource
bool explicit_stroke_capssource

Wire version 13: explicit caps apply at the start vertex of the first authored segment and the end vertex of the last one, both open chain ends. Without them every stroke end is round, as before.

StrokeCap start_capsource
StrokeCap end_capsource
std::unique_ptr<std::pmr::monotonic_buffer_resource> identifier_arenasource

Identifier bytes are allocated from a monotonic arena so decoding a path does not perform one heap allocation for every distinct wire identifier. The arena is owned separately because string_views must remain valid when PathData is moved into a retained Node.

std::unique_ptr<std::pmr::unsynchronized_pool_resource> identifier_index_poolsource

The lookup index is decode-time state. Pool allocation keeps its nodes out of the general heap, and finalization releases them.

std::unique_ptr<IdentifierIndex> identifier_indexsource
uint32_t identifier_readssource
bool identifier_index_finalizedsource
std::string_view internIdentifier(std::string_view value)source
void finalizeIdentifierInterning()source
PathData()
PathData(PathData &&) noexcept
PathData(const PathData &other)
source

DrawnSegmentProblem taco::render::prepareDrawnSegment( const VectorSegment &segment, double start_x, double start_y, double end_x, double end_y, DrawnSegment *drawn )

source

Fills drawn with segment as the renderer draws it between its own start and end vertices. Everything the renderer changes on the way from the stored numbers to the path happens here:

  • The two vertices and a cubic’s two control points (each a vertex plus

its handle) are rounded to single precision.

  • An arc’s two radii lose their sign and are rounded to single precision,

and its rotation is rounded to single precision as well, in degrees and again in radians. A rotation of millions of degrees ends up at a different angle from the one the stored number means.

  • An arc becomes a straight line when a rounded radius is zero, when its

rounded ends are one point, or when it sweeps less than a millionth of half a turn.

  • Radii too small to reach from one end to the other are scaled up until

the arc is half an ellipse, and large_arc and sweep choose among the four arcs that fit. All of this is worked out in single precision, by the steps Skia’s SkPathBuilder::arcTo takes, so the pieces are the ones that call would make.

  • When an arc’s pieces are a quarter turn each and its radii and end are

whole numbers, the pieces’ points are rounded to whole numbers too, as Skia does to keep a rounded rectangle’s corners exact.

  • Radii so small or so large beside the arc’s length that single precision

cannot work out an angle for it give a straight line. (Skia’s own call would add nothing for these and leave the path a segment short.)

How far the hit test is from this. A join’s miter point and a square end are built from the drawn segment. Everything else in the hit test works from the stored numbers in double precision: the stroke between a segment’s ends, arrow heads, fills, and the bounds, which the editor reads and outside which nothing is hit. Those sit as far from the painted shape as the steps above move it:

  • Single precision numbers of size m are at most m / 2^23 apart, about

m * 1.2e-7. Call that a step. A line’s ends and a cubic’s four points are each drawn within half a step of where they are stored, on each axis.

  • An arc is worked out in single precision from start to finish. Its

points are drawn within about two steps of the stored arc, where m is the larger of its coordinates and its radii.

  • An arc of nearly half a turn is drawn less exactly, because its centre

comes from the square root of a difference that rounding leaves uncertain. Within 15 degrees of half a turn the two steps no longer hold, and an arc of exactly half a turn, which is what an arc with radii too small becomes, was measured up to a 2000th of its radius from the stored arc.

  • The whole-number rounding above moves an arc’s points by up to half a

unit on each axis, whatever the arc’s size, unless the stored arc has them on whole numbers already, as a rounded rectangle’s corner does.

Supported range: the painted stroke and the stroke the hit test covers agree to within 2.5% of the stroke width when, in the path’s own units,

  • coordinates and arc radii are at most 50,000 times the stroke width,
  • an arc’s rotation is within one turn,
  • an arc within 15 degrees of half a turn has radii of at most 25 times

the stroke width, and

  • an arc whose points are rounded to whole numbers has them on whole

numbers as stored. That is on top of the 0.05 of a unit, measured on the page, that the hit test allows itself when it splits a curve into straight pieces. The session test checks the range at its edge. Outside it the two drift apart in proportion. From 2^24 (16,777,216) units on, a step is 2 units or more, so a vertex is drawn up to a unit or more from where it is stored, and a stroke thinner than that can be painted wholly outside the place the hit test looks. Skia also works a stroke’s outline out in single precision, in the path’s own units, so by then the painted stroke is not exactly the stroke of the drawn segments either. docs/render-core/futures.md says what closing this would take.

void taco::render::skia_path_ops::resetVectorPathOpsStats()

source

struct taco::render::Resource

source
std::string idsource
ResourceKind kindsource
std::vector<uint8_t> bytessource

enum class taco::render::ResourceKind : uint8_t

source

TypeScript: ResourceKind

kFont = 1source
kImage = 2source
kBlob = 3source

struct taco::render::RetainedPaint

source

TypeScript: RetainedPaintRecord

PaintKind kindsource
BlendMode blend_modesource

Wire version 19: the byte after the kind is the paint’s blend mode, and the next byte’s bit 1 hides the paint. A hidden paint keeps its place in the list but draws nothing; a blended one blends with the paints below it on the same node, inside the node’s own paint layer.

bool hiddensource
double opacitysource
Color colorsource
double anglesource

Linear gradients use angle alone. Radial and angular gradients key their center to the shape bounds as fractions; radial adds elliptical radii as fractions, angular reuses angle as its start angle.

double center_xsource
double center_ysource
double radius_xsource
double radius_ysource
std::vector<GradientStop> stopssource
std::string asset_idsource

Image paints name a logical asset attached through the media frame set.

MediaFit image_fitsource
bool isGradient() constsource

struct taco::render::RetainedShadow

source
bool innersource

Wire version 9 prefixes each record with its kind. An inner shadow paints inside the shape over its fills; a drop shadow paints behind the node.

double xsource
double ysource
double blursource
double spreadsource
Color colorsource

struct taco::render::RetainedStroke

source
Color colorsource
double widthsource
StrokePosition positionsource
std::vector<double> dashsource
bool has_paintsource

Wire version 6 can carry a full stroke paint. When absent the compact color remains authoritative. Every stroke after the first carries one.

RetainedPaint paintsource
bool has_sidessource

Wire version 18 can give a container stroke its own weight per side, in the order top, right, bottom, left; width then holds the largest one.

std::array<double, 4> sidessource
bool has_stylesource

Wire version 23 can set the join, the ends of every dash and the miter limit together. Without them a path strokes with round joins and round dash ends and a container with miter joins (limit 4) and flat dash ends.

StrokeJoin joinsource
DashCap dash_capsource
double miter_limitsource

struct taco::render::RichTextRun

source
uint32_t start_bytesource
uint32_t end_bytesource
uint32_t style_indexsource

struct taco::render::RichTextStyle

source
std::vector<std::string> font_resource_idssource
std::string languagesource
double font_sizesource
double line_heightsource
double letter_spacingsource
uint16_t weightsource
bool italicsource
bool underlinesource
bool strikethroughsource
Color colorsource
std::vector<OpenTypeFeature> open_type_featuressource
std::vector<FontVariation> font_variationssource

struct taco::render::RoundedPathData

source

A path with its rounded corners worked into its segments (roundPathVertices). ids owns the identifiers of the vertices and segments a rounded corner adds.

std::deque<std::string> idssource
PathData datasource

bool taco::render::roundPathVertices( const PathData &authored, RoundedPathData *out, std::string *error )

source

Fills out with authored as the renderer paints it. A vertex with a corner radius is rounded when exactly two segment ends meet there, they belong to two different segments, both are lines or cubics with a direction at the vertex, and they are neither in one straight line nor folded back on each other. Each of the two segments is cut short and a corner segment joins the cuts. Every other vertex is left as stored, whatever its radius: the end of a path, a vertex three or more segment ends meet at, a vertex an arc meets, and a vertex whose two ends are one closed curve’s. No vertex of out has a corner radius. This is the only place that decides whether a corner is rounded: the renderer paints out and the hit test reads the stroke’s joins from it (buildStrokeContours), so a join is hit at exactly the vertices that are painted with one. False, with error set, when a segment or region names a missing vertex or segment.

struct taco::render::SceneHitIndex

source
bool validsource
Bounds boundssource
std::array<std::vector<uint32_t>, kCellCount> cellssource
std::vector<uint32_t> unboundedsource

enum class taco::render::SegmentDirection : uint8_t

source
kForward = 0source
kReverse = 1source

bool taco::render::segmentEndDirection( const VectorSegment &segment, double start_x, double start_y, double end_x, double end_y, bool at_start, double *away_x, double *away_y )

source

The unit direction a segment leaves one of its ends in, pointing from that end into the segment: the direction the stroke has there, which its joins and square ends are built along. start and end are the segment’s own start and end vertices; at_start picks the end. The direction is read from the segment as drawn (prepareDrawnSegment): it points from the end toward the nearest drawn point that is not at the end itself. So a line leaves along itself; a cubic along its handle, however short, or toward its next control point when the handle is too short to move its control point off the vertex in single precision, or along its chord when that is too; and an arc toward the control point of its first or last piece, which lies on the ellipse’s tangent. False when there is no direction to take: when every drawn point is the same, when a line’s two drawn ends are within kDrawnPointTolerance of each other, when the same holds for a cubic that has no bend to draw (at least two of its three steps from one point to the next have no length), and when the segment cannot be drawn at all. The renderer chooses which ends to join from this and the hit test builds the join from it, at the end as drawn, so both call this instead of measuring the curve in their own ways. This only reads the drawn points; what is painted is the drawn segment unchanged.

Skia’s two stroke painters agree with this for a curve that bends. They differ from each other for a cubic whose control points all lie within about 0.3% of its length of one straight line, and for an arc that sweeps less than about half a degree: the one that runs on the processor paints either as straight lines, so a turn that small at an end does not move its join, while the one that runs on the graphics card follows the curve as this does (see docs/render-core/futures.md).

enum class taco::render::SegmentKind : uint8_t

source
kLine = 1source
kCubic = 2source
kEllipticalArc = 3source

struct taco::render::SessionApplyStats

source

Counters for apply/digest cost. Tests assert complexity bounds; production callers may ignore them. digest_walks is incremented only when digest() actually walks the retained scene.

uint64_t digest_walkssource
uint64_t payload_walkssource
uint64_t geometry_nodes_computedsource
uint64_t path_bounds_index_buildssource
uint64_t dirty_nodes_visitedsource
uint64_t scene_nodes_validatedsource
uint64_t geometry_nodes_validatedsource
uint64_t hit_path_index_buildssource
uint64_t hit_segment_flatten_callssource
uint64_t hit_clip_parent_string_copiessource
uint64_t hit_rounded_path_buildssource

How often a hit test worked out a path with its rounded corners (roundPathVertices) and a path’s stroke contours (buildStrokeContours). Both are kept per node (PaintedStrokeCache), so hit tests on a node that has not changed add to these once, for as long as what is kept for the node fits that cache’s size limit.

uint64_t hit_stroke_contour_buildssource
uint64_t hit_test_traversalssource
uint64_t hit_nodes_testedsource
uint64_t scene_state_node_entries_copiedsource

bool taco::render::singlePrecision(double value, float *rounded)

source

Rounds value to single precision, the precision the renderer builds every path in. False when value is not finite or is too large for single precision to hold. Every number that reaches a drawn path is rounded here and nowhere else.

struct taco::render::skia_adapter::StaticImageFrame

source

Internal callers, including a GPU bridge, may supply an already-created SkImage. A replacement image may be raster- or texture-backed; the public C boundary constructs owned raster images before entering this path.

std::string_view asset_idsource
uint64_t content_revisionsource
uint32_t kindsource
StaticImageFrameDisposition dispositionsource
sk_sp<SkImage> imagesource

enum class taco::render::skia_adapter::StaticImageFrameDisposition : uint8_t

source
kReuseActivesource
kReplacesource

double taco::render::strokeCapReach(StrokeCap cap, double width)

source

The farthest distance from the endpoint that a cap of stroke width width paints, including the stroke’s own half width around arrow arms.

struct taco::render::StrokeContour

source

One run of segments a stroke is painted along without a break. The stroke has a join between each step and the next, and between the last and the first when closed; the two ends of a contour that is not closed get the stroke’s ends instead. The renderer paints every step. Where a step is too short to have a direction (segmentEndDirection), the hit test reads the join from the steps on either side of it and a stroke’s end from the first or last step that has one.

std::vector<StrokeContourStep> stepssource
bool closedsource

StrokeContourOrder taco::render::strokeContourOrder( const NodeStyle &style, const PathData &path, const RetainedStroke &stroke )

source

Which contours of a path the renderer paints stroke along. path is the path as stored, which says whether it has stroke caps.

enum class taco::render::StrokeContourOrder : uint8_t

source
kStoredsource

Segments outside every region loop keep the order and direction they are stored in: a segment continues the contour of the one stored before it only when it starts where that one ends, at a vertex no other segment touches. A dashed stroke and a path with stroke caps are painted along these, because a dash pattern and a cap follow the stored order.

kJoinedsource

Segments outside every region loop are followed through every vertex where the stroke should join, whatever order and direction they are stored in: a vertex two segment ends touch joins those two, and a vertex three or more touch joins the two branchJoinPair chooses among the ends that have a direction, or the only two that have one. A solid stroke that names its join is painted along these.

struct taco::render::StrokeContourStep

source

One segment of a stroke contour: forward when the contour runs through it from its start vertex to its end vertex.

const VectorSegment *segmentsource
bool forwardsource

StrokeOutlineStats &taco::render::skia_adapter::strokeOutlineStats()

source

struct taco::render::skia_adapter::StrokeOutlineStats

source

Counts for tests of buildNodeStrokeOutline: how many strokes were turned into the area they cover (Skia made their dashes and stroked them), and how many of those went on to Skia PathOps (one refused for too many contours or too much work does not). capped_stroke_bodies counts the lines of strokes with end shapes that were dashed and stroked, for painting and for outlines alike. dash_estimate is the last count of dashes made before dashing; path_ops_segments and path_ops_work are the segments and the estimate of work last checked before PathOps (the work of a refused stroke is only counted until it passes the limit).

uint64_t stroked_pathssource
uint64_t path_ops_runssource
uint64_t capped_stroke_bodiessource
double dash_estimatesource
uint64_t path_ops_segmentssource
uint64_t path_ops_worksource

enum class taco::render::StrokePosition : uint8_t

source

TypeScript: RetainedStrokePosition

kCenter = 0source
kInside = 1source
kOutside = 2source

bool taco::render::strokesJoinOpenRuns(const NodeStyle &style)

source

Whether any stroke of style names its join (wire version 23). Only then do segments outside the region loops run on into each other; otherwise each is a contour of its own.

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_1 1u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_1_SIZE 32u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_2 2u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_2_SIZE 40u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_3 3u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_3_SIZE 48u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_4 4u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_4_SIZE 56u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_5 5u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_5_SIZE 64u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_6 6u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_6_SIZE 72u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_7 7u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_7_SIZE 80u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_8 8u

source

#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION_8_SIZE 88u

source

#define TACO_RENDER_FONT_METADATA_MAX_AXES 65535u

source

TypeScript: RENDER_FONT_METADATA_MAX_AXES

#define TACO_RENDER_FONT_METADATA_MAX_FEATURES 131070u

source

TypeScript: RENDER_FONT_METADATA_MAX_FEATURES

#define TACO_RENDER_FONT_METADATA_VERSION 1u

source

#define TACO_RENDER_INCREMENTAL_RENDER_VERSION 1u

source

#define TACO_RENDER_MEDIA_FRAME_MAX_BYTES TACO_RENDER_STATIC_IMAGE_FRAME_MAX_BYTES

source

#define TACO_RENDER_MEDIA_FRAME_MAX_COUNT TACO_RENDER_STATIC_IMAGE_FRAME_MAX_COUNT

source

Image and video frames share one bounded decoded-equivalent media budget.

#define TACO_RENDER_MEDIA_FRAME_MAX_DIMENSION TACO_RENDER_STATIC_IMAGE_FRAME_MAX_DIMENSION

source

#define TACO_RENDER_MEDIA_FRAME_REUSE_ACTIVE TACO_RENDER_STATIC_IMAGE_FRAME_REUSE_ACTIVE

source

#define TACO_RENDER_MEDIA_FRAME_RGBA8888 TACO_RENDER_STATIC_IMAGE_FRAME_RGBA8888

source

#define TACO_RENDER_STATIC_IMAGE_FRAME_MAX_BYTES (64u * 1024u * 1024u)

source

#define TACO_RENDER_STATIC_IMAGE_FRAME_MAX_COUNT 16u

source

#define TACO_RENDER_STATIC_IMAGE_FRAME_MAX_DIMENSION 32768u

source

#define TACO_RENDER_STATIC_IMAGE_FRAME_VERSION 1u

source

#define TACO_RENDER_STROKE_OUTLINE_MAX_NAMESPACE_BYTES TACO_RENDER_VECTOR_BOOLEAN_MAX_NAMESPACE_BYTES

source

#define TACO_RENDER_STROKE_OUTLINE_MAX_NETWORK_BYTES TACO_RENDER_VECTOR_BOOLEAN_MAX_NETWORK_BYTES

source

#define TACO_RENDER_TEXT_QUERY_MAX_RECORDS 65536u

source

Text geometry work is exact up to this fixed ceiling. Larger caret sets, or ranges/overlays spanning more selected UTF-8 bytes plus paragraph separators, return LIMIT_EXCEEDED without writes. This keeps native and 512 MiB Wasm sessions predictably bounded.

#define TACO_RENDER_TEXT_QUERY_VERSION 1u

source

#define TACO_RENDER_VECTOR_BOOLEAN_MAX_NAMESPACE_BYTES 1008u

source

#define TACO_RENDER_VECTOR_BOOLEAN_MAX_NETWORK_BYTES (64u * 1024u * 1024u)

source

#define TACO_RENDER_VECTOR_BOOLEAN_VERSION 1u

source

#define TACO_RENDER_VECTOR_EDITING_OVERLAY_MAX_COMPONENTS 65536u

source

#define TACO_RENDER_VECTOR_EDITING_OVERLAY_MAX_NETWORK_BYTES (64u * 1024u * 1024u)

source

#define TACO_RENDER_VECTOR_EDITING_OVERLAY_VERSION 2u

source

#define TACO_RENDER_VECTOR_QUERY_MAX_IDENTIFIER_BYTES (16u * 1024u * 1024u)

source

#define TACO_RENDER_VECTOR_QUERY_MAX_RECORDS 65536u

source

Geometry applies this ceiling to each record array; hit results apply it once.

#define TACO_RENDER_VECTOR_QUERY_VERSION 1u

source

#define TACO_RENDER_VIDEO_FRAME_MAX_BYTES TACO_RENDER_MEDIA_FRAME_MAX_BYTES

source

#define TACO_RENDER_VIDEO_FRAME_MAX_COUNT TACO_RENDER_MEDIA_FRAME_MAX_COUNT

source

#define TACO_RENDER_VIDEO_FRAME_MAX_DIMENSION TACO_RENDER_MEDIA_FRAME_MAX_DIMENSION

source

#define TACO_RENDER_VIDEO_FRAME_VERSION 1u

source

struct TacoRenderDeviceRect

source
uint32_t xsource
uint32_t ysource
uint32_t widthsource
uint32_t heightsource

enum TacoRenderExportFlags

source
TACO_RENDER_EXPORT_FLAG_SVG_NODE_MARKERS = 1source

SVG only: surrounds each painted node’s elements with two link annotations, <a xlink:href="taco-node-start:HEX"> and <a xlink:href="taco-node-end:HEX">, where HEX is the node identifier’s UTF-8 bytes in lowercase hexadecimal, so a host can turn them into id attributes.

enum TacoRenderExportImageSampling

source

TypeScript: RenderExportImageSampling

TACO_RENDER_EXPORT_IMAGE_SAMPLING_DEFAULT = 0source
TACO_RENDER_EXPORT_IMAGE_SAMPLING_CUBIC = 1source

Bicubic (Mitchell) sampling, for photographs and gradients.

TACO_RENDER_EXPORT_IMAGE_SAMPLING_NEAREST = 2source

Nearest-pixel sampling, for pixel art and hard edges.

struct TacoRenderFontAxis

source

One sorted variable-font axis from the exact retained SkTypeface.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
char[4] tagsource
uint8_t hiddensource
uint8_t[7] reservedsource
double minimumsource
double default_valuesource
double maximumsource

struct TacoRenderFontFeature

source

One sorted, unique, printable four-byte feature tag from GSUB or GPOS.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
char[4] tagsource

struct TacoRenderFontMetadataQuery

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource

enum TacoRenderGeometryFlags

source
TACO_RENDER_GEOMETRY_FLAG_HAS_LOCAL_BOUNDS = 1source
TACO_RENDER_GEOMETRY_FLAG_HAS_WORLD_BOUNDS = 2source
TACO_RENDER_GEOMETRY_FLAG_HAS_VISIBLE_WORLD_BOUNDS = 4source
TACO_RENDER_GEOMETRY_FLAG_EFFECTIVELY_VISIBLE = 8source

enum TacoRenderIncrementalFrameFlags

source
TACO_RENDER_INCREMENTAL_FRAME_HAS_DAMAGE = 1source
TACO_RENDER_INCREMENTAL_FRAME_FULL_PAINT = 2source
TACO_RENDER_INCREMENTAL_FRAME_REUSED_TARGET = 4source

enum TacoRenderIncrementalRenderFlags

source
TACO_RENDER_INCREMENTAL_RENDER_FORCE_FULL = 1source

typedef TacoRenderStaticImageFrame TacoRenderMediaFrame

source

Video-frame version 1 deliberately reuses the proven static-image physical record layout. The kind field distinguishes image and video ownership. Mixed sets are globally sorted by logical asset ID; video-update sets contain only the scene’s exact, deduplicated video asset set.

typedef TacoRenderStaticImageFrameDisposition TacoRenderMediaFrameDisposition

source

enum TacoRenderMediaFrameKind

source
TACO_RENDER_MEDIA_FRAME_STATIC_IMAGE = 1source
TACO_RENDER_MEDIA_FRAME_VIDEO = 2source

typedef TacoRenderStaticImageFrameSet TacoRenderMediaFrameSet

source

enum TacoRenderNodeKind

source

The kind of a retained node, as reported by node and geometry queries.

TACO_RENDER_NODE_PATH = 1source
TACO_RENDER_NODE_TEXT = 2source
TACO_RENDER_NODE_CONTAINER = 3source
TACO_RENDER_NODE_MEDIA = 4source

struct TacoRenderRect

source

An axis-aligned rectangle: top-left corner plus size.

double xsource
double ysource
double widthsource
double heightsource

enum TacoRenderStaticImageFrameDisposition

source
TACO_RENDER_STATIC_IMAGE_FRAME_REUSE_ACTIVE = 1source
TACO_RENDER_STATIC_IMAGE_FRAME_RGBA8888 = 2source

enum TacoRenderTextAffinity

source

TypeScript: RenderTextAffinity

TACO_RENDER_TEXT_AFFINITY_UPSTREAM = 0source
TACO_RENDER_TEXT_AFFINITY_DOWNSTREAM = 1source

enum TacoRenderTextAutoResize

source
TACO_RENDER_TEXT_AUTO_RESIZE_NONE = 0source
TACO_RENDER_TEXT_AUTO_RESIZE_HEIGHT = 1source
TACO_RENDER_TEXT_AUTO_RESIZE_WIDTH_AND_HEIGHT = 2source

struct TacoRenderTextCaret

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
TacoRenderTextPosition positionsource
TacoRenderRect local_boundssource

enum TacoRenderTextEditingOverlayFlags

source
TACO_RENDER_TEXT_EDITING_OVERLAY_HAS_COMPOSITION = 1source
TACO_RENDER_TEXT_EDITING_OVERLAY_CARET_VISIBLE = 2source

struct TacoRenderTextHitQuery

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource
double local_xsource
double local_ysource

struct TacoRenderTextHitResult

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t insidesource
uint64_t generationsource
TacoRenderTextPosition positionsource

struct TacoRenderTextLayoutMetrics

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t auto_resizesource
uint64_t generationsource
double frame_widthsource
double frame_heightsource
double content_widthsource
double content_heightsource
double suggested_widthsource
double suggested_heightsource
uint32_t paragraph_countsource
uint32_t line_countsource
uint32_t caret_countsource
uint32_t reservedsource

enum TacoRenderTextNavigationOperation

source

TypeScript: RenderTextNavigationOperation

TACO_RENDER_TEXT_NAVIGATION_GRAPHEME_BACKWARD = 0source
TACO_RENDER_TEXT_NAVIGATION_GRAPHEME_FORWARD = 1source
TACO_RENDER_TEXT_NAVIGATION_WORD_BACKWARD = 2source
TACO_RENDER_TEXT_NAVIGATION_WORD_FORWARD = 3source
TACO_RENDER_TEXT_NAVIGATION_VISUAL_LEFT = 4source
TACO_RENDER_TEXT_NAVIGATION_VISUAL_RIGHT = 5source
TACO_RENDER_TEXT_NAVIGATION_LINE_UP = 6source
TACO_RENDER_TEXT_NAVIGATION_LINE_DOWN = 7source
TACO_RENDER_TEXT_NAVIGATION_LINE_START = 8source
TACO_RENDER_TEXT_NAVIGATION_LINE_END = 9source
TACO_RENDER_TEXT_NAVIGATION_PARAGRAPH_START = 10source
TACO_RENDER_TEXT_NAVIGATION_PARAGRAPH_END = 11source
TACO_RENDER_TEXT_NAVIGATION_DOCUMENT_START = 12source
TACO_RENDER_TEXT_NAVIGATION_DOCUMENT_END = 13source

struct TacoRenderTextNavigationQuery

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t operationsource
uint64_t expected_generationsource
TacoRenderTextPosition fromsource
double preferred_local_xsource
uint32_t has_preferred_local_xsource
uint32_t reservedsource

struct TacoRenderTextNavigationResult

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t has_resultsource
uint64_t generationsource
TacoRenderTextPosition positionsource
double preferred_local_xsource
uint32_t has_preferred_local_xsource
uint32_t reservedsource

struct TacoRenderTextQuery

source

The common text query input: the scene generation the caller expects. reserved must be 0.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource

struct TacoRenderTextRangeQuery

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource
TacoRenderTextPosition anchorsource
TacoRenderTextPosition focussource

struct TacoRenderTextRect

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
TacoRenderRect local_boundssource

enum TacoRenderVectorBooleanOperation

source

TypeScript: RenderVectorBooleanOperation

TACO_RENDER_VECTOR_BOOLEAN_UNION = 1source
TACO_RENDER_VECTOR_BOOLEAN_INTERSECTION = 2source
TACO_RENDER_VECTOR_BOOLEAN_DIFFERENCE = 3source
TACO_RENDER_VECTOR_BOOLEAN_EXCLUSION = 4source

enum TacoRenderVectorComponentKind

source
TACO_RENDER_VECTOR_COMPONENT_VERTEX = 1source
TACO_RENDER_VECTOR_COMPONENT_CUBIC_HANDLE = 2source
TACO_RENDER_VECTOR_COMPONENT_SEGMENT = 3source
TACO_RENDER_VECTOR_COMPONENT_REGION = 4source

struct TacoRenderVectorCubicHandle

source

TypeScript: decodeWasmVectorGeometry, PORTABLE_WASM_ABI_LAYOUT

TypeScript, through decodeWasmVectorGeometry: RenderCoreSession.getVectorGeometry

uint32_t struct_sizesource
uint32_t endpointsource
uint32_t id_offsetsource
uint32_t id_sizesource
uint32_t segment_id_offsetsource
uint32_t segment_id_sizesource
uint32_t vertex_id_offsetsource
uint32_t vertex_id_sizesource
double local_xsource
double local_ysource
double screen_xsource
double screen_ysource

struct TacoRenderVectorEditingComponent

source

One selected component in a transient vector-editing overlay. Identity is the pair (kind, identifier); identifiers may coincide across kinds. At most one record may carry PRIMARY, and that record is part of the selection.

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t kindsource
uint32_t id_offsetsource
uint32_t id_sizesource
uint32_t flagssource
uint32_t[3] reservedsource

enum TacoRenderVectorEditingComponentFlags

source
TACO_RENDER_VECTOR_EDITING_COMPONENT_PRIMARY = 1source

struct TacoRenderVectorEditingOverlayQuery

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource

enum TacoRenderVectorEditingPendingHandleFlags

source

A pending handle is a curve handle that no segment of the preview network owns yet: the one a Pen drag pulls out of a vertex for the segment drawn from it next. It is editing chrome only (a guide line and a knob) and never changes the painted preview. MIRRORED also draws its exact mirror about the vertex, for a vertex no segment arrives at. Without PRESENT every pending handle field is zero.

TACO_RENDER_VECTOR_EDITING_PENDING_HANDLE_PRESENT = 1source
TACO_RENDER_VECTOR_EDITING_PENDING_HANDLE_MIRRORED = 2source

enum TacoRenderVectorEndpoint

source
TACO_RENDER_VECTOR_ENDPOINT_NONE = 0source
TACO_RENDER_VECTOR_ENDPOINT_START = 1source
TACO_RENDER_VECTOR_ENDPOINT_END = 2source

struct TacoRenderVectorHit

source

TypeScript: decodeWasmVectorHits, PORTABLE_WASM_ABI_LAYOUT

TypeScript, through decodeWasmVectorHits: RenderCoreSession.hitTestVector

uint32_t struct_sizesource
uint32_t kindsource
uint32_t id_offsetsource
uint32_t id_sizesource
uint32_t prioritysource
uint32_t endpointsource
uint32_t authored_ordersource
uint32_t reservedsource
double distance_css_pxsource

struct TacoRenderVectorHitBufferResult

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t hit_countsource
uint32_t identifier_byte_countsource
uint32_t reservedsource
uint64_t generationsource

struct TacoRenderVectorHitQuery

source

TypeScript: PORTABLE_WASM_ABI_LAYOUT

uint32_t struct_sizesource
uint32_t reservedsource
uint64_t expected_generationsource
double screen_xsource
double screen_ysource
double tolerance_css_pxsource
double[6] document_to_screensource

struct TacoRenderVectorRegion

source

TypeScript: decodeWasmVectorGeometry, PORTABLE_WASM_ABI_LAYOUT

TypeScript, through decodeWasmVectorGeometry: RenderCoreSession.getVectorGeometry

uint32_t struct_sizesource
uint32_t winding_rulesource
uint32_t id_offsetsource
uint32_t id_sizesource
uint32_t authored_ordersource
uint32_t[3] reservedsource
TacoRenderRect local_boundssource
TacoRenderRect screen_boundssource

struct TacoRenderVectorSegment

source

TypeScript: decodeWasmVectorGeometry, PORTABLE_WASM_ABI_LAYOUT

TypeScript, through decodeWasmVectorGeometry: RenderCoreSession.getVectorGeometry

uint32_t struct_sizesource
uint32_t kindsource
uint32_t id_offsetsource
uint32_t id_sizesource
uint32_t start_vertex_id_offsetsource
uint32_t start_vertex_id_sizesource
uint32_t end_vertex_id_offsetsource
uint32_t end_vertex_id_sizesource
uint32_t start_handle_id_offsetsource
uint32_t start_handle_id_sizesource
uint32_t end_handle_id_offsetsource
uint32_t end_handle_id_sizesource
uint32_t flagssource
uint32_t authored_ordersource
TacoRenderRect local_boundssource
TacoRenderRect screen_boundssource
double radius_xsource
double radius_ysource
double x_axis_rotationsource

enum TacoRenderVectorSegmentFlags

source
TACO_RENDER_VECTOR_SEGMENT_LARGE_ARC = 1source
TACO_RENDER_VECTOR_SEGMENT_SWEEP = 2source

enum TacoRenderVectorSegmentKind

source
TACO_RENDER_VECTOR_SEGMENT_LINE = 1source
TACO_RENDER_VECTOR_SEGMENT_CUBIC = 2source
TACO_RENDER_VECTOR_SEGMENT_ELLIPTICAL_ARC = 3source

struct TacoRenderVectorVertex

source

TypeScript: decodeWasmVectorGeometry, PORTABLE_WASM_ABI_LAYOUT

TypeScript, through decodeWasmVectorGeometry: RenderCoreSession.getVectorGeometry

uint32_t struct_sizesource
uint32_t id_offsetsource
uint32_t id_sizesource
uint32_t reservedsource
double local_xsource
double local_ysource
double screen_xsource
double screen_ysource

enum TacoRenderVectorWindingRule

source
TACO_RENDER_VECTOR_WINDING_NONZERO = 0source
TACO_RENDER_VECTOR_WINDING_EVENODD = 1source

enum TacoRenderViewportMode

source

How taco_render_session_map_viewport scales the scene into a viewport.

TACO_RENDER_VIEWPORT_FIT = 0source

Uniform scale; the whole scene fits inside the viewport.

TACO_RENDER_VIEWPORT_FILL = 1source

Uniform scale; the scene covers the whole viewport.

TACO_RENDER_VIEWPORT_STRETCH = 2source

Separate x and y scales; the scene matches the viewport exactly.

enum class taco::render::TangentConstraintMode : uint8_t

source
kAngle = 0source
kAngleAndLength = 1source

enum class taco::render::TextAlign : uint8_t

source
kLeft = 0source
kCenter = 1source
kRight = 2source
kJustify = 3source

enum class taco::render::TextAutoResize : uint8_t

source
kNone = 0source
kHeight = 1source
kWidthAndHeight = 2source

struct taco::render::TextData

source
double widthsource
double heightsource
double line_heightsource
TextWrap wrapsource
TextAlign alignsource
std::string textsource
std::vector<TextRun> runssource
TextVerticalAlign vertical_alignsource
TextOverflow overflowsource
TextAutoResize auto_resizesource
std::vector<RichTextStyle> stylessource
std::vector<TextParagraph> paragraphssource

enum class taco::render::TextDirection : uint8_t

source
kAuto = 0source
kLtr = 1source
kRtl = 2source

enum class taco::render::TextListKind : uint8_t

source
kNone = 0source
kOrdered = 1source
kUnordered = 2source

struct taco::render::TextListMarker

source
bool has_textsource
std::string textsource
bool has_stylesource
uint32_t style_indexsource
bool has_gapsource
double gapsource

enum class taco::render::TextOverflow : uint8_t

source
kClip = 0source
kVisible = 1source

struct taco::render::TextParagraph

source
TextDirection directionsource
TextAlign alignsource
double spacing_beforesource
double spacing_aftersource
bool has_indentsource
TextParagraphIndent indentsource
TextParagraphList listsource
std::string textsource
std::vector<RichTextRun> runssource

struct taco::render::TextParagraphIndent

source
double leftsource
double rightsource
double first_linesource

struct taco::render::TextParagraphList

source
TextListKind kindsource
uint32_t levelsource
bool has_startsource
uint32_t startsource
bool has_markersource
TextListMarker markersource

struct taco::render::TextRun

source
uint32_t start_bytesource
uint32_t end_bytesource
std::string font_resource_idsource
double font_sizesource
double letter_spacingsource
uint16_t weightsource
bool italicsource
bool underlinesource
bool strikethroughsource
Color colorsource
std::vector<OpenTypeFeature> open_type_featuressource
std::vector<FontVariation> font_variationssource

enum class taco::render::TextVerticalAlign : uint8_t

source
kTop = 0source
kMiddle = 1source
kBottom = 2source

enum class taco::render::TextWrap : uint8_t

source
kNone = 0source
kWord = 1source
kCharacter = 2source

TacoRenderRect taco::render::toRect(const Bounds &bounds)

source

C ABI: taco_render_session_hit_test, taco_render_session_get_last_dirty_bounds

struct taco::render::Transform

source

An affine matrix stored as values = [a, b, c, d, e, f], the same layout as the TypeScript Transform. A point maps as (x′,y′)=(ax+cy+e, bx+dy+f)(x', y') = (a x + c y + e, \ b x + d y + f).

std::array<double, 6> valuessource

Bounds taco::render::unionBounds(const Bounds &first, const Bounds &second)

source

struct taco::render::VectorGeometryQueryResult

source
std::vector<TacoRenderVectorVertex> verticessource
std::vector<TacoRenderVectorCubicHandle> handlessource
std::vector<TacoRenderVectorSegment> segmentssource
std::vector<TacoRenderVectorRegion> regionssource
std::string identifierssource

struct taco::render::VectorHandle

source
std::string_view idsource
double dxsource
double dysource

struct taco::render::VectorHitQueryResult

source
std::vector<TacoRenderVectorHit> hitssource
std::string identifierssource

VectorPathOpsStats &taco::render::skia_path_ops::vectorPathOpsStats()

source

struct taco::render::skia_path_ops::VectorPathOpsStats

source
int id_concat_temporariessource
uint64_t contour_pair_checkssource

Pairs of contours vectorNetworkFromPath compared to group contours into regions.

uint64_t projected_segmentssource

Segments vectorNetworkFromPath last counted in a path before making any (counting stops just past the limit), and segments made from path verbs since the last reset: none when a path was refused by that count.

uint64_t contour_segmentssource

struct taco::render::VectorRegion

source
std::string_view idsource
WindingRule winding_rulesource
bool has_explicit_fillssource
std::vector<RetainedPaint> fillssource
std::vector<VectorRegionLoop> loopssource

struct taco::render::VectorRegionLoop

source
bool implicit_closesource
std::vector<VectorSegmentReference> segmentssource

struct taco::render::VectorSegment

source
std::string_view idsource
std::string_view start_vertex_idsource
std::string_view end_vertex_idsource
SegmentKind kindsource
VectorHandle start_handlesource
VectorHandle end_handlesource
double radius_xsource
double radius_ysource
double x_axis_rotationsource
bool large_arcsource
bool sweepsource

struct taco::render::VectorSegmentReference

source
std::string_view segment_idsource
SegmentDirection directionsource

struct taco::render::VectorTangentConstraint

source
std::string_view vertex_idsource
std::array<std::string_view, 2> handle_idssource
TangentConstraintMode modesource

struct taco::render::VectorVertex

source
std::string_view idsource
double xsource
double ysource
double corner_radiussource

Wire version 12: a positive radius rounds this vertex at paint time when it joins exactly two line or cubic segments. The authored position stays the editing and query geometry.

enum class taco::render::WindingRule : uint8_t

source
kNonzero = 0source
kEvenodd = 1source