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 schema | 0.27.0 | CURRENT_DOCUMENT_SCHEMA_VERSION; see the schema history |
|---|---|---|
| Stored history | 1 | CURRENT_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.
sourcetype DesignDocument = DesignDocumentV1A Kimi Design document. Values returned by this package are deeply frozen; change one with
applyOperationsor through aDocumentStore.
sourceinterface DesignDocumentV1The stored shape of a document at
CURRENT_DOCUMENT_SCHEMA_VERSION. Use theDesignDocumentalias in code.The data is normalized into flat maps keyed by ID.
assertDocumentchecks 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.assetIdis not checked against the asset map.)readonly schemaVersion: typeof CURRENT_DOCUMENT_SCHEMA_VERSIONsourcereadonly id: DocumentIdsourcereadonly title: stringsourcereadonly pageOrder: readonly PageId[]sourceEvery page ID exactly once, in display order.
readonly pages: Readonly<Record<string, Page>>sourcereadonly nodes: Readonly<Record<string, SceneNode>>sourceEvery node on every page, keyed by
NodeId.readonly assets: Readonly<Record<string, Asset>>sourceImages, videos and fonts, keyed by
AssetId.readonly theme: DocumentThemesourcereadonly components?: Readonly<Record<string, ComponentDefinition>>since 0.23.0sourceReusable layer trees, keyed by
ComponentId. Omitted when empty. Each component’s layers live innodesunder itsrootId, on no page.readonly metadata?: Readonly<Record<string, JsonValue>>sourceFree-form JSON data kept with the document. The model does not read it.
sourceinterface PageOne 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: PageIdsourceMatches the page’s key in
DesignDocument.pages.readonly name: stringsourceMust not be empty.
readonly rootId: NodeIdsourceThe page’s root node: a frame or group with no parent.
readonly guides?: readonly Guide[]since 0.11.0sourceRuler guides on the page’s canvas, in page coordinates. Omitted when empty.
readonly variableModes?: VariableModessince 0.26.0sourceThe 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
since 0.11.0sourceinterface GuideA 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: numbersourcePosition 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.
sourcetype DocumentSchemaVersion = number | SemanticVersionA document schema version. Current documents use a
SemanticVersion; the number form is only read from legacy files saved before version0.0.0.
sourcetype SemanticVersion = `${number}.${number}.${number}`A
major.minor.patchversion string, such as"0.14.0".
sourceconst CURRENT_DOCUMENT_SCHEMA_VERSION: "0.27.0"The schema version this package reads and writes.
deserializeDocumentmigrates older documents to it, andassertDocumentrejects any other version.
sourcefunction pageRootFrame( document: DesignDocument, pageId: PageId ): Rect | undefinedA 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).
sourcetype SceneNode = | FrameNode | GroupNode | TextNode | CodeNode | ShapeNode | ImageNode | VideoNode | ConnectorNode | InstanceNodeAny node in a page’s tree, told apart by its
typefield. Nodes live in the flatDesignDocument.nodesmap; containers list their children by ID and parents are not stored.
sourceinterface NodeBaseFields every
SceneNodehas.readonly id: NodeIdsourceMatches the node’s key in
DesignDocument.nodes.readonly name: stringsourceThe layer name. Must not be empty.
readonly visible: booleansourcereadonly locked: booleansourceProtects the node from edits on the editor canvas.
readonly opacity: numbersourceFrom 0 (transparent) to 1 (opaque).
readonly blendMode?: BlendModesince 0.8.0sourceHow the node composites onto what lies behind it; normal when omitted.
readonly mask?: booleansince 0.8.0sourceA mask hides its later siblings outside its own painted alpha and paints nothing itself. A hidden mask masks nothing.
readonly aspectRatioLocked?: booleansince 0.1.1sourceKeeps width and height proportional when the node is resized.
readonly variableBindings?: VariableBindingssourceProperties whose values come from theme variables.
readonly exportSettings?: readonly ExportSetting[]since 0.14.0sourceThe node’s Export section rows, in order. Omitted when empty.
readonly expressions?: NodeExpressionssince 0.24.0sourceComputed fields: expression source text keyed by field, for nodes under a component root only (see
ComputableFieldandcomputableFieldsOf). The plain field keeps the base value, which the expression reads asstoredand 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.0sourceA 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.0sourceHow 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 itsconstraints. Ignored when the parent has no auto layout.readonly variableModes?: VariableModessince 0.26.0sourceThe 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>>sourceFree-form JSON data kept with the node. The model does not read it.
sourceinterface FramedNodeBase extends NodeBaseFields of every node placed by a rectangle: all kinds except
PathNode, which uses a transform matrix instead.readonly frame: RectsourcePosition and unrotated size, in the parent’s coordinate system (relative to the parent’s frame’s top-left corner).
readonly rotation: numbersourceDegrees, clockwise on screen, about the frame’s center.
readonly flipX?: booleansourceMirrors the rendered node around its local horizontal center.
readonly flipY?: booleansourceMirrors the rendered node around its local vertical center.
Together,
frame,rotationand the flips place a local point (origin at the frame’s top-left corner) in the parent aswhere is
rotationin radians and has on the axis of each flip that is set. The flip happens first, then the rotation.readonly resizing?: NodeResizingsince 0.15.0sourceFill sizing and size limits. Omitted when empty.
sourcetype ContainerNode = FrameNode | GroupNodeNodes that have
children. Test withisContainerNode.
sourceconst isContainerNode: (node: SceneNode) => node is ContainerNodeTrue for frames and groups, the nodes that have
children.
sourceinterface FrameNode extends FramedNodeBaseA 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"sourcereadonly children: readonly NodeId[]sourceChild node IDs in paint order: the first child is at the back.
readonly layout: LayoutsourceHow children are positioned: freely, or by a stack or grid auto layout.
readonly style?: VisualStylesourcereadonly clipContent?: booleansourceHides the parts of children that fall outside the frame.
readonly guides?: readonly Guide[]since 0.11.0sourceRuler 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.
sourceinterface GroupNode extends FramedNodeBaseA 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 byapplyOperationsis removed too.readonly type: "group"sourcereadonly variableBindings?: GroupVariableBindingssourceProperties whose values come from theme variables. A group’s size is computed from its layers, so it cannot bind
widthorheight.readonly children: readonly NodeId[]sourceChild node IDs in paint order: the first child is at the back.
readonly clipContent?: booleansource
sourceinterface TextNode extends FramedNodeBaseA text box made of styled paragraphs.
readonly type: "text"sourcereadonly paragraphs: readonly TextParagraph[]sourcereadonly verticalAlign: "top" | "middle" | "bottom"sourceWhere the text sits in the frame when the frame is taller than the text.
readonly overflow: "clip" | "visible"sourceWhether text that does not fit the frame is cut off or still drawn.
readonly autoResize: "none" | "height" | "width-and-height"sourcenonekeeps both frame dimensions fixed,heightkeeps the width and lets the height follow the text, andwidth-and-heightlets both follow.readonly wrap: "none" | "word" | "character"sourceWhere long lines may break: never, between words, or between any characters.
sourcetype TextualNode = TextNodeNodes that hold editable rich text. Currently only
TextNode.
sourceconst isTextualNode: (node: SceneNode) => node is TextualNodeTrue for nodes that hold rich text (
TextNode).
sourceinterface CodeNode extends FramedNodeBaseA block of source code. Syntax highlighting is worked out when rendering; only the raw source is stored.
readonly type: "code"sourcereadonly source: stringsourcereadonly language: stringsourceThe language identifier used for highlighting. Must not be empty.
readonly fontFamily: stringsourceApplied uniformly to the full source rather than to individual text runs.
readonly fontSize: numbersourceApplied uniformly to the full source rather than to individual text runs. Positive.
readonly codeStyleId?: CodeStyleIdsourceThe theme code style; the theme’s
defaultCodeStyleIdwhen omitted.readonly codeStyleOverrides?: Partial< Pick< CodeStyle, "lineHeight" | "letterSpacing" | "background" | "foreground" | "syntax" > >sourceValues that replace the code style’s for this node only.
readonly lineNumbers: boolean | numbersourceFalse hides line numbers,
truestarts at one, and a number sets the first line.readonly autoResize: "none" | "height" | "width-and-height"since 0.1.3sourceSame meaning as
TextNode.autoResize.readonly wordWrap: booleansourceWraps long source lines to the frame width.
readonly stroke?: Strokesince 0.1.4sourceA stroke drawn around the code block.
readonly decorations: readonly CodeDecoration[]source
sourceinterface CodeDecorationA marked range of a
CodeNode’s source.readonly from: numbersourceStart offset into
source, in UTF-16 code units.readonly to: numbersourceEnd offset (exclusive), from
fromup to the source length.readonly kind: "emphasis" | "addition" | "deletion" | "annotation"sourcereadonly label?: stringsource
sourcetype ShapeNode = | RectangleNode | EllipseNode | PolygonNode | StarNode | LineNode | PathNodeEvery node with
type: "shape", told apart by itsshapefield.
sourceinterface RectangleNode extends FramedNodeBaseA rectangle filling its frame;
VisualStyle.cornerRadiusrounds it.readonly type: "shape"sourcereadonly shape: "rectangle"sourcereadonly style: VisualStylesource
sourceinterface EllipseNode extends FramedNodeBaseAn ellipse filling its frame, or an arc, pie or ring of one.
readonly type: "shape"sourcereadonly shape: "ellipse"sourcereadonly style: VisualStylesourcereadonly arc?: EllipseArcsince 0.12.0sourceOmitted draws the whole ellipse.
since 0.12.0sourceinterface EllipseArcTurns 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: numbersourcereadonly endingAngle: numbersourcereadonly innerRadius: numbersourceInner radius as a fraction of the outer radius, from 0 (a pie) to below 1 (a ring).
since 0.12.0sourceinterface PolygonNode extends FramedNodeBaseA regular polygon whose points are scaled to fill the frame, first point at the top center, as Figma draws it.
readonly type: "shape"sourcereadonly shape: "polygon"sourcereadonly pointCount: numbersourceWhole number of corners from 3 to 60.
readonly style: VisualStylesource
since 0.12.0sourceinterface StarNode extends FramedNodeBaseA star whose outer and inner points are scaled to fill the frame.
readonly type: "shape"sourcereadonly shape: "star"sourcereadonly pointCount: numbersourceWhole number of outer points from 3 to 60.
readonly innerRadius: numbersourceInner point distance as a fraction of the outer radius, from 0 to 1.
readonly style: VisualStylesource
since 0.12.0sourceinterface LineNode extends FramedNodeBaseA 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"sourcereadonly shape: "line"sourcereadonly style: VisualStylesourcereadonly startCap: StrokeCapsourcereadonly endCap: StrokeCapsource
sourceinterface PathNode extends NodeBaseAn editable vector shape. Unlike the other shapes it has no frame or rotation:
transformplaces the network in the parent, and its bounds are derived from the transformed network (seegetNodeBounds).readonly type: "shape"sourcereadonly shape: "path"sourcereadonly vectorNetwork: VectorNetworksince 0.2.0sourceThe path’s geometry.
readonly transform: TransformMatrixsourceMaps network coordinates into the parent’s coordinate system.
readonly style: VisualStylesourcereadonly startCap?: StrokeCapsince 0.22.0sourceThe 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
sourceinterface ImageNode extends FramedNodeBaseA placed image. The pixels live in an
ImageAsset.readonly type: "image"sourcereadonly assetId: AssetIdsourceMust name an asset of kind
image.readonly fit: MediaFitsourcereadonly crop?: RectsourceThe part of the source image to show, in source pixels.
readonly alt: stringsourceAlternative text for accessibility; may be empty.
sourceinterface VideoNode extends FramedNodeBaseA placed video. The file lives in a
VideoAsset.readonly type: "video"sourcereadonly assetId: AssetIdsourceMust name an asset of kind
video.readonly fit: MediaFitsourcereadonly crop?: RectsourceThe part of the video frame to show, in source pixels.
readonly alt: stringsourceAlternative text for accessibility; may be empty.
sourcetype MediaFit = "contain" | "cover" | "fill"How an image or video fills its frame, as in CSS
object-fit.
sourceinterface ConnectorNode extends FramedNodeBaseA stroked line or arrow between two points, each optionally attached to a node.
readonly type: "connector"sourcereadonly start: ConnectorEndpointsourcereadonly end: ConnectorEndpointsourcereadonly routing: "straight" | "elbow" | "curve"sourcereadonly stroke: Strokesourcereadonly startCap: StrokeCapsince 0.20.0sourceThe 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: StrokeCapsourcereadonly cornerRadius?: numbersince 0.21.0sourceHow 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.0sourceHow 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.cornerSmoothingand drawn the same way. A smoothed bend takes up to(1 + cornerSmoothing) * cornerRadiusof 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’sConnectorNodehas no smoothing; this extends it. Missing means 0.
sourceinterface ConnectorEndpointOne end of a
ConnectorNode.An end with
nodeIdattaches 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,BOTTOMorLEFTin the plugin API’sConnectorEndpointConnectedWithMagnet). 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
AUTOmagnet), the end goes where the layer’s outline faces the other end.xandyare 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?: NodeIdsourceThe node this end attaches to. It must be on the same page as the connector.
readonly x: numbersourcereadonly y: numbersourcereadonly side?: ConnectorSidesince 0.20.0sourceThe side of the attached layer this end is pinned to. Needs
nodeId, and cannot be set together withpointoranchor.readonly anchor?: ConnectorAnchorsince 0.20.0sourceThe key point on the attached shape’s outline this end is pinned to. Needs
nodeId, and cannot be set together withsideorpoint.readonly point?: { readonly x: number; readonly y: number }since 0.20.0sourceThe 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. NeedsnodeId, and cannot be set together withsideoranchor.
since 0.20.0sourcetype ConnectorSide = "top" | "right" | "bottom" | "left"A side of a layer that an attached connector end is pinned to.
since 0.20.0sourceinterface ConnectorAnchorA 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: cornerindexof the outline, counted aspolygonPointsandstarPointslist 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 vertexindexto 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"sourcereadonly index: numbersourceA whole number from 0.
sourcetype BlendMode = (typeof BLEND_MODES)[number]
since 0.8.0sourceconst BLEND_MODES: readonly [ "normal", "darken", "multiply", "color-burn", "lighten", "screen", "color-dodge", "overlay", "soft-light", "hard-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity" ]Separable and non-separable compositing modes, in CSS
mix-blend-modeterms.
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.
sourcetype Layout = AbsoluteLayout | AutoLayout
sourceinterface AbsoluteLayoutreadonly kind: "absolute"source
sourcetype AutoLayout = StackAutoLayout | GridAutoLayout
sourceinterface StackAutoLayout extends AutoLayoutConstraintsA row (
"horizontal") or column ("vertical") auto layout. The main axis runs along the stack; the cross axis runs across it.readonly kind: "vertical" | "horizontal"sourcereadonly gap: numbersourceSpace between children on the main axis.
readonly padding: LayoutPaddingsourcereadonly align: "start" | "center" | "end"sourceWhere 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"sourceHow children are spread on the main axis within each line. The
space-*values replacegapwith an automatic gap that is never negative:space-betweenputs all free space between children (a single child sits at the start),space-aroundputs half a gap before the first and after the last child, andspace-evenlyputs 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.0sourceMoves 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.0sourceSpace between wrapped lines. Omitted means the same as
gap. Kept, but unused, whilewrapis off.readonly alignContent?: "auto" | "space-between"since 0.15.0sourceHow wrapped lines share the cross axis.
"auto"(the default) packs the lines byalign, 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, whilewrapis off.
sourceinterface GridAutoLayout extends AutoLayoutConstraintsreadonly kind: "grid"sourcereadonly columns: numbersourcereadonly columnGap: numbersourcereadonly rowGap: numbersourcereadonly padding: LayoutPaddingsource
sourceinterface AutoLayoutConstraintsThe frame’s own sizing. Size limits moved to
FramedNodeBase.resizingin 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
sourceinterface LayoutPaddingreadonly top: numbersourcereadonly right: numbersourcereadonly bottom: numbersourcereadonly left: numbersource
sourcetype LayoutSizing = "fixed" | "hug"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.
since 0.15.0sourceinterface NodeResizingHow auto layout may size a node beyond its stored frame.
widthorheightset 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’sautoResize).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"sourcereadonly height?: "fill"sourcereadonly minWidth?: numbersourcereadonly maxWidth?: numbersourcereadonly minHeight?: numbersourcereadonly 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.
sourcetype LayoutAxis = "width" | "height"A size axis of a node: its width or its height.
sourcefunction nodeResizing(node: SceneNode): NodeResizing | undefinedThe node’s Fill flags and size limits, or
undefinedfor a path.
sourcefunction canFillParent(node: SceneNode): booleanWhether auto layout may give
nodea 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.
sourcefunction fillsAxis(node: SceneNode, axis: LayoutAxis): booleanWhether
nodefills its auto layout parent onaxis.
sourcefunction stackLineBreakSize(node: SceneNode, mainAxis: LayoutAxis): numberThe main-axis size a stack child takes when lines are broken: its box size, or its minimum size when it fills the main axis.
sourcefunction stackLineBreaks( layout: Pick<StackAutoLayout, "gap" | "justify" | "wrap">, availableMain: number, sizes: readonly number[] ): number[][]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.sizesare the children’s main sizes fromstackLineBreakSize. 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.
sourcefunction withAutoLayoutOperations( document: DesignDocument, operations: readonly DocumentOperation[] ): readonly DocumentOperation[]operationsfollowed 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.
sourcefunction layoutFrameTree( nodes: Record<string, SceneNode>, frameId: NodeId, changed: Set<NodeId> = new Set() ): Set<NodeId>Lays out the auto layout frame
frameIdinnodes(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.
sourcefunction applyFrameLayout( frame: FrameNode, childNodes: readonly SceneNode[], layout: Layout ): AppliedFrameLayoutLays out one frame’s direct children with
layoutand returns the frame with its new size and the moved or resized children. Child frames are not laid out again here;layoutFrameTreedoes that.
sourceinterface AppliedFrameLayoutA frame and its children after
applyFrameLayouthas placed them.readonly frame: FrameNodesourcereadonly children: ReadonlyMap<NodeId, SceneNode>source
sourcefunction createFrameLayout( kind: Layout["kind"], gap = DEFAULT_AUTO_LAYOUT_GAP ): LayoutA new layout of the given kind. Auto layouts start with a fixed width, a hugged height, no padding and
gapbetween children (a grid uses it for both its column and row gaps).
sourcefunction frameLayoutGap(layout: Layout): numberThe 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.
sourceconst DEFAULT_AUTO_LAYOUT_GAP: 24The 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.
since 0.25.0sourceinterface ResizeConstraintsA 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: HorizontalConstraintsourcereadonly vertical: VerticalConstraintsource
since 0.25.0sourcetype HorizontalConstraint = "left" | "right" | "left-right" | "center" | "scale"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.
since 0.25.0sourcetype VerticalConstraint = "top" | "bottom" | "top-bottom" | "center" | "scale"How a layer’s vertical position and height follow its frame’s height; the vertical counterpart of
HorizontalConstraint.
since 0.25.0sourceconst DEFAULT_RESIZE_CONSTRAINTS: ResizeConstraintsThe constraints a layer has when it stores none: Left and Top, Figma’s default, which keeps the layer’s position and size.
since 0.25.0sourcefunction nodeConstraints(node: SceneNode): ResizeConstraintsA layer’s constraints, with
DEFAULT_RESIZE_CONSTRAINTSwhen it stores none.
since 0.25.0sourcefunction followsConstraints(parent: FrameNode, child: SceneNode): booleanWhether a child of
parentis placed by its constraints whenparentresizes: 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.
since 0.25.0sourcefunction constrainChildren( nodes: NodeRecord, parentId: NodeId, from: FrameSize, to: FrameSize ): readonly SceneNode[]Places each child of the frame
parentIdfor a resize of that frame fromfromtoto, by the child’s constraints (NodeBase.constraints), as Figma does when a frame is resized.nodesholds 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, aslayoutFrameTreeor 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 withlayoutFrameTree.- 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 (
since 0.25.0sourceinterface FrameSizeA 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: numbersourcereadonly height: numbersource
since 0.25.0sourcefunction scaleLayerInParent( nodes: NodeRecord, node: SceneNode, scaleX: number, scaleY: number ): readonly SceneNode[]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
rotatedSizefor 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.
nodesmust hold the layer’s descendants.- 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
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.
sourcetype AxisSizing = "fixed" | "hug" | "fill"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.
sourcetype SizingAxis = "width" | "height"The axis a sizing applies to.
sourcefunction nodeAxisSizing( document: DesignDocument, node: SceneNode, axis: SizingAxis ): AxisSizingThe 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 ownresizingfor every kind of node.
sourcefunction canHugAxis(document: DesignDocument, node: SceneNode): booleanAuto 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.
sourcefunction canFillAxis( document: DesignDocument, node: SceneNode, axis: SizingAxis, parent: SceneNode | undefined = parentNodeOf(document, node.id) ): booleanWhether 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.
sourcefunction withoutFill(node: SceneNode, axis: SizingAxis): SceneNodeThe node without a Fill flag on the axis.
sourcefunction withoutAnyFill(node: SceneNode): SceneNodeThe node without any Fill flag, keeping its size limits.
sourcefunction withAxisSizing( document: DesignDocument, node: SceneNode, axis: SizingAxis, sizing: "fixed" | "hug" ): SceneNodeThe node with its sizing on
axisset to Fixed or Hug: the one writer of it, the counterpart ofnodeAxisSizing. 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: seewithoutFill.
sourcetype ManualSizeChange = "resize" | "scale" | "variable"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.
sourcefunction withManualSize( document: DesignDocument, before: SceneNode, after: SceneNode, change: ManualSizeChange, axes: readonly SizingAxis[] = changedSizeAxes(before, after) ): SceneNodeafter, the nodebeforewith 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.
sourcefunction withManualSizes( document: DesignDocument, nodes: readonly SceneNode[], change: ManualSizeChange, only?: ReadonlySet<NodeId> ): readonly SceneNode[]nodes, the output of a resize or a scale of layers ofdocument, each passed throughwithManualSizeagainst the node it replaces.onlylimits the rule to the layers the user changed, where the others were only carried along (the descendants of a resized group or selection).
sourcefunction withoutStretchWhereHugging( document: DesignDocument, node: SceneNode ): SceneNodeThe 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;
stretchWhereHuggingReplacementsapplies the rule to every edit, on the axes the edit brings to break it.
sourcefunction stretchWhereHuggingReplacements( previous: DesignDocument, next: DesignDocument, changedNodeIds: ReadonlySet<NodeId> ): readonly SceneNode[]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.
applyOperationscalls 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.nextispreviousafter the batch andchangedNodeIdsthe 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
nextand did not inprevious(the node is new, or it kept the rule on that axis there). A node that already hugged and stretched on an axis inprevious, 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.
sourcefunction sizingReplacements( document: DesignDocument, nodeIds: readonly NodeId[], axis: SizingAxis, sizing: AxisSizing ): ReadonlyMap<NodeId, SceneNode>The nodes that change when
nodeIdsare set tosizingonaxis, by id. Nodes that cannot take that sizing are left out.An instance follows every rule here as an auto layout frame does, through
nodeAxisSizingandwithAxisSizing, 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
applyOperationsto turn into Left or Top, as it does after every edit (stretchWhereHuggingReplacements).
sourcefunction resizingReplacements( document: DesignDocument, nodeId: NodeId, resizing: NodeResizing | undefined ): ReadonlyMap<NodeId, SceneNode>The nodes that change when a node’s Fill flags and size limits are replaced with
resizing(undefinedremoves them all). A newly set Fill follows the same rules as choosing Fill container in the sizing menu.
sourcefunction isAutoLayoutFrame(node: SceneNode | undefined): node is AutoLayoutFrameWhether the node is a frame with a stack or grid auto layout.
sourcefunction parentNodeOf( document: DesignDocument, nodeId: NodeId ): SceneNode | undefinedThe container that holds the node, or
undefinedfor a page’s top level node.
sourcefunction documentWithNodes( document: DesignDocument, nodes: ReadonlyMap<NodeId, SceneNode> ): DesignDocumentThe document with
nodesreplacing 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.
sourceinterface VisualStylereadonly styleId?: NodeStyleIdsourcereadonly fills?: readonly Paint[]sourcereadonly strokes?: readonly Stroke[]since 0.5.0sourceOrdered strokes painted after the fills, first entry at the bottom.
readonly cornerRadius?: numbersourceOne radius for every corner. Rectangles and frames use
cornerRadiiinstead when it is present; polygons, stars and paths round every vertex with it.readonly cornerRadii?: CornerRadiisince 0.16.0sourceIndependent corner radii for rectangles and frames. When present it replaces
cornerRadiuson those nodes; other shapes ignore it. The editor clearscornerRadiuswhen it writes this field, so one of the two describes the corners.readonly cornerSmoothing?: numbersince 0.9.0sourceCorner 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) * radiusof edge.For a rectangle, the renderer first limits the radius and smoothing to the budget , then works out how much of each edge the corner uses:
Walking into the corner, a cubic curve eases off the edge, a circular arc of radius turns through degrees, and a mirrored cubic eases onto the next edge. With degrees, the arc covers along each edge and the cubic control distances are
so . At this is a plain quarter circle.
readonly shadows?: readonly Shadow[]sourcereadonly layerBlur?: numbersince 0.7.0sourceGaussian blur radius applied to the node and its descendants as one layer.
readonly backgroundBlur?: numbersince 0.7.0sourceGaussian blur radius applied to whatever lies behind the node’s shape.
since 0.16.0sourceinterface CornerRadiiSeparate radii for the four corners of a rectangle or frame, as in Figma’s
topLeftRadius,topRightRadius,bottomRightRadiusandbottomLeftRadius. 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: numbersourcereadonly topRight: numbersourcereadonly bottomRight: numbersourcereadonly bottomLeft: numbersource
sourceconst CORNER_KEYS: readonly ["topLeft", "topRight", "bottomRight", "bottomLeft"]The four corners in Figma’s panel order: top left, top right, bottom right, bottom left.
sourcetype CornerKey = (typeof CORNER_KEYS)[number]One corner of a rectangle or frame, as named in
CornerRadii.
sourcefunction styleCornerRadii( style: | Pick<VisualStyle, "cornerRadius" | "cornerRadii"> | undefined ): CornerRadiiThe radii a rectangle or frame paints with:
cornerRadiiwhen present, otherwise the uniformcornerRadius(zero when absent) on every corner.
sourcefunction uniformCornerRadii(radius: number): CornerRadiiFour equal radii.
sourcefunction cornerRadiiAreUniform(radii: CornerRadii): booleanTrue when all four corners have the same radius.
sourcefunction cornerRadiiAreRounded(radii: CornerRadii): booleanTrue when any corner is rounded.
sourcefunction scaleCornerRadii(radii: CornerRadii, factor: number): CornerRadiiMultiplies every radius by
factor, as the scale tool does.
sourcetype Paint = SolidPaint | GradientPaint | ImagePaint
since 0.18.0sourceinterface PaintVisibilityFields 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.0sourceFalse 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.0sourceHow 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".
sourceinterface SolidPaint extends PaintVisibilityreadonly kind: "solid"sourcereadonly color: stringsourcereadonly opacity?: numbersource
sourcetype GradientPaint = | LinearGradientPaint | RadialGradientPaint | AngularGradientPaint
sourceinterface LinearGradientPaint extends PaintVisibilityreadonly kind: "linear-gradient"sourcereadonly angle: numbersourcereadonly stops: readonly GradientStop[]sourcereadonly opacity?: numbersource
since 0.6.0sourceinterface RadialGradientPaint extends PaintVisibilityA gradient that spreads outward from a center point.
readonly kind: "radial-gradient"sourcereadonly center?: PaintAnchorsourceDefaults to the center of the bounds.
readonly radius?: PaintAnchorsourceEllipse radii as fractions of the bounds; defaults to half the width and height.
readonly stops: readonly GradientStop[]sourcereadonly opacity?: numbersource
since 0.6.0sourceinterface AngularGradientPaint extends PaintVisibilityA gradient that sweeps around a center point.
readonly kind: "angular-gradient"sourcereadonly center?: PaintAnchorsourceDefaults to the center of the bounds.
readonly angle?: numbersourceStart angle in degrees, clockwise from the positive x axis; defaults to 0.
readonly stops: readonly GradientStop[]sourcereadonly opacity?: numbersource
sourceinterface GradientStopreadonly offset: numbersourcereadonly color: stringsource
since 0.6.0sourceinterface PaintAnchorA point in the painted shape’s bounds, as fractions of its width and height.
readonly x: numbersourcereadonly y: numbersource
since 0.6.0sourceinterface ImagePaint extends PaintVisibilityPaints an image asset.
readonly kind: "image"sourcereadonly assetId: AssetIdsourcereadonly fit?: ImagePaintFitsourceDefaults to cover.
readonly opacity?: numbersource
since 0.6.0sourcetype ImagePaintFit = "contain" | "cover" | "fill"How an image paint is scaled into the painted shape’s bounds.
sourceinterface Strokereadonly paint: Paintsourcereadonly width: numbersourceThe weight of every side. With
sides, the editor keeps it at the largest side weight.readonly dash?: readonly number[]sourcereadonly position?: StrokePositionsince 0.0.1sourceDefaults to center when omitted by an older operation or document.
readonly sides?: StrokeSidessince 0.17.0sourceSeparate weights for the top, right, bottom and left sides, replacing
widthon 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.0sourceHow 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. SeedefaultStrokeJoin. Rectangles and frames with side weights ignore it.readonly miterAngle?: numbersince 0.22.0sourceThe 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 isDEFAULT_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.0sourceThe 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.
since 0.0.1sourcetype StrokePosition = "center" | "inside" | "outside"Which side of the outline a stroke is drawn on.
since 0.17.0sourceinterface StrokeSidesStroke 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.strokescarries its own.readonly top: numbersourcereadonly right: numbersourcereadonly bottom: numbersourcereadonly left: numbersource
sourceconst STROKE_SIDE_KEYS: readonly ["top", "right", "bottom", "left"]The four stroke sides in the order the wire and the panel use.
sourcetype StrokeSideKey = (typeof STROKE_SIDE_KEYS)[number]One side of a stroke, as named in
StrokeSides.
sourcefunction strokeSideWeights(stroke: Pick<Stroke, "width" | "sides">): StrokeSidesThe weight of each side a stroke paints with:
sides, orwidthon all four.
sourcefunction widestStrokeSide(sides: StrokeSides): numberThe largest of the four side weights.
sourcetype StrokeCap = (typeof STROKE_CAPS)[number]
since 0.12.0sourceconst STROKE_CAPS: readonly [ "none", "round", "square", "line-arrow", "triangle-arrow", "reversed-triangle", "circle-arrow", "diamond-arrow" ]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).
sourcetype StrokeJoin = (typeof STROKE_JOINS)[number]How a stroke’s corners join: sharp (miter), cut off (bevel) or round.
since 0.22.0sourceconst STROKE_JOINS: readonly ["miter", "bevel", "round"]The stroke joins, as Figma names them (
MITER,BEVEL,ROUND).
sourcetype DashCap = (typeof DASH_CAPS)[number]How each dash of a dashed stroke ends: flat at the dash (none), round or square.
since 0.22.0sourceconst DASH_CAPS: readonly ["none", "round", "square"]The ends of each dash of a dashed stroke, as in Figma’s Dash cap menu.
since 0.22.0sourceconst DEFAULT_STROKE_MITER_ANGLE: numberFigma’s default miter angle in degrees, 2 * asin(1 / 4): the angle at which a miter limit of 4 bevels.
sourcefunction strokeMiterAngle(stroke: Pick<Stroke, "miterAngle">): numberA stroke’s miter angle, or Figma’s default when it has none.
since 0.22.0sourcefunction miterLimitFromAngle(angle: number): numberThe 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
angledegrees has a longer miter.
sourcefunction miterAngleFromLimit(limit: number): numberThe miter angle, in degrees, that a miter limit of at least 1 gives.
since 0.22.0sourcefunction defaultStrokeJoin( node: SceneNode, style: VisualStyle | undefined ): StrokeJoinThe join a stroke of
nodedraws 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.styleis the layer’s resolved style.
since 0.22.0sourcefunction defaultDashCap( node: SceneNode, style: VisualStyle | undefined ): DashCapThe dash ends a dashed stroke of
nodedraws 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.
sourceconst DEFAULT_PATH_STROKE_CAP: StrokeCapThe end style an open path draws when it names none: round, as before 0.22.0.
since 0.22.0sourcefunction openPathChain(network: VectorNetwork): readonly VectorSegment[] | nullThe segments of
networkwhen 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.startCapandendCap). Returns null for a closed, branched or empty network.
sourceinterface Shadowreadonly kind?: ShadowKindsince 0.7.0sourceDefaults to a drop shadow when omitted by an older document.
readonly x: numbersourcereadonly y: numbersourcereadonly blur: numbersourcereadonly spread: numbersourcereadonly color: stringsource
since 0.7.0sourcetype ShadowKind = "drop" | "inner"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.
since 0.2.0sourceinterface VectorNetworkThe vertices, segments and filled regions that make up a path’s shape.
readonly vertices: readonly VectorVertex[]sourcereadonly segments: readonly VectorSegment[]sourcereadonly constraints?: readonly VectorTangentConstraint[]sourceOmitted is equivalent to an empty constraint list.
readonly regions: readonly VectorRegion[]sourceOrdered from back to front for painting.
since 0.2.0sourceinterface VectorVertexA point in a
VectorNetwork, in network coordinates.readonly id: VectorVertexIdsourcereadonly x: numbersourcereadonly y: numbersourcereadonly cornerRadius?: numbersince 0.10.0sourceCorner 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.
since 0.2.0sourcetype VectorSegment = | VectorLineSegment | VectorCubicSegment | VectorEllipticalArcSegmentAn edge between two vertices of a
VectorNetwork.
sourceinterface VectorLineSegment extends VectorSegmentBasereadonly kind: "line"source
sourceinterface VectorCubicSegment extends VectorSegmentBasereadonly kind: "cubic"sourcereadonly startHandle: VectorHandlesourcereadonly endHandle: VectorHandlesource
sourceinterface VectorEllipticalArcSegment extends VectorSegmentBaseCompatibility geometry for exact migration and SVG interchange. The editor initially creates and directly edits line and cubic segments only.
readonly kind: "elliptical-arc"sourcereadonly radiusX: numbersourcereadonly radiusY: numbersourcereadonly xAxisRotation: numbersourcereadonly largeArc: booleansourcereadonly sweep: booleansource
sourceinterface VectorHandlereadonly id: VectorHandleIdsourcereadonly dx: numbersourceHorizontal control-point offset relative to the segment endpoint.
readonly dy: numbersourceVertical control-point offset relative to the segment endpoint.
sourceinterface VectorTangentConstraintCouples 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: VectorVertexIdsourcereadonly handleIds: readonly [VectorHandleId, VectorHandleId]sourcereadonly mode: "angle" | "angle-and-length"sourceKeep only the angle mirrored, or mirror both angle and handle length.
since 0.2.0sourceinterface VectorRegionA filled area of a
VectorNetwork, bounded by loops of segments.readonly id: VectorRegionIdsourcereadonly windingRule: "nonzero" | "evenodd"sourcereadonly loops: readonly VectorRegionLoop[]sourcereadonly fills?: readonly Paint[]sourceOmitted inherits the path’s resolved fills; an empty array disables fill.
sourceinterface VectorRegionLoopreadonly segments: readonly VectorSegmentReference[]sourcereadonly implicitClose?: truesourceCloses an open chain for filling without adding or stroking another edge, matching SVG’s implicit fill closure for open subpaths.
sourceinterface VectorSegmentReferencereadonly segmentId: VectorSegmentIdsourcereadonly direction: "forward" | "reverse"source
sourcetype VectorWindingRule = VectorRegion["windingRule"]
sourcefunction vectorNetworkFromSvgPathData( pathData: string, windingRule: VectorWindingRule = "nonzero" ): VectorNetworkConverts 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.
sourcefunction vectorRegionToSvgPathData( network: VectorNetwork, region: VectorRegion ): stringProjects one fillable region to SVG path data.
sourcefunction vectorRegionPathDataById( network: VectorNetwork, regionId: string ): string | undefinedProjects the first matching region, useful for callers that retain a region ID.
sourcefunction vectorNetworkToStrokePathData(network: VectorNetwork): stringProjects 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.
sourcefunction transformVectorPoint( x: number, y: number, transform: TransformMatrix ): { readonly x: number; readonly y: number }Applies an SVG-compatible affine matrix to a point.
sourcefunction scaleVectorNetwork( network: VectorNetwork, scale: { readonly x: number; readonly y: number }, anchor: { readonly x: number; readonly y: number } ): VectorNetworkScales a vector network in its own coordinate space around one anchor. An elliptical arc keeps its exact shape: see
scaleEllipticalArc.
sourcefunction assertValidPathData(pathData: string): void
sourcefunction isClosedPathData(pathData: string): booleanReturns
truewhen 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.
sourceinterface TextParagraphOne paragraph of a
TextNode: its runs plus paragraph-level layout.readonly runs: readonly TextRun[]sourcereadonly direction?: "auto" | "ltr" | "rtl"sourceAuto resolves direction from paragraph content when omitted or set explicitly.
readonly align?: "left" | "center" | "right" | "justify"sourcereadonly spacingBefore?: numbersourcereadonly spacingAfter?: numbersourcereadonly indent?: { readonly left: number; readonly right: number; readonly firstLine: number; }sourcereadonly list?: { readonly kind: "ordered" | "unordered"; readonly level: number; readonly start?: number; readonly marker?: TextListMarker; }sourceMakes the paragraph a list item.
sourceinterface TextRunA span of text sharing one style.
readonly text: stringsourcereadonly styleId?: TextStyleIdsourceThe theme text style; the theme’s
defaultTextStyleIdwhen omitted.readonly overrides?: TextStyleOverridessourceDirect formatting on top of the style.
sourceinterface TextListMarkerThe bullet or number shown before a list paragraph, and how it is styled.
readonly text?: stringsourceCustom marker text; must not be empty when given.
readonly styleId?: TextStyleIdsourcereadonly overrides?: TextStyleOverridessourcereadonly gap?: numbersourceSpace between the marker and the paragraph text. Non-negative.
sourceinterface TextStylereadonly name: stringsourcereadonly fontFamily: stringsourcereadonly fontAssetId?: AssetIdsourcereadonly fontFallbackAssetIds?: readonly AssetId[]sourceOrdered embedded font assets tried after
fontAssetId.readonly language?: stringsourceCanonically-cased BCP 47 language tag used for shaping and line breaking.
readonly fontSize: numbersourcereadonly fontWeight: numbersourcereadonly lineHeight: numbersourcereadonly letterSpacing?: numbersourcereadonly color: stringsourcereadonly italic?: booleansourcereadonly underline?: booleansourcereadonly strikethrough?: booleansince 0.2.0sourceDraws a line through the text.
readonly openTypeFeatures?: Readonly<Record<string, boolean | number>>since 0.2.0sourceExplicit deviations from a font’s OpenType feature defaults.
readonly fontVariations?: Readonly<Record<string, number>>since 0.2.0sourceExplicit variable-font axis coordinates keyed by four-byte OpenType tag.
since 0.2.1sourcetype TextStyleOverrides = & Partial<Omit<TextStyle, "name" | "fontAssetId" | "fontFallbackAssetIds">> & { readonly fontAssetId?: AssetId | null; readonly fontFallbackAssetIds?: readonly AssetId[]; }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
nullprimary clears an inheritedfontAssetId, while an empty fallback list clears inherited fallbacks.
sourcefunction assertTextStyleOverrides( value: unknown ): asserts value is TextStyleOverridesValidates a standalone authored run or list-marker text-style override.
sourceconst MAX_TEXT_FONT_FALLBACK_ASSETS: 4095Canonical upper bound; retained text also carries one primary font asset.
since 0.2.0sourcetype OpenTypeTag = Brand<string, "OpenTypeTag">A four-character OpenType feature or axis tag. Make one with
openTypeTag.
sourcefunction openTypeTag(value: string): OpenTypeTagOpenType 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.
sourcetype Asset = ImageAsset | VideoAsset | FontAssetA file stored in
DesignDocument.assets, told apart by itskindfield.
sourceinterface ImageAssetAn image file used by
ImageNodeandImagePaint.readonly id: AssetIdsourceMatches the asset’s key in
DesignDocument.assets.readonly kind: "image"sourcereadonly mimeType: stringsourcereadonly source: AssetSourcesourcereadonly width?: numbersourceIntrinsic width in pixels, when known.
readonly height?: numbersourceIntrinsic height in pixels, when known.
sourceinterface VideoAssetA video file used by
VideoNode.readonly id: AssetIdsourceMatches the asset’s key in
DesignDocument.assets.readonly kind: "video"sourcereadonly mimeType: stringsourcereadonly source: AssetSourcesourcereadonly width?: numbersourceIntrinsic width in pixels, when known.
readonly height?: numbersourceIntrinsic height in pixels, when known.
sourceinterface FontAssetOne font face, referenced by
TextStyle.fontAssetId,TextStyle.fontFallbackAssetIdsandCodeStyle.fontAssetId.readonly id: AssetIdsourceMatches the asset’s key in
DesignDocument.assets.readonly kind: "font"sourcereadonly mimeType: stringsourceMust be
font/woff2when the source is Google Fonts, andfont/ttforfont/otfwhen the source is a local font.readonly source: FontAssetSourcesourcereadonly family: stringsourcereadonly weight: numbersourceFrom 1 to 1000, as in CSS
font-weight.readonly style: "normal" | "italic" | "oblique"source
sourcetype AssetSource = | { readonly kind: "url"; readonly url: string } | { readonly kind: "data"; readonly data: string }Where an asset’s bytes come from: a URL, or data stored in the document.
since 0.13.0sourcetype FontAssetSource = AssetSource | GoogleFontsSource | LocalFontSourceWhere a font’s files come from.
since 0.13.0sourceinterface GoogleFontsSourceA font family served by Google Fonts as many small WOFF2 files, each covering part of Unicode (declared by
unicode-rangein 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"sourcereadonly family: stringsourceThe Google Fonts family name, such as “Noto Sans SC”.
since 0.19.0sourceinterface LocalFontSourceA 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"sourcereadonly family: stringsourceThe installed family name, such as “Helvetica Neue”.
readonly postScriptName: stringsourceThe face’s PostScript name, which identifies it among installed fonts.
readonly styleName: stringsourceThe installed style name, such as “Bold Italic”.
readonly sha256: stringsourceSHA-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.
sourceinterface DocumentThemeThe document’s shared styles, named colors and variables. Nodes refer to them by ID and may keep direct overrides on top.
readonly defaultTextStyleId: TextStyleIdsourceThe text style used by runs that name none.
readonly defaultCodeStyleId: CodeStyleIdsourceThe code style used by code nodes that name none.
readonly colors: Readonly<Record<string, string>>sourceNamed colors, keyed by name.
readonly nodeStyles: Readonly<Record<string, NodeStyle>>sourcereadonly textStyles: Readonly<Record<string, TextStyle>>sourcereadonly codeStyles: Readonly<Record<string, CodeStyle>>sourcereadonly variableCollections?: Readonly<Record<string, VariableCollection>>sourceVariable collections stored in this document. Libraries shared between documents are intentionally out of scope.
sourceinterface NodeStyle extends Omit<VisualStyle, "styleId">readonly name: stringsource
sourceinterface CodeStyleProvides palette colors and the typography defaults used for new code nodes.
readonly name: stringsourcereadonly fontFamily: stringsourcereadonly fontAssetId?: AssetIdsourcereadonly fontSize: numbersourcereadonly lineHeight: numbersourcereadonly letterSpacing?: numbersince 0.1.2sourceExtra space added between characters.
readonly background: stringsourcereadonly foreground: stringsourcereadonly syntax: Readonly<Record<string, string>>source
sourceinterface VariableCollectionreadonly name: stringsourcereadonly defaultModeId: VariableModeIdsourcereadonly activeModeId?: VariableModeIdsourceThe mode this document shows where no page or layer chooses one (see
VariableModes). It defaults todefaultModeIdwhen omitted.readonly modeOrder: readonly VariableModeId[]sourcereadonly modes: Readonly<Record<string, VariableMode>>sourcereadonly variables: Readonly<Record<string, VariableDefinition>>source
sourceinterface VariableModereadonly name: stringsource
sourcetype VariableDefinition = | VariableDefinitionBase<"color", string> | VariableDefinitionBase<"number", number> | VariableDefinitionBase<"string", string> | VariableDefinitionBase<"boolean", boolean>
sourcetype VariableType = "color" | "number" | "string" | "boolean"
sourcetype VariableValue = string | number | boolean
sourcetype VariableProperty = | "visibility" | "opacity" | "width" | "height" | "cornerRadius" | "fillColor" | "strokeColor" | "strokeWidth" | "gap" | "wrapGap" | "columnGap" | "rowGap" | "paddingTop" | "paddingRight" | "paddingBottom" | "paddingLeft" | "fontFamily" | "fontSize" | "fontWeight" | "lineHeight" | "letterSpacing" | "textColor"
sourcetype VariableBindings = Readonly<Partial<Record<VariableProperty, VariableId>>>
sourceconst VARIABLE_PROPERTY_TYPES: Readonly<Record<VariableProperty, VariableType>>
sourceinterface LocatedVariablereadonly collectionId: stringsourcereadonly collection: VariableCollectionsourcereadonly variableId: VariableIdsourcereadonly variable: VariableDefinitionsource
sourcefunction findVariable( theme: DocumentTheme, variableId: VariableId | string ): LocatedVariable | undefined
sourcefunction resolveVariableValue( theme: DocumentTheme, variableId: VariableId | string, modes?: VariableModes ): VariableValue | undefinedThe variable’s value in the mode its collection shows under
modes(the modes in effect at a layer, seevariableModesAt): the inherited choice, else the collection’s active mode, else its default mode. A mode without a value reads the default mode’s value.
sourcefunction listVariablesForProperty( theme: DocumentTheme, property: VariableProperty ): readonly LocatedVariable[]
sourcefunction variableSupportsProperty( variable: VariableDefinition, property: VariableProperty ): boolean
sourcefunction nodeSupportsVariableProperty( node: SceneNode, property: VariableProperty ): boolean
### 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.
since 0.26.0sourcetype VariableModes = Readonly<Record<string, VariableModeId>>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 itsdefaultModeId). Omitted when empty.
since 0.26.0sourcefunction inheritVariableModes( parent: VariableModes | undefined, own: VariableModes | undefined ): VariableModes | undefinedThe 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
parentitself whenownchooses nothing.
since 0.26.0sourcefunction variableModeFor( collectionId: string, collection: VariableCollection, modes?: VariableModes ): VariableModeIdThe mode a collection shows under
modes: the inherited choice, else the collection’sactiveModeId, else itsdefaultModeId.
since 0.26.0sourcefunction variableModesAt( document: DesignDocument, nodeId: NodeId ): VariableModes | undefinedThe 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.
since 0.26.0sourcefunction inheritedVariableModes( document: DesignDocument, nodeId: NodeId ): VariableModes | undefinedThe modes a stored node inherits:
variableModesAtits parent, or the page’s modes for a page root. The node’s own choices are not included.
since 0.26.0sourcefunction locatedVariableValue( located: LocatedVariable, modes?: VariableModes ): VariableValue | undefinedresolveVariableValuefor a variable already found withfindVariable.
since 0.26.0sourcefunction applyVariableBindings( document: DesignDocument, node: SceneNode, modes: VariableModes | undefined ): SceneNodeWrites each of the node’s variable bindings in the modes
modesinto the bound field (seewithVariableValue). A binding to a missing variable, or one the node cannot take, is skipped. A text layer that sizes itself is measured again. Returnsnodeitself when it has no bindings.
since 0.26.0sourcefunction withVariableValue( theme: DocumentTheme, node: SceneNode, property: VariableProperty, value: VariableValue ): SceneNodeWrites 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.
since 0.26.0sourcefunction bindingsDependOnModes( theme: DocumentTheme, node: SceneNode, modes: VariableModes | undefined ): booleanWhether 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 needsapplyVariableBindings.
since 0.26.0sourcefunction variableModesKey( theme: DocumentTheme, modes: VariableModes | undefined ): stringA stable text form of
modesfor 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.
sourcetype DocumentId = Brand<string, "DocumentId">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.
sourceconst documentId: (value: string) => DocumentIdBrands a string as a
DocumentId. Throws aTypeErrorfor an empty string.
sourcetype PageId = Brand<string, "PageId">The ID of a
Page.
sourceconst pageId: (value: string) => PageIdBrands a string as a
PageId. Throws aTypeErrorfor an empty string.
sourcetype NodeId = Brand<string, "NodeId">The ID of a
SceneNode.
sourceconst nodeId: (value: string) => NodeIdBrands a string as a
NodeId. Throws aTypeErrorfor an empty string.
sourcetype AssetId = Brand<string, "AssetId">The ID of an
Asset.
sourceconst assetId: (value: string) => AssetIdBrands a string as an
AssetId. Throws aTypeErrorfor an empty string.
sourcetype NodeStyleId = Brand<string, "NodeStyleId">The ID of a
NodeStyleinDocumentTheme.nodeStyles.
sourceconst nodeStyleId: (value: string) => NodeStyleIdBrands a string as a
NodeStyleId. Throws aTypeErrorfor an empty string.
sourcetype TextStyleId = Brand<string, "TextStyleId">The ID of a
TextStyleinDocumentTheme.textStyles.
sourceconst textStyleId: (value: string) => TextStyleIdBrands a string as a
TextStyleId. Throws aTypeErrorfor an empty string.
sourcetype CodeStyleId = Brand<string, "CodeStyleId">The ID of a
CodeStyleinDocumentTheme.codeStyles.
sourceconst codeStyleId: (value: string) => CodeStyleIdBrands a string as a
CodeStyleId. Throws aTypeErrorfor an empty string.
sourcetype VariableCollectionId = Brand<string, "VariableCollectionId">The ID of a
VariableCollection.
sourceconst variableCollectionId: (value: string) => VariableCollectionIdBrands a string as a
VariableCollectionId. Throws aTypeErrorfor an empty string.
sourcetype VariableModeId = Brand<string, "VariableModeId">The ID of a
VariableModewithin its collection.
sourceconst variableModeId: (value: string) => VariableModeIdBrands a string as a
VariableModeId. Throws aTypeErrorfor an empty string.
sourcetype VariableId = Brand<string, "VariableId">The ID of a
VariableDefinition.
sourceconst variableId: (value: string) => VariableIdBrands a string as a
VariableId. Throws aTypeErrorfor an empty string.
sourcetype CommitId = Brand<string, "CommitId">The ID of a
CommitRecordin aDocumentStore.
sourceconst commitId: (value: string) => CommitIdBrands a string as a
CommitId. Throws aTypeErrorfor an empty string.
since 0.2.0sourcetype VectorVertexId = Brand<string, "VectorVertexId">The ID of a
VectorVertex.
sourceconst vectorVertexId: (value: string) => VectorVertexIdBrands a string as a
VectorVertexId. Throws aTypeErrorfor an empty string.
since 0.2.0sourcetype VectorSegmentId = Brand<string, "VectorSegmentId">The ID of a
VectorSegment.
sourceconst vectorSegmentId: (value: string) => VectorSegmentIdBrands a string as a
VectorSegmentId. Throws aTypeErrorfor an empty string.
since 0.2.0sourcetype VectorHandleId = Brand<string, "VectorHandleId">The ID of a
VectorHandle.
sourceconst vectorHandleId: (value: string) => VectorHandleIdBrands a string as a
VectorHandleId. Throws aTypeErrorfor an empty string.
since 0.2.0sourcetype VectorRegionId = Brand<string, "VectorRegionId">The ID of a
VectorRegion.
sourceconst vectorRegionId: (value: string) => VectorRegionIdBrands a string as a
VectorRegionId. Throws aTypeErrorfor 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.
sourcefunction deserializeDocument( input: string | unknown, migrations: MigrationRegistry = defaultMigrations ): DesignDocumentLoads a document from a JSON string or an already parsed value.
Runs
migrationsup toCURRENT_DOCUMENT_SCHEMA_VERSION, validates the result withassertDocument, 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_JSONfor a string that does not parse, a migration error code (seeMigrationRegistry.migrate), or a validation error.
sourcefunction serializeDocument(document: DesignDocument, indentation = 0): stringValidates
documentwithassertDocumentand writes it as JSON with sorted keys, so the same document always gives the same text.indentationis passed toJSON.stringify; 0 writes one line.
sourcefunction snapshotDocument(input: DesignDocument | unknown): DesignDocumentReturns a validated, deeply frozen copy of a document. The same as
deserializeDocumentwith the default migrations.
sourcefunction assertDocument(value: unknown): asserts value is DesignDocumentChecks that
valueis a validDesignDocumentatCURRENT_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; usedeserializeDocumentfor older data.Throws a
ModelErrornaming the first problem found.
sourcefunction assertSceneNode(value: unknown): asserts value is SceneNodeChecks the shape of one node on its own. References to other nodes, styles and assets are not checked. Throws a
ModelErroron failure.
sourcefunction assertDocumentTheme(value: unknown): asserts value is DocumentThemeChecks the shape of a theme on its own. Throws a
ModelErroron failure.
sourcefunction assertAsset(value: unknown): asserts value is AssetChecks the shape of one asset on its own. Throws a
ModelErroron failure.
sourceclass ModelError extends ErrorThe 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
sourcetype ModelErrorCode = | "INVALID_JSON" | "INVALID_SCHEMA" | "INVALID_TREE" | "UNSUPPORTED_VERSION" | "MISSING_MIGRATION" | "MIGRATION_FAILED" | "NOT_IMPLEMENTED"What kind of problem a
ModelErrorreports:INVALID_JSON: the input string is not JSON, or a value is not plain JSON (it holdsundefined, 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.
sourceclass MigrationRegistryAn ordered chain of
DocumentMigrationsteps.deserializeDocumentanddeserializeStoreusedefaultMigrationsunless given another registry.new (migrations: readonly DocumentMigration[] = [])sourceregister(migration: DocumentMigration): thissourceAdds a step and returns the registry. Throws a
ModelErrorwith codeINVALID_SCHEMAif the step does not move forward asDocumentMigration.todescribes, or if a step from the same version is already registered.migrateOperation( operation: unknown, from: DocumentSchemaVersion, target: DocumentSchemaVersion = CURRENT_DOCUMENT_SCHEMA_VERSION ): unknownsourceCarries one stored operation from the version it was recorded under to the current one, using the same ordered steps as documents. Steps without
migrateOperationpass the operation through. Returnsnullwhen a step returnsnull.migrate( input: unknown, target: DocumentSchemaVersion = CURRENT_DOCUMENT_SCHEMA_VERSION ): unknownsourceRuns the steps from the input’s
schemaVersionup totargetand returns the result as plain JSON. It does not validate the result.Throws a
ModelError:UNSUPPORTED_VERSIONwhen the input is newer thantarget,MISSING_MIGRATIONwhen no step starts at a version on the way, andMIGRATION_FAILEDwhen a step throws or writes the wrong version.
sourceinterface DocumentMigrationOne step that upgrades a document from one schema version to the next. Register steps in a
MigrationRegistry.readonly from: DocumentSchemaVersionsourceThe version this step reads. Only one step may start at a version.
readonly to: DocumentSchemaVersionsourceThe version this step writes:
from + 1for a legacy number,"0.0.0"from legacy version 1, or a later semantic version.migrate(input: unknown): unknownsourceReturns the upgraded document as plain JSON, with
schemaVersionset toto. The input is a private copy, so changing it in place is fine.migrateOperation?(operation: unknown): unknownsourceCarries 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
migraterewrites 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.
sourceconst defaultMigrations: MigrationRegistryThe migrations this package ships: one step per schema version, from legacy version 1 through
0.0.0and every later version up toCURRENT_DOCUMENT_SCHEMA_VERSION. Data saved at a version with no step fails withMISSING_MIGRATION.
sourcefunction readDocumentSchemaVersion( value: unknown, path: string ): DocumentSchemaVersionChecks that
valueis a document schema version: a non-negative whole number (legacy) or amajor.minor.patchstring. Returns it unchanged. Throws aModelErrorwith codeINVALID_SCHEMAatpathotherwise.
sourcefunction compareDocumentSchemaVersions( left: DocumentSchemaVersion, right: DocumentSchemaVersion ): numberOrders 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
TypeErrorfor a malformed version string.
These helpers work on any plain JSON value.
sourcetype JsonValue = | JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue }Any JSON value. Used for the free-form
metadatafields.
sourcetype JsonPrimitive = string | number | boolean | nullA JSON scalar: string, finite number, boolean, or
null.
sourcefunction canonicalJson(value: unknown, indentation = 0): stringSerializes a JSON value with object keys sorted, so equal values give equal text. Throws a
ModelErrorifvalueis not plain JSON (for exampleundefined,NaN, or a class instance).
sourcefunction cloneJson<Value>(value: Value): ValueDeep-copies a plain JSON value. Throws a
ModelErrorif it is not plain JSON.
sourcefunction deepFreeze<Value>(value: Value): Readonly<Value>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.
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.
sourcefunction applyOperations( input: DesignDocument, operations: readonly DocumentOperation[] ): ApplyOperationsResultApplies
operationsin order and returns the new document, the inverse operations, and aChangeSet. 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
xandyit 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 withsnapshotDocument.All or nothing: if any operation does not fit the document, or the result is invalid, it throws a
ModelErrorand returns nothing.
sourceinterface ApplyOperationsResultWhat
applyOperationsreturns.readonly document: DesignDocumentsourceThe new, deeply frozen document; the input document when nothing changed.
readonly inverseOperations: readonly DocumentOperation[]sourceOperations that turn
documentback 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
applyOperationskept 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
sourcetype 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; }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;nullmetadata removes it.node.insert: addssubtreeas a child ofparentIdatindex(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 tonewParentIdatindex, 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’stypeor a container’schildren; use insert, remove and move.page.insert,page.remove,page.move: add, delete or reorder pages. An inserted page’srootIdmust match its subtree’s root. Removing a page removes its nodes.page.replace: replaces a page’s fields; it cannot changerootId.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.
sourceinterface SerializedSubtreeA node and all its descendants, as carried by insert operations.
readonly rootId: NodeIdsourcereadonly nodes: Readonly<Record<string, SceneNode>>sourceThe root and every descendant, keyed by
NodeId.
sourcefunction serializeSubtree( document: DesignDocument, rootId: NodeId ): SerializedSubtreeCopies a node and its descendants into a
SerializedSubtree, ready for anode.insertorpage.insertoperation. Throws aModelErrorwhen the node does not exist.
sourcefunction getSubtreeNodeIds( document: DesignDocument, rootId: NodeId ): ReadonlySet<NodeId>The IDs of a node and all its descendants. Empty when the node does not exist.
sourcefunction nodeIdsRemovedBy( input: DesignDocument, operations: readonly DocumentOperation[] ): readonly NodeId[]The IDs of the nodes that are in
inputand gone onceoperationshave run, in the orderinput.nodeslists them. That is every node the operations remove themselves (node.remove,page.removeandcomponent.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
applyOperationsbut 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 whichapplyOperationswould refuse the edit.It throws a
ModelErrorwhen an operation does not fit the document.
sourceinterface ChangeSetWhat 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: booleansourceFalse only when the document is unchanged; then every list is empty.
readonly themeChanged: booleansourcereadonly changedNodeIds: readonly NodeId[]sourceNodes 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[]sourceNodes 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[]sourceNodes that no longer exist.
readonly changedPageIds: readonly PageId[]sourcePages added, removed, changed or moved in
pageOrder.readonly changedAssetIds: readonly AssetId[]sourcereadonly changedComponentIds: readonly ComponentId[]since 0.23.0sourceComponents 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).
sourceconst EMPTY_CHANGE_SET: ChangeSetThe frozen
ChangeSetreturned whenever nothing changed.
sourcefunction diffDocuments( previous: DesignDocument, current: DesignDocument ): ChangeSetWorks out the
ChangeSetbetween two valid documents, for changes that did not come fromapplyOperations(such as undo or checkout inDocumentStore). Parts shared by identity are skipped without reading.
sourcefunction serializeOperations( operations: readonly DocumentOperation[], indentation = 0 ): stringChecks each operation with
assertOperationand writes the list as JSON with sorted keys.indentationis passed toJSON.stringify.
sourcefunction deserializeOperations( input: string | unknown ): readonly DocumentOperation[]Reads a list of operations from a JSON string or a parsed value, checks each with
assertOperation, and returns a deeply frozen copy. Throws aModelError(INVALID_JSONfor a string that does not parse).
sourcefunction assertOperation(value: unknown): asserts value is DocumentOperationChecks 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 byapplyOperations. Throws aModelError.
sourcefunction assertChangedDocument( value: unknown, changed: ChangedDocumentEntities ): asserts value is DesignDocumentValidates 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.
sourceinterface ChangedDocumentEntitiesNames 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>sourcereadonly pageIds: ReadonlySet<string>sourcereadonly assetIds: ReadonlySet<string>sourcereadonly componentIds: ReadonlySet<string>since 0.23.0sourceComponents 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.
sourceclass DocumentStoreHolds 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.checkoutorDocumentStore.redowith an ID. Named branches point at commits and follow the head while checked out. A new store starts on themainbranch 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 aModelError.new (initialDocument: DesignDocument | unknown, options: StoreOptions = {})sourceValidates and freezes
initialDocumentwithsnapshotDocumentand makes it the root commit.getSnapshot(): DesignDocumentsourceThe head commit’s document.
getHeadCommitId(): CommitIdsourcegetRootCommitId(): CommitIdsourcegetCurrentBranch(): string | nullsourceThe checked-out branch, or
nullwhen the head is detached.getBranches(): Readonly<Record<string, CommitId>>sourceEvery branch name and the commit it points to.
getCommit(id: CommitId): CommitRecord | undefinedsourcegetHistoryGroupId(id: CommitId = this.#headCommitId): string | undefinedsourceThe 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 | undefinedsourceThe document as of a commit, or
undefinedfor an unknown ID.listCommits(): readonly CommitRecord[]sourceEvery commit, ordered by
CommitRecord.sequence.listChildren(id: CommitId = this.#headCommitId): readonly CommitId[]sourceThe commits made directly on top of
id(the head by default).preview( operations: readonly DocumentOperation[] ): ReturnType<typeof applyOperations>sourceApplies operations to the head document without committing them.
dispatch(input: TransactionInput): CommitResult | nullsourceApplies
input.operationsto 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. WithamendHead, or ahistoryGroupIdequal 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
nullwhen nothing changed (or an amended commit was removed). Throws aModelErrorwhenbaseCommitIdis 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.0sourceFreezes or unfreezes the document. While frozen,
dispatch,undo,redo,squashHistory,checkoutandcheckoutBranchchange nothing and returnnull, 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(): booleansourceTrue between
setFrozen(true)andsetFrozen(false).canUndo(): booleansourceTrue unless the head is the root commit.
canRedo(): booleansourceTrue when
redowith no argument has a commit to move to.undo(): CommitResult | nullsourceMoves the head (and the current branch) to the head’s parent and remembers the old head for
redo. Returnsnullat the root commit.redo(id?: CommitId): CommitResult | nullsourceMoves the head to a child commit. Without
idit picks the most recently undone child, or the only child when there is exactly one, and returnsnullwhen there is none. Throws aModelErrorwhenidis not a child of the head.getNextSequence(): numbersourceThe sequence number the next new commit gets.
squashHistory( fromSequence: number, options: { readonly onlyCommits?: ReadonlySet<CommitId>; } = {} ): CommitResult | nullsourceFolds the commits made since
fromSequence(seegetNextSequence) 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 beforefromSequence, 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 andnullis 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
nullwhen there were fewer than two commits to fold, they were removed, or a commit outsideonlyCommitswas found. Throws aModelErrorwhen a branch other than the checked-out one points at a commit it would remove.checkout(id: CommitId): CommitResult | nullsourceMoves the head to any commit and detaches it from its branch, so later commits move no branch. Clears the redo entries. Returns
nullwhenidis already the head. Throws aModelErrorfor an unknown commit.createBranch(name: string, from: CommitId = this.#headCommitId): voidsourceAdds a branch pointing at
from(the head by default) without checking it out. Throws aModelErrorif the name is empty, holds anullcharacter, or is taken, or if the commit is unknown.checkoutBranch(name: string): CommitResult | nullsourceMakes
namethe current branch and moves the head to its commit. Clears the redo entries. Returnsnullwhen the head does not move. Throws aModelErrorfor an unknown branch.renameBranch(previousName: string, nextName: string): voidsourceRenames a branch. Throws a
ModelErrorif the old name is unknown or the new one is invalid or taken.deleteBranch(name: string): voidsourceDeletes a branch name; its commits stay. Deleting the current branch detaches the head. Throws a
ModelErrorfor an unknown branch.subscribe(target: SubscriptionTarget, listener: StoreListener): UnsubscribesourceCalls
listenerafter each head move whose changes touchtarget(seeSubscriptionTarget). Returns a function that removes the listener.subscribeState(listener: StoreStateListener): UnsubscribesourceCalls
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(): PersistedStoresourceThe saved form of the whole store, as a frozen plain object.
serialize(indentation = 0): stringsourcetoJSONwritten as JSON with sorted keys. Same asserializeStore.static fromJSON( input: string | unknown, options: StoreOptions = {}, storeMigrations: StoreMigrationRegistry = defaultStoreMigrations, documentMigrations: MigrationRegistry = defaultMigrations ): DocumentStoresourceLoads a store saved by
serialize. Same asdeserializeStore.
sourceinterface StoreOptionsOptions for
DocumentStoreanddeserializeStore.readonly rootCommitId?: CommitIdsourceThe root commit’s ID. Ignored when loading, which keeps the saved one.
readonly idFactory?: () => CommitIdsourceMakes new commit IDs. It must not repeat an ID in the store, or
dispatchthrows. The default combines the time, a counter and random characters.readonly now?: () => numbersourceThe clock for
CommitRecord.timestamp. Defaults toDate.now.readonly onListenerError?: (error: unknown) => voidsourceReceives errors thrown by listeners, so one failing listener does not stop the others. Errors are dropped when omitted.
sourceinterface TransactionInputThe argument to
DocumentStore.dispatch.readonly operations: readonly DocumentOperation[]sourcereadonly baseCommitId?: CommitIdsourceThe head commit the caller based the operations on. When given and the head has moved since,
dispatchthrows instead of applying them.readonly historyGroupId?: stringsourceGroups 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
nullcharacters.readonly message?: stringsourcereadonly actor?: CommitActorsourcereadonly amendHead?: booleansourceFolds 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
canRedofirst.
sourceinterface CommitResultThe commit the head now points to, and its document.
readonly commit: CommitRecordsourcereadonly document: DesignDocumentsource
sourceinterface CommitRecordOne entry in a
DocumentStore’s history. Commits form a tree.readonly id: CommitIdsourcereadonly parentId: CommitId | nullsourceThe commit this one was made on top of;
nullonly for the root commit.readonly sequence: numbersourceCreation order within the store: 0 for the root, then counting up. It also orders
DocumentStore.listCommits.readonly timestamp: numbersourceWhen the commit was made or last amended, from
StoreOptions.now.readonly message?: stringsourcereadonly actor?: CommitActorsourcereadonly operations: readonly DocumentOperation[]sourceThe operations that turn the parent’s document into this commit’s.
readonly inverseOperations: readonly DocumentOperation[]sourceThe operations that turn this commit’s document back into the parent’s.
readonly changes: ChangeSetsourceWhat
operationschanged relative to the parent.
sourceinterface CommitActorWho made a commit. The root commit’s actor is
{ kind: "system", id: "taco-slides" }.readonly kind: "human" | "assistant" | "system"sourcereadonly id: stringsourceAn app-defined identifier for the person or agent.
sourceinterface StoreChangeEventPassed to
DocumentStore.subscribelisteners after the head moves. The event is deeply frozen.readonly reason: TransitionReasonsourcereadonly previousCommitId: CommitIdsourceThe head before the change. Equal to
currentCommitIdwhen a dispatch amended the head commit in place.readonly currentCommitId: CommitIdsourcereadonly previous: DesignDocumentsourcereadonly current: DesignDocumentsourcereadonly operations: readonly DocumentOperation[]sourceOperations that transform
previousintocurrentfor this transition.readonly changes: ChangeSetsource
sourcetype TransitionReason = "dispatch" | "undo" | "redo" | "checkout"Which
DocumentStorecall moved the head.checkoutcoverscheckoutBranchtoo.
sourcetype SubscriptionTarget = | { readonly kind: "document" } | { readonly kind: "node"; readonly nodeId: NodeId } | { readonly kind: "page"; readonly pageId: PageId } | { readonly kind: "asset"; readonly assetId: AssetId }What a
DocumentStore.subscribelistener 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 inChangeSet.changedPageIds, or any node in its tree changed.asset: a change to that asset.
sourcetype StoreListener = (event: StoreChangeEvent) => voidA
DocumentStore.subscribecallback.
sourcetype StoreStateListener = () => voidA
DocumentStore.subscribeStatecallback.
sourcetype Unsubscribe = () => voidRemoves 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.
sourcefunction serializeStore(store: DocumentStore, indentation = 0): stringWrites 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.
sourcefunction deserializeStore( input: string | unknown, options: StoreOptions = {}, storeMigrations: StoreMigrationRegistry = defaultStoreMigrations, documentMigrations: MigrationRegistry = defaultMigrations ): DocumentStoreLoads 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 withdocumentMigrations(seeDocumentMigration.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
ModelErrorfor invalid or inconsistent data.
sourcetype PersistedStore = PersistedStoreV1The current saved-history format.
sourceinterface PersistedStoreV1The saved form of a
DocumentStore: the root document plus every commit’s operations, the head, branches and redo stack. Produced byDocumentStore.toJSON.readonly storeVersion: typeof CURRENT_STORE_SCHEMA_VERSIONsourcereadonly documentSchemaVersion: typeof CURRENT_DOCUMENT_SCHEMA_VERSIONsourcereadonly rootCommitId: CommitIdsourcereadonly rootDocument: DesignDocumentsourcereadonly commits: readonly PersistedCommitV1[]sourcereadonly headCommitId: CommitIdsourcereadonly branches: Readonly<Record<string, CommitId>>sourcereadonly currentBranch: string | nullsourcereadonly redoStack: readonly CommitId[]source
sourceinterface PersistedCommitV1A commit as saved. Inverse operations and changes are not saved; loading works them out again by replaying the operations.
readonly id: CommitIdsourcereadonly parentId: CommitId | nullsourcereadonly sequence: numbersourcereadonly timestamp: numbersourcereadonly message?: stringsourcereadonly actor?: CommitActorsourcereadonly operations: readonly DocumentOperation[]source
sourceconst CURRENT_STORE_SCHEMA_VERSION: 1The version of the saved-history format written by
serializeStore. It is separate fromCURRENT_DOCUMENT_SCHEMA_VERSIONand counts up by whole numbers.
sourceclass StoreMigrationRegistryAn ordered chain of
StoreMigrationsteps, used bydeserializeStore.new (migrations: readonly StoreMigration[] = [])sourceregister(migration: StoreMigration): thissourceAdds a step and returns the registry. Throws a
ModelErrorwith codeINVALID_SCHEMAif 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): unknownsourceRuns the steps from the input’s
storeVersionup toCURRENT_STORE_SCHEMA_VERSION. Throws aModelErrorwith codeUNSUPPORTED_VERSION,MISSING_MIGRATIONorMIGRATION_FAILED.
sourceinterface StoreMigrationOne step that upgrades saved history by one
storeVersion. Document data inside it is upgraded separately by the document migrations.readonly from: numbersourcereadonly to: numbersourceMust be
from + 1.migrate(input: unknown): unknownsourceReturns the upgraded store as plain JSON with
storeVersionset toto.
sourceconst defaultStoreMigrations: StoreMigrationRegistryThe 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.
sourceinterface RectAn axis-aligned rectangle;
xandyare its top-left corner.readonly x: numbersourcereadonly y: numbersourcereadonly width: numbersourcereadonly height: numbersource
sourcetype TransformMatrix = readonly [ a: number, b: number, c: number, d: number, e: number, f: number ]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
sourceconst IDENTITY_TRANSFORM: TransformMatrix
sourcefunction getNodeBounds(node: SceneNode): RectReturns layout bounds in the coordinate system of the node’s parent.
sourcefunction getRotatedRectBounds(frame: Rect, rotation: number): RectReturns the parent-space axis-aligned bounds of a rotated frame.
sourcefunction resizeRotatedRect( frame: Rect, rotation: number, size: { readonly width: number; readonly height: number }, anchor: { readonly x: number; readonly y: number } = { x: 0, y: 0 } ): RectChanges 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.
anchoris a fraction of the size, so is the center. With the rotation byrotationdegrees (clockwise in y-down space), the anchor sits at this offset from the frame’sx,y:and the new position is .
sourcefunction getPathBounds( pathData: string, transform: TransformMatrix = IDENTITY_TRANSFORM ): Rect | nullReturns tight fill bounds after applying an SVG-compatible affine transform.
sourcefunction getVectorNetworkBounds( vectorNetwork: VectorNetwork, transform: TransformMatrix = IDENTITY_TRANSFORM ): Rect | nullReturns tight network-edge bounds after applying an affine transform.
sourcefunction getGroupChildrenBounds( nodes: NodeRecord, group: GroupNode ): Rect | nullReturns the union of a group’s children bounds in the group’s own coordinate system, or
nullfor a group without children. Rotated children contribute their rotated bounding box and nested groups contribute their own (already fitted) frame.
sourcefunction fitGroupToChildren( nodes: NodeRecord, group: GroupNode ): FittedGroup | nullFits a group’s frame to its children without moving anything on the page. Returns
nullwhen the frame already matches. An empty group keeps its position and collapses to zero size.
sourceinterface FittedGroupreadonly group: GroupNodesourcereadonly children: readonly SceneNode[]sourceChildren whose local position changed because the group origin moved.
sourcefunction fitAllGroups(nodes: NodeRecord): NodeRecordFits 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.
sourcefunction buildParentMap(nodes: NodeRecord): Map<NodeId, NodeId>
sourcefunction nodeDepth(parents: ReadonlyMap<NodeId, NodeId>, nodeId: NodeId): number
sourceinterface ShapePointPure 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: numbersourcereadonly y: numbersource
since 0.12.0sourceconst MIN_SHAPE_POINT_COUNT: 3Figma’s polygon and star point counts.
sourceconst MAX_SHAPE_POINT_COUNT: 60
sourceconst DEFAULT_POLYGON_POINT_COUNT: 3Figma’s defaults for newly drawn shapes.
sourceconst DEFAULT_STAR_POINT_COUNT: 5
sourceconst DEFAULT_STAR_INNER_RADIUS: 0.382
sourcefunction isValidShapePointCount(value: number): boolean
sourcefunction polygonPoints( pointCount: number, width: number, height: number ): ShapePoint[]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 of starts on the unit circle (y points down)
and is then scaled into the frame by its bounding box: , and likewise with the height (0 when the span is 0).
sourcefunction starPoints( pointCount: number, innerRadius: number, width: number, height: number ): ShapePoint[]The alternating outer and inner points of a star in frame-local coordinates, clockwise from the top outer point.
innerRadiusis 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 of iswith =
innerRadius; the stretch is the same aspolygonPoints.
sourcefunction closedPolygonVectorNetwork( points: readonly ShapePoint[], idPrefix = "shape" ): VectorNetworkA closed straight-edged network through the given points, one region.
sourcefunction validateEllipseArc(arc: EllipseArc): string | null
sourcefunction ellipseArcSweep(arc: EllipseArc): numberThe sweep in degrees, positive clockwise; at most one full turn: .
sourcefunction isWholeEllipseArc(arc: EllipseArc | undefined): booleanTrue when the arc draws the plain whole ellipse.
sourcefunction ellipseVectorNetwork( width: number, height: number, arc?: EllipseArc, idPrefix = "shape" ): VectorNetworkThe network for an ellipse filling a
widthbyheightframe, 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 in degrees (0 points right, positive turns clockwise because y points down) lands atwhere on the outer edge and =
innerRadiuson the inner edge.
sourcefunction lineEndpoints( line: Pick< LineNode, | "frame" | "rotation" | "flipX" | "flipY" > ): { readonly start: ShapePoint; readonly end: ShapePoint }The start and end points of a line in its parent’s coordinates. With frame center , width , rotation (degrees converted to radians) and when
flipXis set, else :
sourcefunction lineFrameFromEndpoints( start: ShapePoint, end: ShapePoint ): { readonly frame: Rect; readonly rotation: number }The zero-height frame and rotation of a line between two parent-space points. The rotation is in degrees within (-180, 180].
sourcefunction constrainLineEndpoint( anchor: ShapePoint, point: ShapePoint ): ShapePointMoves
pointso the segment fromanchorkeeps 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.
sourcefunction connectorKeyPoints(node: SceneNode): readonly ConnectorKeyPoint[]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,BOTTOMandLEFTmagnets. - 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_COUNTpoints. - A star: its outer tips. Its inner corners point into the star, so a connector there would cross it.
- A rectangle, frame, text, image or any other box: the middle of each side, as FigJam’s
sourceinterface ConnectorKeyPoint extends ShapePointA point on a layer’s outline that a connector end can be pinned to.
readonly place: ConnectorKeyPlacesourcereadonly normal: ShapePointsourceThe outline’s outward direction at the point.
sourcetype ConnectorKeyPlace = | { readonly side: ConnectorSide; readonly anchor?: undefined } | { readonly anchor: ConnectorAnchor; readonly side?: undefined }How an end is pinned to a key point: by a side (Figma’s magnet) or an anchor.
sourcefunction sameConnectorKeyPlace( a: ConnectorKeyPlace, b: ConnectorKeyPlace ): booleanTwo places are the same pin: the same side, or the same kind and index of anchor.
sourcefunction connectorPinnedPlace( node: SceneNode, endpoint: ConnectorEndpoint ): ConnectorOutlinePlace | undefinedThe place of an attached end pinned by
side,pointoranchoronnode, 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.
sourcefunction connectorAnchorPoint( node: SceneNode, anchor: ConnectorAnchor ): ConnectorOutlinePlace | undefinedThe 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.
sourcefunction connectorOutlinePointToward( node: SceneNode, toward: ShapePoint ): ShapePointWhere 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 whentowardis the centre.
sourceinterface ConnectorOutlinePlace extends ShapePointA place on a layer an end is drawn at, with the outward direction when it has one.
readonly normal?: ShapePointsource
sourceconst CONNECTOR_SIDE_NORMALS: Readonly<Record<ConnectorSide, ShapePoint>>The outward direction of each side of a layer’s box.
sourceconst MAX_CONNECTOR_EDGE_KEY_POINT_COUNT: 8A 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.
since 0.23.0sourceinterface ComponentDefinitionA reusable layer tree, stored once per document and shown in the Assets panel, never on a page. Its layers live in
DesignDocument.nodesunderrootId, so every node operation, undo and validation works on them unchanged. Instances (InstanceNode) show it on pages.readonly id: ComponentIdsourceMatches the key in
DesignDocument.components.readonly key: stringsourceA 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: stringsourceShown in the Assets panel. Must not be empty.
readonly description?: stringsourceOptional free text shown in the Assets panel.
readonly rootId: NodeIdsourceThe root of the component’s layer tree: always a frame with no parent that no page uses. Its
frame.xandframe.yare 0; its width and height are the natural size, which is the size shown in the component view. Itslayoutsizing (Hug or Fixed) is the size behaviour every instance starts with; seeLayerOverride.sizing.readonly properties?: readonly ComponentPropertyDefinition[]since 0.24.0sourceThe 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>>sourceFree-form JSON data kept with the component. The model does not read it.
since 0.23.0sourcetype ComponentId = Brand<string, "ComponentId">The ID of a
ComponentDefinition, its key inDesignDocument.components.
since 0.23.0sourceconst componentId: (value: string) => ComponentIdBrands a string as a
ComponentId. Throws aTypeErrorfor an empty string.
since 0.23.0sourceinterface InstanceNode extends FramedNodeBaseOne 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>(seeparseVirtualNodeId).The instance has no
children;isContainerNodeis false for it. ItsNodeBasefields apply to the whole instance, like a frame’s, and itsframe,rotation, flips andresizingplace it like any framed node.frameholds the resolved size, so geometry, selection and snapping code works without the resolver;applyOperationskeeps 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"sourcereadonly componentId: ComponentIdsourcereadonly overrides?: Readonly<Record<string, LayerOverride>>sourceChanges 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.0sourceThe 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.
since 0.23.0sourceinterface LayerOverrideThe changes one instance makes to one layer of its component, stored in
InstanceNode.overridesunder 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:
- A variable ID in
variableBindingsfor the field’s property: the variable’s value wins. - Otherwise a plain value in this override for the field (or
nullinvariableBindingsfor 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. - 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?: booleansourcereadonly opacity?: numbersourceFrom 0 (transparent) to 1 (opaque).
readonly characters?: stringsourceText 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 withparagraphs.readonly paragraphs?: readonly TextParagraph[]sourceText 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 | nullsourceText layers: the theme text style every run takes instead of its own;
nullclears the runs’ style IDs so they use the theme default.readonly textStyle?: TextStyleOverridessourceText 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 | nullsourceFrames and shapes: the node style to use;
nullclears the layer’s.readonly fills?: readonly Paint[]sourcereadonly strokes?: readonly Stroke[]sourcereadonly shadows?: readonly Shadow[]sourcereadonly layerBlur?: numbersourcereadonly backgroundBlur?: numbersourcereadonly imageAssetId?: AssetIdsourceImage layers: the image asset to show instead.
readonly componentId?: ComponentIdsourceNested instance layers: the component to show instead (a swap).
readonly sizing?: LayerSizingOverridesourceFrame layers, and the component root for the instance’s own size.
readonly variableBindings?: LayerOverrideVariableBindingssourcereadonly propertyValues?: ComponentPropertyValuessince 0.24.0sourceNested 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.
- A variable ID in
since 0.23.0sourceinterface LayerSizingOverrideWhich axes of a layer inside an instance hug their content or keep a fixed size, replacing the component frame’s
layout.widthSizingandlayout.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 ownresizing, like any framed node.readonly width?: "hug" | "fixed"sourcereadonly height?: "hug" | "fixed"source
since 0.23.0sourcetype LayerOverrideVariableBindings = Readonly< Partial<Record<VariableProperty, VariableId | null>> >Variable bindings an instance stores for one layer, keyed by property. A variable ID binds the property in that instance;
nullremoves the component’s binding for that property in that instance and leaves the plain value (the layer’s, or the override’s) in force.
since 0.23.0sourceconst LAYER_PATH_SEPARATOR: ";"The character that joins the IDs in a layer path and a virtual node ID. Stored node IDs never contain it.
since 0.23.0sourceinterface VirtualNodeIdThe parts of a virtual node ID: the stored instance the layer belongs to and the layer path inside it.
readonly instanceId: NodeIdsourceThe stored
InstanceNodeon a page or inside a component.readonly path: readonly NodeId[]sourceThe 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.
sourceconst isVirtualNodeId: (id: string) => booleanTrue when
idnames a layer inside an instance rather than a stored node.
since 0.23.0sourcefunction parseVirtualNodeId(id: string): VirtualNodeId | nullSplits a virtual node ID
<instance id>;<layer path>into its instance and layer path. Returnsnullfor a stored node ID (no separator) and for a malformed ID with an empty segment. This is the only place that splits virtual IDs.
sourcefunction virtualNodeId(instanceId: NodeId, path: readonly NodeId[]): NodeIdBuilds the virtual node ID of the layer at
pathinsideinstanceId.
sourcefunction layerPathKey(path: readonly NodeId[]): stringThe key of
InstanceNode.overridesfor the layer atpath.
since 0.23.0sourcefunction parseLayerPath(key: string): readonly NodeId[] | nullSplits an override key into its component node IDs, or returns
nullwhen 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.
sourcefunction storedNodeIdOf(id: NodeId): NodeIdThe 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.
sourcefunction componentRootIds(document: DesignDocument): readonly NodeId[]The root node IDs of every component, in
componentsorder.
sourcefunction findComponentByRoot( document: DesignDocument, rootId: NodeId ): ComponentDefinition | undefinedThe component whose root is
rootId, if any.
sourcefunction componentOwningNode( document: DesignDocument, nodeId: NodeId, parents: ReadonlyMap< NodeId, NodeId > = buildParentMap(document.nodes) ): ComponentDefinition | undefinedThe component whose layer tree contains the stored node
nodeId(the root included), orundefinedwhen the node is on a page or does not exist. Passparents(frombuildParentMap) when calling this for many nodes.
sourcefunction isComponentLayer(document: DesignDocument, nodeId: NodeId): booleanTrue when the stored node is a component layer (its root included) rather than a page node.
sourcefunction instanceNodeOf( document: DesignDocument, instanceId: NodeId ): InstanceNode | undefinedThe node an instance ID names, checked to be an
InstanceNode.
sourcefunction componentReferencesOf(node: SceneNode): readonly ComponentId[]The components a node refers to: an instance’s
componentId, and every component its overrides swap a nested instance to. Empty for other nodes.
sourcefunction instancesOfComponent( document: DesignDocument, componentId: ComponentId ): readonly NodeId[]The stored instance nodes of
componentId, by ID, sorted. Swaps in overrides do not count.
since 0.23.0sourceinterface ComponentNestingGraphWhich components contain which.
nests.get(a)holds everybsuch that a node undera’s root is an instance ofb, or an override undera’s root swaps a nested instance tob.nestedByis 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>>sourcereadonly nestedBy: ReadonlyMap<ComponentId, ReadonlySet<ComponentId>>source
sourcefunction componentNestingGraph(document: DesignDocument): ComponentNestingGraphBuilds the
ComponentNestingGraphof a document. Missing component references are kept as edges.
since 0.23.0sourcefunction findComponentCycle( graph: ComponentNestingGraph ): readonly ComponentId[] | nullFinds a cycle in the nesting graph: a list of component IDs where each nests the next and the last equals the first. Returns
nullwhen the graph has no cycle.
sourcefunction componentsNesting( graph: ComponentNestingGraph, componentId: ComponentId ): ReadonlySet<ComponentId>Every component whose tree contains
componentId, directly or through nesting. A change tocomponentIdalso changes the instances of these.
sourcefunction componentsNestedIn( graph: ComponentNestingGraph, componentId: ComponentId ): ReadonlySet<ComponentId>Every component that
componentId’s tree contains, directly or through nesting.
since 0.23.0sourcefunction wouldNestCycle( document: DesignDocument, hostId: ComponentId, placedId: ComponentId ): booleanReports whether placing an instance of
placedIdinsidehostId’s tree, or swapping a nested instance there toplacedId, would make a component contain itself. The editor disables such choices;assertDocumentrejects the result.
since 0.23.0sourceinterface ResolvedLayerOne derived layer of a resolved instance.
nodeis the component node with the instance’s overrides applied (by the precedence onLayerOverride) and itsframelaid out for the instance’s size, so the compiler compiles it exactly as it compiles a stored node, withvirtualIdas the render node ID.node.idequalsvirtualId, and a container’schildrenlist holds the virtual IDs ofchildren, in paint order.pathnames 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
virtualIdis the instance’s own ID, itspathis[component root ID](the override key for the instance’s paints and size), and itsnodeis aFrameNodecarrying the instance’sNodeBasefields,frame,rotation, flips andresizing, with the component root’slayout,styleandclipContent(overrides applied).instancenames the stored instance. A nested instance layer’spathis the path to the nested instance node, which is also its override key on the outer instance for its paints and sizing, aseditTargetForwrites it. - A nested instance inside the component is expanded the same way: its
nodeis aFrameNodebuilt like the root layer (the nested instance node’s placement andNodeBasefields, after the outer overrides, with the nested component root’slayout,styleandclipContent),instancegives the nested instance and the component it shows after any swap, andchildrenare the nested component’s layers, whose paths continue through it.
readonly virtualId: NodeIdsourcereadonly sourceNodeId: NodeIdsourceThe component node this layer is derived from.
readonly path: readonly NodeId[]sourceThe layer path inside the outermost instance; see
VirtualNodeId.path.readonly node: SceneNodesourcereadonly children: readonly ResolvedLayer[]sourcereadonly instance?: ResolvedLayerInstancesourcePresent on the root layer and on nested instance layers.
readonly inheritedVariableModes?: VariableModessince 0.26.0sourceThe variable modes the layer inherits from its parent layer, as
inheritedVariableModesgives 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.0sourceThe variable modes in effect at the layer, which its bound fields read. Present on the layers of a resolved instance.
since 0.23.0sourceinterface ResolvedLayerInstanceThe stored instance a resolved layer stands for, set on the root layer and on every nested instance layer.
readonly instanceId: NodeIdsourceThe stored
InstanceNode: the outer instance, or a nested one inside a component.readonly componentId: ComponentIdsourceThe component shown, after any swap by an outer override.
readonly node: InstanceNodesince 0.26.0sourceThe instance node as resolved: swapped, with outer overrides and the enclosing component’s computed fields (its own
propertyValuesincluded) applied. The component view draws a nested instance from it.readonly propertyValues?: ComponentPropertyValuessince 0.26.0sourceThe 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).
since 0.23.0sourceinterface ResolvedInstanceWhat
resolveInstancereturns: the instance’s derived layer tree.readonly instanceId: NodeIdsourcereadonly componentId: ComponentIdsourcereadonly root: ResolvedLayersourceThe layer standing for the instance itself; see
ResolvedLayer.readonly layers: ReadonlyMap<NodeId, ResolvedLayer>sourceEvery resolved layer by virtual ID, the root under the instance ID.
readonly componentIds: readonly ComponentId[]sourceThe 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.0sourceComputed 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.
since 0.23.0sourceinterface InstanceResolveCacheResolved 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
clearwhen 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(): voidsourcereadonly stats: { readonly hits: number; readonly misses: number; readonly evaluations: number; }sourceHow many
resolveInstanceandresolveComponentcalls 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.
sourcefunction createInstanceResolveCache(): InstanceResolveCacheMakes an
InstanceResolveCacheto pass toresolveInstance.
since 0.23.0sourceinterface ResolveInstanceOptionsreadonly cache?: InstanceResolveCachesourcereadonly variableModes?: VariableModessince 0.26.0sourceThe variable modes the instance inherits from its parent, as
inheritedVariableModesreturns them. Read from the document when omitted; the page compiler passes them as it walks the page.
since 0.23.0sourcefunction resolveInstance( document: DesignDocument, instanceId: NodeId, options?: ResolveInstanceOptions ): ResolvedInstanceDerives 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
ModelErrorwith codeINVALID_TREEwheninstanceIdis not an instance node or its component is missing.
since 0.23.0sourcefunction resolveComponent( document: DesignDocument, componentId: ComponentId, options?: ResolveInstanceOptions ): ResolvedLayerDerives the component view: the component’s own layer tree at its natural size with no overrides, as
ResolvedLayers whosevirtualIds are the stored node IDs. Isolation mode draws it; a nested instance inside it is expanded as inresolveInstance, 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.
since 0.23.0sourcefunction huggedInstanceSize( document: DesignDocument, instanceId: NodeId, options?: ResolveInstanceOptions ): { readonly width?: number; readonly height?: number }The size an instance takes on each axis it hugs, from its resolved root.
undefinedon an axis the instance keeps as stored (Fixed, Fill, or a component root without auto layout).
sourcefunction cascadeComponentChanges( document: DesignDocument, nodes: Record<string, SceneNode>, changedNodeIds: ReadonlySet<NodeId>, changedComponentIds: readonly ComponentId[], changed: Set<NodeId> ): voidThe 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.
nodesis the working copy after the operations and the layout pass; it is changed in place, and every node it changes is added tochanged.changedNodeIdsare the nodes the operations and the layout pass changed.
since 0.23.0sourceinterface OverrideRemapTreeOne 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: DesignDocumentsourcereadonly rootId: NodeIdsourcereadonly shownComponentAt?: (path: readonly NodeId[]) => ComponentId | undefinedsince 0.26.0sourceThe 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.
since 0.23.0sourcefunction remapOverrides( overrides: | Readonly<Record<string, LayerOverride>> | undefined, from: OverrideRemapTree, to: OverrideRemapTree, mapComponent: ( id: ComponentId ) => ComponentId = (id) => id ): Record<string, LayerOverride>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, withmapComponentfor instance swap values). Swap, redefine and paste use this, then store the result.
since 0.23.0sourcefunction overridePathResolves( document: DesignDocument, componentId: ComponentId, path: readonly NodeId[], overrides: Readonly<Record<string, LayerOverride>> = {}, shownComponentAt?: (path: readonly NodeId[]) => | ComponentId | undefined ): booleanWhether 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 componentshownComponentAtgives), and the last a node in that tree.
since 0.23.0sourcefunction pruneOverrides( document: DesignDocument, instance: InstanceNode ): InstanceNodeThe instance without the override keys that name layers its component no longer has (design 3.5); the same node when every key still resolves.
since 0.23.0sourcetype OverrideField = keyof LayerOverrideThe fields an instance can override; the keys of
LayerOverride.
sourceconst OVERRIDE_FIELDS: readonly OverrideField[]Every
OverrideField, inLayerOverrideorder.
since 0.23.0sourcetype 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 }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 withnode.replace.override: a layer inside an instance, or an instance’s own paints and size. Write it withsetOverrideOperations(anode.replaceof the instance with the changedoverrides[key]);sourceNodeIdis the component node the layer is derived from, for reading the base value.unsupported: the edit cannot be made here;reasonsays why, for the user (a structural field on a layer inside an instance, or a layer the component no longer has).
since 0.23.0sourcefunction editTargetFor( document: DesignDocument, id: string, field: string ): EditTargetDecides where an edit goes.
idis a stored node ID or a virtual ID (<instance>;<path>);fieldis the layer field being edited: anOverrideField, a node field name with the same meaning (assetIdforimageAssetId), orvariableBindings.<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) areunsupportedon a virtual ID: they need an edit to the component, or Detach.
since 0.23.0sourcetype LayerOverridePatch = { readonly [Field in OverrideField]?: LayerOverride[Field] | undefined }A change to one override entry: each present key replaces that field, and a key set to
undefinedremoves it (Reset for that field).
since 0.23.0sourcefunction withOverride( instance: InstanceNode, key: string, patch: LayerOverridePatch ): InstanceNodeReturns the instance with
patchmerged intooverrides[key]. Settingcharactersremovesparagraphsand the other way round, so the two never coexist. An entry left with no fields is removed, and an emptyoverridesmap is dropped. Pure: the input is not changed.
since 0.23.0sourcefunction withoutOverride( instance: InstanceNode, key?: string, field?: OverrideField ): InstanceNodeReturns the instance without an override: one field of one layer, one whole layer (
fieldomitted), or every override (keyomitted). Pure.
since 0.23.0sourcefunction setOverrideOperations( document: DesignDocument, instanceId: NodeId, key: string, patch: LayerOverridePatch ): readonly DocumentOperation[]The operations that write
patchintooverrides[key]of the instance: onenode.replace, or none when the instance is not an instance node.applyOperationsdrops a replace that changes nothing.
since 0.23.0sourcefunction resetOverrideOperations( document: DesignDocument, instanceId: NodeId, key?: string, field?: OverrideField ): readonly DocumentOperation[]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).
since 0.23.0sourceconst 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; }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]).
since 0.23.0sourceconst componentSampleDocuments: { readonly plain: () => DesignDocument; readonly nested: () => DesignDocument; readonly textOverride: () => DesignDocument; readonly variableBoundOverride: () => DesignDocument; readonly swappedNested: () => DesignDocument; }Every component sample by name, for tests that run over all of them.
since 0.23.0sourcefunction plainInstanceSample(): DesignDocumentOne Button component and one instance of it on the page, with no overrides. The instance is at the component’s natural size.
since 0.23.0sourcefunction nestedInstanceSample(): DesignDocumentA Card component whose tree holds an instance of Button, and one Card instance on the page. The nesting graph has the edge Card → Button.
since 0.23.0sourcefunction textOverrideSample(): DesignDocumentTwo Button instances with text overrides:
button-1changes the label’s characters only, andbutton-2stores styled paragraphs because part of its label is bold. Both keep the component’s font.
since 0.23.0sourcefunction variableBoundOverrideSample(): DesignDocumentThe Button label’s colour is bound to the
primaryvariable in the component. The instance rebinds it todangerfor the label, and for the root it overrides the fills with a plain colour while removing the component’sfillColorbinding, so the plain override cannot be repainted by the variable.
since 0.23.0sourcefunction swappedNestedInstanceSample(): DesignDocumentThe 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.
since 0.23.0sourceinterface IsolationLevelOne 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: ComponentIdsourceThe component edited at this level. For a swapped nested instance, the component it is swapped to.
readonly instanceId: NodeId | nullsourceThe instance the level was opened from, drawn as the instance view, or
nullwhen 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).
since 0.23.0sourcetype IsolationView = "instance" | "component"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.
since 0.23.0sourcetype EditReach = | { readonly kind: "this-instance"; readonly instanceId: NodeId } | { readonly kind: "component"; readonly componentId: ComponentId; readonly instanceCount: number; }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)”.
since 0.23.0sourcetype IsolationEditRoute = | { readonly kind: "edit"; readonly target: WritableTarget; readonly overridden: boolean; readonly reach: EditReach; } | { readonly kind: "unsupported"; readonly reason: string }Where an edit made in isolation goes (design 5.5, Edit rules).
edit: writetargetaseditTargetFordescribes it.overriddenis 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”.reachis the question F signal.unsupported: the edit cannot be made in this view;reasonis for the user.
since 0.23.0sourcefunction isolationEditRoute( document: DesignDocument, level: IsolationLevel, view: IsolationView, id: NodeId, field: string ): IsolationEditRouteWhere an edit to
fieldof the layerid, made inviewat isolation levellevel, goes (design 5.5, Edit rules and nesting).fieldis as foreditTargetFor: an override field, a node field name,variableBindings.<property>, ortextStyle.<member>for one text style member (routed by that member and its own binding only).Component view:
idis 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 iseditTargetFor’s answer, checked to stay inside the component.Instance view:
idis 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.
since 0.23.0sourcefunction componentOfInstance( document: DesignDocument, instanceId: NodeId ): ComponentId | undefinedThe 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 owncomponentId.A nested instance whose
componentIdis 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.undefinedwhen the ID does not name an instance or the component is missing.
since 0.23.0sourcefunction componentInstanceCount( document: DesignDocument, componentId: ComponentId ): numberHow 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).
since 0.23.0sourceinterface IsolationBreadcrumbOne crumb of the isolation bar: the page, then one per level.
readonly label: stringsourcereadonly depth: numbersourceHow many levels stay open when the crumb is chosen: 0 for the page.
since 0.23.0sourcefunction isolationBreadcrumbs( document: DesignDocument, pageName: string, levels: readonly IsolationLevel[] ): readonly IsolationBreadcrumb[]The isolation bar’s breadcrumbs: page › component › nested component.
since 0.23.0sourceinterface ComponentViewOne 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: ComponentIdsourcereadonly rootId: NodeIdsourceThe component root; the view draws the component’s layer tree from it.
readonly label: stringsourcereadonly bounds: RectsourceThe 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.0sourceA 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.
sourceinterface IsolationViewsOptionsWhat
isolationViewsdraws 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.0sourceProperty values whose view is drawn as well, whatever
varyholds: 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.
since 0.23.0sourceinterface IsolationViewsWhat the canvas draws for one isolation level. The views are not part of the document; they are never saved or exported.
readonly componentId: ComponentIdsourcereadonly instanceId: NodeId | nullsourceThe instance view: the opened instance, drawn in place with its overrides;
nullfrom the Assets panel.readonly componentViews: readonly ComponentView[]sourceThe component views, the defaults first, in reading order (row by row, left to right).
readonly setBounds?: Rectsince 0.27.0sourceThe outline around the views when more than one is drawn, in the same coordinates as their bounds: the views with
COMPONENT_SET_PADDINGaround them, as Figma outlines a component set. Absent with one view.
sourceconst COMPONENT_VIEW_GAP: 40The gap between two views, in canvas units.
since 0.27.0sourceconst COMPONENT_SET_PADDING: 40The space between the set outline and the views inside it, in canvas units.
since 0.27.0sourcefunction isolationViewCombinations( component: ComponentDefinition, options?: IsolationViewsOptions ): readonly VariantCombination[]The combinations
isolationViewsdraws foroptions, in drawing order: the defaults alone with no options; every combination ofvary; and the combination ofalsoafter them when it is not one of those already.
since 0.23.0sourcefunction isolationViews( document: DesignDocument, level: IsolationLevel, options?: IsolationViewsOptions ): IsolationViews | undefinedThe 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 byoptions.alsogoes 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.
since 0.14.0sourceinterface ExportSettingOne 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: ExportFormatsourcereadonly constraint: ExportConstraintsourcereadonly suffix: stringsourceAdded to the file name after the layer name; may be empty.
readonly quality: ExportQualitysourcereadonly resampling: ExportResamplingsourcereadonly ignoreOverlappingLayers: booleansourcePNG, JPEG and SVG only. When
trueonly the layer and its children are drawn; whenfalseevery visible layer inside the layer’s bounds is drawn.readonly includeIdAttribute: booleansourceSVG only: gives drawn elements “id” attributes made from layer names.
sourcetype ExportFormat = (typeof EXPORT_FORMATS)[number]
since 0.14.0sourceconst EXPORT_FORMATS: readonly ["png", "jpeg", "svg", "pdf"]File formats a layer can export to.
since 0.14.0sourcetype ExportConstraint = | { readonly type: "scale"; readonly value: number } | { readonly type: "width"; readonly value: number } | { readonly type: "height"; readonly value: number }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.
sourcetype ExportQuality = (typeof EXPORT_QUALITIES)[number]
since 0.14.0sourceconst EXPORT_QUALITIES: readonly ["low", "medium", "high"]JPEG compression level, or the raster resolution of PDF effects.
sourcetype ExportResampling = (typeof EXPORT_RESAMPLINGS)[number]
since 0.14.0sourceconst EXPORT_RESAMPLINGS: readonly ["detailed", "basic"]“detailed” samples images bicubically; “basic” uses the nearest pixel.
sourceconst MAX_EXPORT_SCALE: 100Largest multiplier a scale constraint may hold.
sourceconst MAX_EXPORT_DIMENSION: 16384Largest pixel width or height a size constraint may hold; the renderer’s own limit.
sourceconst MAX_EXPORT_SUFFIX_LENGTH: 100Longest suffix, in UTF-16 code units.
sourceconst MAX_EXPORT_SETTINGS: 64Most rows one node may hold.
sourcefunction defaultExportQuality(format: ExportFormat): ExportQualityThe quality a row starts with when it switches to
format.
sourcefunction isVectorExportFormat(format: ExportFormat): format is "svg" | "pdf"True when the format ignores the size constraint and exports at 1x.
sourcefunction nextExportSetting(existing: readonly ExportSetting[]): ExportSettingThe 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.
sourcefunction exportConstraintProblem(constraint: ExportConstraint): string | nullThe reason a constraint is invalid, or
nullwhen it is valid.
sourcefunction exportSuffixProblem(suffix: string): string | nullThe reason a suffix is invalid, or
nullwhen it is valid.
sourcefunction formatExportConstraint(constraint: ExportConstraint): stringA constraint as the scale field shows it: “2x”, “512w” or “512h”.
sourcefunction parseExportConstraint(text: string): ExportConstraint | nullReads what a person typed in the scale field. “2”, “2x” and “2X” are multipliers, “512w” a fixed width and “512h” a fixed height. Returns
nullwhen the text is not a valid constraint.
sourcefunction exportFileExtension(format: ExportFormat): stringThe file extension for a format; JPEG files use “.jpg”, as Figma’s do.
sourcefunction exportFileStem(name: string): stringThe part of a layer name that is safe in a file name on every common system. Path separators and reserved characters become “-“.
sourcefunction exportFileName( name: string, setting: Pick<ExportSetting, "format" | "suffix"> ): string<layer name><suffix>.<extension>, as Figma names each exported file.
sourcefunction uniqueExportFileNames(names: readonly string[]): readonly string[]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.
sourcefunction uniqueSvgId(name: string, used: Set<string>): stringA 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 “_”.
usedholds ids already in the file; a repeated id gets “_2”, “_3” and so on. The result is added toused.
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.
sourcefunction parseExpression(source: string): ExpressionParseResultParses source text into an expression tree, or reports the first syntax error.
sourcetype ExpressionParseResult = | { readonly ok: true; readonly expression: Expression } | { readonly ok: false; readonly error: ExpressionError }What
parseExpressiongives back: a tree, or the first error.
sourceconst EXPRESSION_SOURCE_LIMIT: 4000The longest expression source the parser accepts, in UTF-16 code units.
sourceconst EXPRESSION_DEPTH_LIMIT: 64The deepest nesting of sub-expressions the parser accepts.
sourcetype 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; }A node of the expression syntax tree.
kindtells the construct.
sourceinterface ExpressionSpanA half-open range of UTF-16 offsets in an expression’s source text.
readonly start: numbersourcereadonly end: numbersource
sourceinterface ExpressionErrorA parse or type error, with the text it points at and an optional fix.
readonly message: stringsourcereadonly span: ExpressionSpansourcereadonly hint?: stringsourceA short suggested fix, for example “write
8px”.
sourcetype ExpressionUnit = "px" | "%" | "deg"The unit suffix of a number literal:
8px,50%,45deg.
sourcetype ExpressionBinaryOperator = | "or" | "and" | "==" | "!=" | "<" | "<=" | ">" | ">=" | "++" | "+" | "-" | "*" | "/"The two-operand operators, from
or(lowest precedence) to/(highest).
sourceinterface ExpressionColorAn sRGB colour with every channel from 0 to 1, as a
#rrggbb[aa]literal parses to.readonly red: numbersourcereadonly green: numbersourcereadonly blue: numbersourcereadonly alpha: numbersource
sourceinterface ExpressionArgumentOne argument of a call: positional, or named with
name: value.readonly name?: stringsourcereadonly nameSpan?: ExpressionSpansourcereadonly value: Expressionsource
sourceinterface ExpressionFieldUpdateOne
name: valuepair insidewith { ... }.readonly name: stringsourcereadonly nameSpan: ExpressionSpansourcereadonly value: Expressionsource
sourcetype ExpressionPattern = | { readonly kind: "option"; readonly name: string; readonly span: ExpressionSpan; } | { readonly kind: "boolean"; readonly value: boolean; readonly span: ExpressionSpan; }A
matcharm pattern: an enum option (.small) or a boolean (true).
sourceinterface ExpressionMatchArmOne arm of a
match: one or more patterns and the value they give.readonly patterns: readonly ExpressionPattern[]sourcereadonly body: Expressionsource
sourceconst EXPRESSION_KEYWORDS: readonly [ "if", "then", "else", "let", "in", "match", "with", "or", "and", "not", "true", "false", "stored", "var" ]The words the language reserves; they cannot name a property or a
let.
sourcetype ExpressionKeyword = (typeof EXPRESSION_KEYWORDS)[number]
sourcefunction parseColorLiteral(text: string): ExpressionColor | undefinedReads a
#rgb,#rgba,#rrggbbor#rrggbbaaliteral into channels from 0 to 1.
sourcetype 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 }A type of the expression language. Every value has exactly one.
sourcetype ExpressionUnitTypeKind = "length" | "percent" | "angle"The unit types: numbers that carry a unit and never mix with each other.
sourceinterface ExpressionEnumTypeAn enum type: a named set of options. Two enum types are the same when their names match.
readonly kind: "enum"sourcereadonly name: stringsourceUnique 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[]sourceOption names in display order, as written after the dot:
.large.
sourceconst 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" }; }The types with a single
kindand nothing else, ready to use.
sourceconst STROKE_ALIGN_TYPE: ExpressionEnumTypeThe enum type of a stroke’s
alignargument and field.
sourcefunction expressionListType(element: ExpressionType): ExpressionTypelist(element).
sourcefunction expressionEnumType( name: string, options: readonly string[] ): ExpressionEnumTypeAn enum type with the given name and options.
sourcefunction expressionTypesEqual(a: ExpressionType, b: ExpressionType): booleanTrue when a value of type
ais also a value of typeb. Enums match by name.
sourcefunction isExpressionUnitType( type: ExpressionType ): type is { readonly kind: ExpressionUnitTypeKind }True for
length,percentandangle.
sourcefunction isExpressionNumeric(type: ExpressionType): booleanTrue for
numberand the unit types: what arithmetic, comparison andminaccept.
sourcefunction formatExpressionType(type: ExpressionType): stringThe type as the language writes it:
length,list(paint),Size (.small, .large).
sourcefunction expressionRecordFields( type: ExpressionType ): Readonly<Record<string, ExpressionType>> | undefinedThe fields a record type exposes to
value.fieldandvalue with { field: ... }, or undefined for a type that is not a record. Field names follow the language’s snake_case (top_left,line_height).
sourceconst EXPRESSION_LIBRARY: Readonly<Record<string, ExpressionFunction>>Every constructor and standard library function, by name. A null prototype keeps
toStringand the like out of lookups.
sourceinterface ExpressionFunctionA constructor or standard library function with its overloads, tried in order.
readonly name: stringsourcereadonly group: "numbers" | "colours" | "strings" | "constructors"sourcereadonly summary: stringsourcereadonly overloads: readonly ExpressionSignature[]source
sourceinterface ExpressionSignatureOne way to call a function: its parameters, in order, and its result type.
readonly parameters: readonly ExpressionParameter[]sourcereadonly rest?: ExpressionTypesourceWhen set, any number of extra positional arguments of this type may follow the parameters.
readonly result: ExpressionTypesource
sourceinterface ExpressionParameterOne parameter of a signature. A parameter with a default may be left out.
readonly name: stringsourcereadonly type: ExpressionTypesourcereadonly defaultSource?: stringsourceThe default, as expression source text (
0px,.center); absent for a required parameter.
sourcefunction formatExpressionSignature( name: string, signature: ExpressionSignature ): stringA signature as the language writes it:
min(a: number, b: number, ...) -> number.
sourcefunction checkExpression( expression: Expression, expected: ExpressionType, environment: ExpressionEnvironment ): ExpressionCheckResultType-checks an expression against the type its field expects (
booleanfor a condition), the component’s properties and the document’s variables.
sourcefunction checkExpressionSource( source: string, expected: ExpressionType, environment: ExpressionEnvironment ): ExpressionCheckResultParses and checks source text in one step; a syntax error is the only error reported.
sourceinterface ExpressionEnvironmentWhat 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 | undefinedsourceThe type of the component property with this name, or undefined when there is none.
variableType(name: string): ExpressionType | undefinedsourceThe type of the document variable written
"Collection/Name", or undefined when there is none.readonly storedType?: ExpressionTypesourceThe type of the component’s base value of the field being computed, which is what
storedreads. Leave it out for a condition, wherestoredhas no meaning.
sourceinterface ExpressionCheckResultThe outcome of checking one expression.
readonly ok: booleansourceTrue when there are no errors.
readonly type?: ExpressionTypesourceThe type of the whole expression, when it could be told.
readonly errors: readonly ExpressionError[]sourcereadonly references: readonly ExpressionReference[]sourceEvery property, option and variable the expression names, in source order.
sourcetype 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; }A place in the source that names something the editor can rename.
sourcefunction renameInExpression( source: string, expected: ExpressionType, environment: ExpressionEnvironment, renames: | ExpressionRename | readonly ExpressionRename[] ): ExpressionRenameResultRewrites 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
.largecan 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. Aletname that shadows a property is not a reference to it.
sourcetype 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 }What to rename. Names are the ones in force before the rename.
sourceinterface ExpressionRenameResultThe rewritten source and how many references changed.
readonly source: stringsourcereadonly count: numbersource
sourcefunction expressionSignatureAccepts( signature: ExpressionSignature, positionalCount: number, named: ReadonlySet<string> ): booleanTrue 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.
sourcetype 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[] }A value of the expression language.
kindmatches the type’skind.
sourcetype ExpressionEffect = | { readonly kind: "shadow"; readonly shadow: Shadow } | { readonly kind: "layer_blur"; readonly radius: number } | { readonly kind: "background_blur"; readonly radius: number }One effect. The model keeps shadows in
shadowsand the two blurs as plain radii, so this is the union step 3.3 sorts into those fields.
sourcetype ExpressionTextStyle = & Pick<TextStyle, "fontFamily" | "fontWeight" | "fontSize"> & { readonly lineHeightPixels: number; readonly letterSpacing: number }The parts of a text style an expression can build; the name and colour come from elsewhere.
lineHeightPixelsis the language’sline_height, a length in pixels. The model’sTextStyle.lineHeightis a multiple of the font size, so whatever applies this value must divide byfontSizefirst.
sourcefunction expressionColorToHex(color: ExpressionColor): stringThe colour as the model stores it:
#rrggbb, or#rrggbbaawhen the alpha is below 1.
sourcefunction expressionColorFromHex(text: string): ExpressionColorReads a model colour string; a string that is not a hex colour reads as opaque black.
sourcefunction zeroExpressionValue(type: ExpressionType): ExpressionValueThe value
0of 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.
sourcefunction expressionValuesEqual(a: ExpressionValue, b: ExpressionValue): booleanTrue when two values of one type are equal, compared by value; what
==gives.
sourcefunction evaluateExpression( expression: Expression, environment: ExpressionValueEnvironment ): ExpressionValueEvaluates 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.
sourceinterface ExpressionValueEnvironmentWhat 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 | undefinedsourceThe value of the component property with this name, or undefined when there is none.
variableValue(name: string): ExpressionValue | undefinedsourceThe value of the document variable
"Collection/Name"in the active mode, or undefined when there is none.readonly stored?: ExpressionValuesourceThe component’s base value of the field being computed, which
storedreads.
sourcefunction finiteExpressionNumber(value: number): number0 for a number that is not finite (from very large values or 0 / 0); negative zero is plain 0.
sourcefunction clampExpressionAmount(value: number): numberA number held between 0 and 1: what amounts and colour channels become.
sourceclass ExpressionCacheKeeps parsed and checked expressions per source text for one version of a checking environment. Bump
versionwhenever the properties, the variables or the components change; the cache then starts over.check( source: string, expected: ExpressionType, environment: ExpressionEnvironment, version: string | number ): CheckedExpressionsourceParses and checks
sourceagainst the field’s type, or returns the cached result for this version.get size(): numbersourceHow many expressions are cached for the current version.
clear(): voidsourceForgets everything.
sourceinterface CheckedExpressionA parsed and checked expression.
expressionis absent after a syntax error.readonly expression?: Expressionsourcereadonly 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.
sourcefunction expressionTypeNotes( expression: Expression, expected: ExpressionType, environment: ExpressionEnvironment ): readonly ExpressionTypeNote[]Checks the expression and returns, for every node and
matchpattern, 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.
sourceinterface ExpressionTypeNoteWhat the checker knew about one node or
matchpattern: the type it expected there, and the type it found.readonly span: ExpressionSpansourcereadonly expected?: ExpressionTypesourceThe type the place asks for, when it asks for one (a field, an argument, the other side of
==, amatchpattern).readonly type?: ExpressionTypesourceThe node’s type, when it could be told.
sourcefunction completeExpression( source: string, caret: number, context: ExpressionCompletionContext, explicit = false ): ExpressionCompletion | undefinedThe 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; insidevar("..."), the variables; in a word, properties, variables written asvar("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 followsmatchorexplicitis set (the user asked with Ctrl+Space).
sourceinterface ExpressionCompletionContextWhat completion offers and where it comes from.
readonly expected: ExpressionTypesourceThe type the whole expression must have: the field’s type, or
booleanfor a condition.readonly environment: ExpressionEnvironmentsourcereadonly properties: readonly string[]sourceThe component’s property names, in panel order.
readonly variables: readonly string[]sourceThe document’s variables as
"Collection/Name".
sourceinterface ExpressionCompletionThe completions at a caret, for the text between
fromandto.readonly from: numbersourcereadonly to: numbersourcereadonly expected?: ExpressionTypesourceThe type expected at the caret, when the checker could tell.
readonly items: readonly ExpressionCompletionItem[]source
sourceinterface ExpressionCompletionItemOne completion. Choosing it replaces
from–toof the completion withinsert.readonly kind: | "property" | "option" | "field" | "variable" | "function" | "keyword"sourcereadonly label: stringsourceWhat the list shows.
readonly insert: stringsourcereadonly caret?: numbersourceWhere the caret goes inside
insert(after the(of a function); the end when absent.readonly detail?: stringsourceThe type, or a function’s signature.
readonly fits: booleansourceTrue when its type is the type expected at the caret; these come first.
sourcefunction highlightExpression( source: string, isProperty: (name: string) => boolean ): ExpressionHighlight[]Splits source text into coloured stretches. It never fails: text the lexer would reject (an unknown character, an unfinished string) comes out as
invalidor as far as it goes. Names arepropertywhenisPropertysays so,functionbefore a(, andnameotherwise (letnames and unknown names).
sourceinterface ExpressionHighlightOne coloured stretch;
fromandtoare UTF-16 offsets,toexclusive.readonly from: numbersourcereadonly to: numbersourcereadonly kind: ExpressionHighlightKindsource
sourcetype ExpressionHighlightKind = | "keyword" | "property" | "function" | "name" | "option" | "field" | "number" | "string" | "color" | "comment" | "operator" | "punctuation" | "invalid"What a stretch of source text is, for its colour.
sourcefunction expressionValueSource(value: ExpressionValue): string | undefinedThe value as expression source text that evaluates back to it, such as
8px,.large,insets(4px, 8px)orsolid(#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.
sourcefunction formatExpressionNumber(value: number): stringUp to four decimals, without trailing zeros and never in exponent form.
sourcefunction formatExpressionString(value: string): stringA string literal with
",\and line breaks escaped as the lexer reads them.
sourcefunction readValueTable( source: string, environment: ExpressionEnvironment ): ExpressionValueTable | undefinedRecognises a value table: a
matchon one enum or boolean property, one pattern per arm, noelse, every arm a constant. Rows follow the property’s option order (“on” before “off”); an option thematchdoes not cover yet (added to the enum later) is a row withoutsource. Returns undefined for anything else, which the editor shows as an expression.
sourcefunction writeValueTable( table: Pick<ExpressionValueTable, "property" | "subject" | "rows"> ): stringWrites a value table as a
match, one arm per row in row order; rows withoutsourceare left out. It fits on one line when short enough, as inmatch size { .small => 4px, .large => 8px }, and otherwise puts each arm on its own line.
sourcefunction startValueTable( property: string, subject: ValueTableSubject, rowSource: string ): stringA new value table over the property with every row holding
rowSource, the field’s current value.
sourcefunction setValueTableRow( source: string, environment: ExpressionEnvironment, key: string, rowSource: string ): stringReplaces 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
matchdoes not cover yet is added by writing the table again. Throws whensourceis not a value table orkeyis not one of its rows: the caller only offers rows of a table it read.
sourcefunction valueTableSubject( property: string, environment: ExpressionEnvironment ): ValueTableSubject | undefinedThe subject for a property, or undefined when a table cannot vary by it (it is not an enum or boolean).
sourcefunction valueTableKeys( subject: ValueTableSubject ): readonly { readonly key: string; readonly label: string }[]The rows a value table over this subject has, without values.
sourcefunction isConstantExpression(expression: Expression): booleanTrue when the expression reads nothing that changes with property values: no property,
stored,if,letormatch.
sourceinterface ExpressionValueTableA value table read from source text: one row per option, in the property’s option order.
readonly property: stringsourceThe property the table varies by.
readonly subject: ValueTableSubjectsourcereadonly rows: readonly ValueTableRow[]source
sourceinterface ValueTableRowOne row of a value table.
readonly key: stringsourceThe option name, or
"true"/"false"for a boolean.readonly label: stringsourceWhat the row shows: the option name, or “on” / “off”.
readonly source?: stringsourceThe row’s constant as source text; absent when the
matchhas no arm for it yet.
sourcetype ValueTableSubject = | { readonly kind: "enum"; readonly type: ExpressionEnumType } | { readonly kind: "boolean" }What a value table varies by: an enum property’s options, or a boolean’s on and off.
sourcefunction readShowWhen( source: string, environment: ExpressionEnvironment ): ShowWhenCondition | undefinedRecognises a “Show when” condition:
showIcon,not showIcon,size == .large, orsize == .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.
sourcefunction writeShowWhen(condition: ShowWhenCondition): stringWrites a “Show when” condition. Throws for an enum condition without options, which would never show the layer.
sourcetype ShowWhenCondition = | { readonly kind: "boolean"; readonly property: string; readonly on: boolean; } | { readonly kind: "enum"; readonly property: string; readonly options: readonly string[]; }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.
since 0.24.0sourcetype 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[]; })A named, typed input on a component. Each instance chooses a value for it in
InstanceNode.propertyValues, or leaves the default.
since 0.24.0sourceinterface ComponentPropertyBase<Kind extends ComponentPropertyKind>Fields every component property has.
readonly id: ComponentPropertyIdsourceStable and never shown; instance values are keyed by it.
readonly kind: Kindsourcereadonly name: stringsourceUnique 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
since 0.24.0sourcetype ComponentPropertyKind = (typeof COMPONENT_PROPERTY_KINDS)[number]One of
COMPONENT_PROPERTY_KINDS.
since 0.24.0sourceconst COMPONENT_PROPERTY_KINDS: readonly [ "boolean", "number", "text", "enum", "color", "instance" ]The kinds of component property, as
ComponentPropertyDefinition.kind.
since 0.24.0sourcetype ComponentNumberUnit = (typeof COMPONENT_NUMBER_UNITS)[number]One of
COMPONENT_NUMBER_UNITS.
since 0.24.0sourceconst COMPONENT_NUMBER_UNITS: readonly ["none", "px", "%", "deg"]The unit of a number property:
"none"reads as anumberin expressions,"px"as alength,"%"as apercentand"deg"as anangle.
since 0.24.0sourceinterface ComponentPropertyOptionOne 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: stringsourcereadonly name: stringsource
since 0.24.0sourcetype ComponentPropertyId = Brand<string, "ComponentPropertyId">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.
since 0.24.0sourceconst componentPropertyId: (value: string) => ComponentPropertyIdBrands a string as a
ComponentPropertyId. Throws aTypeErrorfor an empty string.
since 0.24.0sourcetype ComponentPropertyValue = boolean | number | stringA 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.
since 0.24.0sourcetype ComponentPropertyValues = Readonly<Record<string, ComponentPropertyValue>>Property values keyed by
ComponentPropertyId. Missing means the default.
since 0.24.0sourcetype ComputableField = | (typeof COMPUTABLE_FIELDS)[number] | `propertyValues.${string}`A field that can hold an expression: a
COMPUTABLE_FIELDSname orpropertyValues.<property id>.
since 0.24.0sourceconst 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" ]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.
since 0.24.0sourcetype NamedComputableField = (typeof COMPUTABLE_FIELDS)[number]A computable field other than
propertyValues.<id>.
since 0.24.0sourcetype NodeExpressions = Readonly<Partial<Record<ComputableField, string>>>Expression source text keyed by computed field.
since 0.24.0sourcefunction computableFieldsOf(node: SceneNode): readonly NamedComputableField[]The named fields a node of this type can compute, in
COMPUTABLE_FIELDSorder. An instance node can also computepropertyValues.<property id>for any property of its component.
since 0.24.0sourcefunction isComputableField( node: SceneNode, field: string ): field is ComputableFieldTrue when
fieldcan hold an expression on this node. It checks the name only: apropertyValues.<id>field naming a property the nested component does not have is allowed and has no effect.
since 0.24.0sourcefunction propertyValueFieldId(field: string): string | undefinedThe property ID in a
propertyValues.<property id>field, or undefined for any other field.
since 0.24.0sourceconst PROPERTY_VALUE_FIELD_PREFIX: "propertyValues."The prefix of the computed field that holds a nested instance’s property value.
since 0.24.0sourceconst COMPUTABLE_FIELD_VARIABLE_PROPERTIES: Readonly< Record<NamedComputableField, readonly VariableProperty[]> >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.
since 0.24.0sourcefunction variablePropertiesOfField(field: string): readonly VariableProperty[]The variable binding properties that clash with a computed field; empty for
propertyValues.<id>.
since 0.24.0sourcefunction isExpressionIdentifier(name: string): booleanTrue when
namecan 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.
since 0.24.0sourcefunction findComponentProperty( component: ComponentDefinition, propertyId: string ): ComponentPropertyDefinition | undefinedThe property with this ID, or undefined.
since 0.24.0sourcefunction findComponentPropertyByName( component: ComponentDefinition, name: string ): ComponentPropertyDefinition | undefinedThe property with this name, or undefined.
since 0.24.0sourcefunction propertyValueFits( property: ComponentPropertyDefinition, value: ComponentPropertyValue ): booleanTrue when
valuefits 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.
since 0.24.0sourcefunction effectivePropertyValue( property: ComponentPropertyDefinition, value: ComponentPropertyValue | undefined ): ComponentPropertyValueThe value a property takes: the given value when it fits the property’s kind, else the default.
since 0.24.0sourceconst LENGTH_VARIABLE_PROPERTIES: readonly VariableProperty[]The binding properties that hold a length in pixels. A
numbervariable whose scopes are all in this list reads as alengthin expressions.
since 0.24.0sourcefunction variableExpressionType(variable: VariableDefinition): ExpressionTypeThe type a variable reads as in expressions. Colour, string and boolean variables read as
color,stringandboolean. Anumbervariable reads as alength(pixels) when it has scopes and every scope is a length (LENGTH_VARIABLE_PROPERTIES), and as a plainnumberotherwise: with no scopes, or any scope such as opacity, font weight or line height.
since 0.24.0sourcefunction variableExpressionName( collectionName: string, variableName: string ): stringThe name an expression reads a variable by:
"Collection/Name".
since 0.24.0sourcefunction variablesByExpressionName( theme: DocumentTheme ): ReadonlyMap<string, LocatedVariable>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.
since 0.24.0sourcefunction variableExpressionValue( located: LocatedVariable, modes?: VariableModes ): ExpressionValue | undefinedThe value of a variable in the mode its collection shows under
modes(seeresolveVariableValue; withoutmodes, the active mode), typed asvariableExpressionType.
since 0.24.0sourcefunction componentPropertyExpressionType( property: ComponentPropertyDefinition, enumName: string = property.name ): ExpressionTypeThe type a property reads as:
boolean,string(text),color,component(instance swap), an enum type namedenumName(the property name by default) with the option names, or for a number propertynumber,length,percentorangleby its unit.
since 0.24.0sourcefunction componentPropertyExpressionValue( property: ComponentPropertyDefinition, value?: ComponentPropertyValue ): ExpressionValueThe expression value of a property:
valuewhen it fits the property’s kind, else the default. An enum value reads as its option’s name.
since 0.24.0sourcefunction nestedPropertyEnumName( component: ComponentDefinition, property: ComponentPropertyDefinition ): stringThe 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.
since 0.24.0sourcefunction componentExpressionEnvironment( document: DesignDocument, component: ComponentDefinition | ComponentId, storedType?: ExpressionType ): ExpressionEnvironmentThe checking environment of a component’s expressions: its properties by name, the document’s variables by
"Collection/Name", andstoredTypeforstored(leave it out for a condition).
since 0.24.0sourcefunction componentValueEnvironment( document: DesignDocument, component: ComponentDefinition | ComponentId, propertyValues: ComponentPropertyValues = {}, stored?: ExpressionValue, modes?: VariableModes ): ExpressionValueEnvironmentThe evaluation environment matching
componentExpressionEnvironment: each property’s value frompropertyValues(an instance’s values; values that do not fit and missing ones read as the default), each variable in the mode its collection shows undermodes(the active mode by default), andstored.
since 0.24.0sourcetype ExpressionField = ComputableField | "condition"A computed field, or
"condition".
since 0.24.0sourceconst LAYOUT_DIRECTION_TYPE: ExpressionEnumTypeThe enum type of the
directionfield.
since 0.24.0sourceconst LAYOUT_ALIGN_TYPE: ExpressionEnumTypeThe enum type of the
alignfield.
since 0.24.0sourceconst LAYOUT_JUSTIFY_TYPE: ExpressionEnumTypeThe enum type of the
justifyfield;space_betweenand the others are the model’s"space-between"and so on.
since 0.24.0sourcefunction computableFieldType( document: DesignDocument, node: SceneNode, field: ExpressionField ): ExpressionType | undefinedThe type an expression in
fieldmust have:booleanfor a condition, the field’s type from the table in design 8.3, or forpropertyValues.<id>the type of that property of the nested instance’s component (with its enum named bynestedPropertyEnumName). Undefined for a field the node cannot compute or a property the nested component does not have.
since 0.24.0sourcefunction storedFieldValue( document: DesignDocument, node: SceneNode, field: ExpressionField ): ExpressionValue | undefinedThe base value of a computed field, which
storedreads: the plain value stored on the node, as an expression value. Absent optional values read as zero (for example a missingmaxWidthis 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 intext_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 atext_style(1.5 at 14 px is 21 px). A nested instance’spropertyValues.<id>reads the value stored on the node, else the nested component’s default. Undefined for a condition and for fieldscomputableFieldTypedoes not type.
since 0.24.0sourceinterface ExpressionFieldContextWhat checking one computed field or condition needs.
readonly expected: ExpressionTypesourcereadonly environment: ExpressionEnvironmentsource
since 0.24.0sourcefunction expressionFieldContext( document: DesignDocument, component: | ComponentDefinition | ComponentId, node: SceneNode, field: ExpressionField ): ExpressionFieldContext | undefinedThe expected type and checking environment of one field of a layer in
component, or undefined when the field has no type (seecomputableFieldType).
since 0.24.0sourcefunction expressionFieldValueEnvironment( document: DesignDocument, component: ComponentDefinition | ComponentId, node: SceneNode, field: ExpressionField, propertyValues: ComponentPropertyValues = {}, modes?: VariableModes ): ExpressionValueEnvironmentThe evaluation environment of one field of a layer in
componentfor an instance withpropertyValues:componentValueEnvironmentwithstoredset tostoredFieldValue.
since 0.24.0sourceinterface ComponentExpressionSiteOne stored expression: a computed field or a condition of a layer.
readonly nodeId: NodeIdsourcereadonly field: ExpressionFieldsourcereadonly source: stringsource
since 0.24.0sourcefunction componentLayerIds( document: DesignDocument, component: ComponentDefinition | ComponentId ): readonly NodeId[]The IDs of a component’s layers, root first, in tree order.
since 0.24.0sourcefunction componentExpressionSites( document: DesignDocument, component: | ComponentDefinition | ComponentId ): readonly ComponentExpressionSite[]Every expression and condition stored on a component’s layers, in tree order, conditions after the node’s computed fields.
since 0.24.0sourcefunction componentPropertyUsage( document: DesignDocument, componentId: ComponentId, propertyId: string, optionId?: string ): numberHow many of
component’s expressions and conditions name the property (or, withoptionId, that option of an enum property), plus thepropertyValues.<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”).
since 0.24.0sourceinterface ExpressionEditResultOperations for one edit, and how many expressions it touches.
readonly operations: readonly DocumentOperation[]sourceApply them together, in one
applyOperationscall.readonly count: numbersourceFor 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).
since 0.24.0sourcefunction renameComponentPropertyOperations( document: DesignDocument, componentId: ComponentId, propertyId: string, name: string ): ExpressionEditResultRenames a property and rewrites every expression in the component that reads it, in one edit. Throws a
ModelErrorwhen the name is not an identifier or another property has it.
since 0.24.0sourcefunction renameComponentPropertyOptionOperations( document: DesignDocument, componentId: ComponentId, propertyId: string, optionId: string, name: string ): ExpressionEditResultRenames 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.
since 0.24.0sourcefunction deleteComponentPropertyOperations( document: DesignDocument, componentId: ComponentId, propertyId: string ): ExpressionEditResultDeletes 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;countsays how many there are.
since 0.24.0sourcefunction deleteComponentPropertyOptionOperations( document: DesignDocument, componentId: ComponentId, propertyId: string, optionId: string ): ExpressionEditResultDeletes 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;
countsays how many. Throws aModelErrorfor the last option.
since 0.24.0sourcefunction changeComponentPropertyOperations( document: DesignDocument, componentId: ComponentId, next: ComponentPropertyDefinition ): ExpressionEditResultReplaces 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, andpropertyValues.<id>expressions of nested instances of the component, are kept and may stop checking;countis 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 amatchnon-exhaustive and a removed one leaves references unresolved). Throws aModelErrorwhen a number property’s minimum is above its maximum.
since 0.24.0sourcefunction renameVariableOperations( document: DesignDocument, variableId: VariableId, name: string ): ExpressionEditResultRenames a variable and rewrites every
var("Collection/Name")that reads it, in every component, in one edit.
since 0.24.0sourcefunction renameVariableCollectionOperations( document: DesignDocument, collectionId: VariableCollectionId, name: string ): ExpressionEditResultRenames a variable collection and rewrites every
var("Collection/Name")that reads one of its variables, in every component, in one edit.
since 0.24.0sourceconst 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; }The IDs the property sample uses, so tests can refer to them by name.
since 0.24.0sourcefunction propertyButtonSample(): DesignDocumentThe 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.
sourceconst CODE_FONT_FAMILY: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"
sourceconst CODE_FONT_SIZE: 32
sourceconst CODE_LINE_HEIGHT: 1.3
sourceconst BUILT_IN_CODE_STYLES: Readonly<Record<string, CodeStyle>>
sourceconst DARK_2026_CODE_STYLE: CodeStyle
sourceconst DARK_2026_CODE_STYLE_ID: CodeStyleId
sourceconst LIGHT_2026_CODE_STYLE: CodeStyle
sourceconst LIGHT_2026_CODE_STYLE_ID: CodeStyleId
sourcefunction getCodeLineNumberStart( lineNumbers: CodeNode["lineNumbers"] ): number | nullReturns
nullwhen 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.
sourcefunction resolveTextStyle( document: DesignDocument, styleId: string | undefined, overrides: TextStyleOverrides | undefined ): TextStyle
sourcefunction resolveTextFontFamily( document: DesignDocument, style: TextStyle ): stringResolves 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.
sourcefunction documentFontFamilyAlias(assetId: string): stringReturns the stable CSS family used for one document font asset identity.
sourcefunction textStyleToCanvasFont( style: TextStyle, document?: DesignDocument ): string
sourcefunction measureTextNode( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): TextMeasurement
sourceinterface TextMeasurementreadonly width: numbersourcereadonly height: numbersourcereadonly lineCount: numbersourcereadonly lines: readonly TextLineMetrics[]source
sourceinterface TextLineMetricsreadonly width: numbersourcereadonly ascent: numbersourcereadonly descent: numbersourcereadonly height: numbersource
sourcefunction getTextNodeBaselines( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): readonly number[]
sourcefunction getAutoSizedTextFrame( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): RectThe 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 fromtextAutoResizeAnchorin place on the page.
sourcefunction autoResizedTextFrame( node: Pick<TextualNode, "frame" | "rotation">, paragraphs: readonly TextParagraph[], size: { readonly width: number; readonly height: number } ): RectThe frame of an auto-sized text box after its content changed its size. The box turns about its center, so keeping
frame.xandframe.ywould move rotated text across the page as it grows; this keeps the anchor fromtextAutoResizeAnchorfixed in page space instead.
sourcefunction textAutoResizeAnchor( paragraphs: readonly TextParagraph[] ): { readonly x: number; readonly y: number }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.
sourcefunction resolveTextListLayout( document: DesignDocument, paragraph: TextParagraph, paragraphIndex: number ): TextListLayout | null
sourceinterface TextListLayoutreadonly text: stringsourcereadonly style: TextStylesourcereadonly gap: numbersourcereadonly nestingOffset: numbersourceAdditional body and marker inset contributed by the authored list level.
readonly left: numbersourcereadonly right: numbersourcereadonly firstLine: numbersourcereadonly markerWidth: numbersourcereadonly markerBoxWidth: numbersource
sourcefunction resolveListMarkerStyle( document: DesignDocument, paragraph: Pick<TextParagraph, "runs" | "list"> ): TextStyleThe 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
textLayoutKeyall resolve markers here, so they cannot disagree.
sourcefunction getTextNodeFontRequests( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): readonly TextFontRequest[]
sourceinterface TextFontRequestreadonly font: stringsourcereadonly text: stringsource
sourcefunction clearTextMeasurementCache(): void
Other exports
Exports not yet placed in a section above, in alphabetical order.
since 0.25.0sourcefunction applyComputedFields( document: DesignDocument, node: SceneNode, results: readonly { readonly field: ComputableField; readonly value: ExpressionValue; }[], onError?: (field: ComputableField, message: string) => void ): SceneNodenodewith 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.
since 0.26.0sourcefunction carryPropertyExpressions( from: ComponentDefinition | undefined, to: ComponentDefinition, expressions: NodeExpressions | undefined ): NodeExpressions | undefinedExpressions 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.
since 0.26.0sourcefunction carryPropertyValues( from: | ComponentDefinition | undefined, to: ComponentDefinition, values: | Readonly< Record< string, ComponentPropertyValue > > | undefined, mapComponent: ( id: ComponentId ) => ComponentId = (id) => id ): Record<string, ComponentPropertyValue> | undefinedProperty 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.
sourcefunction clearDrawnTextSizes(documentId?: DocumentId): voidForgets every drawn text size of one document, or of all documents when none is given.
since 0.25.0sourceinterface ComponentExpressionErrorA 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: NodeIdsourcereadonly field: ExpressionFieldsourcereadonly source: stringsourcereadonly errors: readonly ExpressionError[]source
since 0.25.0sourcefunction componentExpressionErrors( document: DesignDocument, componentId: ComponentId, options?: ResolveInstanceOptions ): readonly ComponentExpressionError[]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.
since 0.25.0sourceinterface ComputedComponentA 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>sourceLayers with at least one computed field, by stored ID; every other layer is as stored.
readonly leftOut: ReadonlySet<NodeId>sourceLayers whose condition is false. Their sublayers are left out with them and are not listed.
readonly errors: readonly ComponentExpressionError[]sourceComputed values these property values make invalid; each field shows its stored value.
sourcefunction computedTextFitOperations( document: DesignDocument, componentIds: readonly ComponentId[] ): readonly DocumentOperation[]The node replacements that bring the stored sizes of
componentIdsup 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).
sourcefunction confirmDrawnTextSize(document: DesignDocument, node: TextualNode): voidMarks 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.
sourcefunction documentWithVariantInstance( document: DesignDocument, instance: InstanceNode ): DesignDocumentdocumentwithinstanceadded to its nodes, so the resolver can read it.
since 0.26.0sourcefunction drawnNodes(document: DesignDocument): readonly SceneNode[]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
resolveInstancederives them, with the instance’s overrides, property values and nested instances applied and the virtual ID asid. Text or a font that exists only in an override is found here and nowhere indocument.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.
sourcefunction drawnTextSize( document: DesignDocument, node: TextualNode, paragraphs: readonly TextParagraph[] = node.paragraphs ): DrawnTextSize | undefinedThe size the renderer gave a text like this one in this document, when it has drawn one.
sourceinterface DrawnTextSizeThe size the renderer gave a text it drew.
readonly width: numbersourcereadonly height: numbersource
since 0.25.0sourcefunction filledPropertyValues( component: ComponentDefinition, values: ComponentPropertyValues = {} ): readonly ComponentPropertyValue[]The instance’s values for
component’s properties with defaults filled in, in the component’s property order: the key of a computed result.
sourcefunction forgetDrawnTextSizesOfEarlierFonts(documentId: DocumentId): numberForgets 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.
sourcefunction getDrawnTextSizesVersion(): numberChanges 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.
sourcetype GroupVariableBindings = Readonly< & Partial<Record<GroupVariableProperty, VariableId>> & { readonly width?: never; readonly height?: never } >A group’s variable bindings. A group’s size is always computed from its layers, so a group never binds
widthorheight.
sourcetype GroupVariableProperty = Exclude<VariableProperty, "width" | "height">The variable properties a group can bind: all but its size.
sourcefunction hasComputedTextContent(node: SceneNode | undefined): booleanWhether 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
charactersexpression).
since 0.23.0sourcefunction isLayerInDocument(document: DesignDocument, id: NodeId): booleanTrue when
idnames 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.
since 0.24.0sourcefunction isLayoutFieldInUse(node: SceneNode, field: string): booleanFalse for a layout field the frame’s current layout kind does not use:
gapon a grid or absolute frame,columnGapon a stack, any layout field on an absolute frame. True for every other field. A frame can keep an expression on any layout field (seecomputableFieldsOf), so it stays valid when the layout kind changes; the resolver applies, and the editor offers, only the fields in use.
sourceconst jsonCodecStats: { jsonPathAllocations: number; canonicalJsonCalls: number; }
sourceconst modelApplyStats: { documentDiffs: number; operationDeserializes: number; positionMapContainersVisited: number; }
sourcefunction multiplyTransforms( left: TransformMatrix, right: TransformMatrix ): TransformMatrixComposes two affine transforms. The result applies
rightfirst, thenleft. Matrices use the[a, b, c, d, e, f]layout, wherex' = a*x + c*y + eandy' = b*x + d*y + f. In 3×3 form this is the matrix product , so .
sourcefunction nodeIdsRenamedWithSuffix(nodeIds: readonly string[]): string[]The stored node IDs in
nodeIdswhose 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.
sourcefunction nodeLocalTransform(node: SceneNode): TransformMatrixThe 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.
sourcefunction nodePageTransform( document: DesignDocument, id: NodeId, parents: ReadonlyMap< NodeId, NodeId > = buildParentMap(document.nodes), transforms = new Map<NodeId, TransformMatrix>() ): TransformMatrixThe 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(frombuildParentMap) and atransformscache when calling this for many layers of one document.
since 0.23.0sourceconst OVERRIDE_FIELD_PROPERTIES: Readonly< Partial<Record<OverrideField, readonly VariableProperty[]>> >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
unsupportedon a virtual ID.
since 0.23.0sourcefunction overrideFieldForProperty(property: string): OverrideField | undefinedThe override field a variable property belongs to, or
undefinedwhen that property binds a field an instance cannot override.
sourcefunction recordDrawnTextSize( document: DesignDocument, node: TextualNode, size: DrawnTextSize ): booleanRemembers 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).nodeis 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 casedrawnTextSizesVersionchanges and sizes derived before it are out of date.
sourcefunction resetJsonCodecStats(): void
sourcefunction resetModelApplyStats(): void
sourcefunction resetTextMeasurementStats(): void
since 0.26.0sourcefunction resolvedShownComponents( document: DesignDocument, instanceId: NodeId ): (path: readonly NodeId[]) => ComponentId | undefinedA
shownComponentAtfor the stored instanceinstanceId: 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.
sourcefunction setDrawnTextFontSet(documentId: DocumentId, name: string): booleanTells the memory which font files the renderer draws a document with.
namestands 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 byforgetDrawnTextSizesOfEarlierFonts. Returns whether the name changed.
sourcefunction splitCssFontFamilies(fontFamily: string): readonly string[]
sourcefunction textLayoutKey( document: DesignDocument, node: Pick< TextualNode, "autoResize" | "wrap" | "frame" | "paragraphs" >, paragraphs: readonly TextParagraph[] = node.paragraphs ): stringNames 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 bysetDrawnTextFontSet. 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.
sourceconst textMeasurementStats: { layoutComputations: number; fontRequestTextRebuilds: number; resolvedStyleCopies: number; }
sourceconst VARIANT_VIEW_CAP: 64The most views “Show all variants” draws; above it the user picks which properties to vary.
sourceinterface VariantCombinationOne “Show all variants” view: its property values and label.
readonly values: ComponentPropertyValuessourceThe values that differ from the defaults; the defaults view has none.
readonly label: stringsourceThe values that differ from the defaults (“size: large, disabled”), or “Component” for the defaults.
readonly key: stringsourceStable text for the combination (“” for the defaults), for editor-only IDs.
since 0.27.0sourcefunction variantCombinationOf( component: ComponentDefinition, values: ComponentPropertyValues ): VariantCombinationThe combination that
valuesname: the enum and boolean values that differ from the defaults (a value no option has counts as the default), with the label and keyvariantCombinationsgives the same view. Values of other kinds of property are left out; a view keeps their defaults.
sourcefunction variantCombinations( component: ComponentDefinition, vary?: readonly ComponentPropertyId[] ): readonly VariantCombination[]The combinations of the varied properties’ values (every variant property when
varyis 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 thanVARIANT_VIEW_CAPviews.
sourcefunction variantInstanceNode( document: DesignDocument, componentId: ComponentId, id: NodeId, name: string, values: ComponentPropertyValues ): InstanceNode | undefinedAn editor-only instance of
componentIdwithvalues, 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.
sourcefunction variantLabel( component: ComponentDefinition, values: ComponentPropertyValues ): stringThe 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.
sourcefunction variantProperties( component: ComponentDefinition ): readonly VariantProperty[]The component’s enum and boolean properties, in the component’s order.
sourcetype VariantProperty = Extract< ComponentPropertyDefinition, { readonly kind: "enum" | "boolean" } >A property “Show all variants” can vary: an enum or a boolean.
sourcefunction variantPropertyValues( property: VariantProperty ): readonly ComponentPropertyValue[]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.
sourcefunction variantViewCount( component: ComponentDefinition, vary?: readonly ComponentPropertyId[] ): numberHow many views varying these properties (every variant property when omitted) gives.
sourcefunction variantViewSize( document: DesignDocument, componentId: ComponentId, values: ComponentPropertyValues, options?: ResolveInstanceOptions ): { readonly width: number; readonly height: number } | undefinedThe natural size of the component with
values: the resolved root of an instance with those values (hugging axes take their content’s size).
since 0.26.0sourcefunction withoutClashingBindings<T extends SceneNode>(node: T): TThe 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.
sourcetype EditorInput = | EditorPointerInput | EditorKeyboardInput | EditorCompositionInput | EditorTextInput | EditorClipboardInputEvery input event the editor accepts. Check one with
assertEditorInput.
sourceinterface EditorPointerInputA pointer event normalized from the platform. Shared by mouse, pen and touch.
readonly kind: "pointer"sourcereadonly phase: "down" | "move" | "up" | "cancel" | "lost-capture"sourcecancelandlost-captureend the pointer sequence without anup.updateBodyInteractiontreats both as a cancel and commits nothing.readonly pointerId: numbersourceIdentifies one pointer across its events. A non-negative integer.
readonly pointerType: EditorPointerTypesourcereadonly position: PointsourcePosition in CSS pixels relative to the interactive surface.
The button that changed in this event, numbered as in the DOM
buttonfield (0 is the primary button).Bit mask of the buttons held after this event, as in the DOM
buttonsfield.readonly pressure: numbersourcePressure from 0 to 1.
readonly modifiers: EditorModifierssource
sourcetype EditorPointerType = "mouse" | "pen" | "touch"The device that produced a pointer event, as in the DOM
pointerType.
sourceinterface EditorModifiersModifier keys held during a pointer or keyboard event.
metais the Command key on macOS.readonly alt: booleansourcereadonly control: booleansourcereadonly meta: booleansourcereadonly shift: booleansource
sourceinterface EditorKeyboardInputA key press or release.
readonly kind: "keyboard"sourcereadonly phase: "down" | "up"sourcereadonly key: stringsourceThe produced key value, as in the DOM
KeyboardEvent.key(for example"a"or"ArrowLeft").readonly code: stringsourceThe physical key, as in the DOM
KeyboardEvent.code(for example"KeyA").readonly repeat: booleansourceTrue when the key is held down and the platform repeats it.
readonly composing: booleansourceTrue while an IME composition is in progress.
readonly modifiers: EditorModifierssource
sourceinterface EditorCompositionInputAn IME composition event.
textis the whole composition text so far, not a difference from the last update.readonly kind: "composition"sourcereadonly phase: "start" | "update" | "end" | "cancel"sourcereadonly text: stringsourcereadonly selectionStart: numbersourceUTF-16 offsets used by browser composition APIs.
readonly selectionEnd: numbersource
sourceinterface EditorTextInputText typed or deleted, modeled on the DOM
beforeinputevent.readonly kind: "text"sourcereadonly inputType: stringsourceThe DOM input type, such as
"insertText"or"deleteContentBackward".readonly data: string | nullsourceThe inserted text, or
nullfor input types that carry none, such as deletions.
sourceinterface EditorClipboardInputA copy, cut or paste. For paste,
itemsholds the clipboard contents.readonly kind: "clipboard"sourcereadonly action: "copy" | "cut" | "paste"sourcereadonly items: readonly EditorClipboardItem[]source
sourceinterface EditorClipboardItemOne clipboard format and its bytes. An item list holds each MIME type at most once.
readonly mimeType: stringsourcereadonly data: Uint8Arraysource
sourcefunction assertEditorInput(input: unknown): asserts input is EditorInputValidates input at the TypeScript-to-renderer boundary. Checks the shape of each
EditorInputkind: 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. ThrowsTypeErrororRangeErroron the first problem.
Rich text travels through the clipboard as UTF-8 JSON under its own MIME type, next to a text/plain fallback.
sourceconst EDITOR_RICH_TEXT_MIME_TYPE: "application/x-taco-slides-rich-text+json"The clipboard MIME type for
EditorRichTextClipboardPayload, stored as UTF-8 JSON.
sourceinterface EditorRichTextClipboardPayloadCanonical rich-text clipboard payload; text/plain remains the required fallback.
readonly version: 1sourcereadonly paragraphs: readonly TextParagraph[]source
sourcefunction encodeEditorRichTextClipboardPayload( payload: EditorRichTextClipboardPayload ): Uint8ArrayEncodes a rich-text clipboard payload as UTF-8 JSON bytes. Validates it first and throws
TypeErrorthe same way asdecodeEditorRichTextClipboardPayload.
sourcefunction decodeEditorRichTextClipboardPayload( bytes: Uint8Array ): EditorRichTextClipboardPayloadDecodes the bytes of an
EDITOR_RICH_TEXT_MIME_TYPEclipboard item. ThrowsTypeErrorwhen the bytes are not UTF-8 JSON, when the object has properties other thanversionandparagraphs, whenversionis not 1, or when the paragraphs are not valid text node paragraphs.
sourceinterface PointA 2D point. The coordinate space depends on where it is used.
readonly x: numbersourcereadonly y: numbersource
sourceinterface SizeA width and height. The units depend on where it is used.
readonly width: numbersourcereadonly 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.
sourceinterface ViewportStateHow the document is placed on the interactive surface. Build one with
createViewportState; convert points withdocumentToScreenPointand the other viewport functions.readonly screenSize: SizesourceSize of the interactive surface in CSS pixels.
readonly documentToScreen: TransformMatrixsourceAffine transform from document units to CSS pixels relative to the surface. It must be finite and invertible.
readonly devicePixelRatio: numbersourceDevice pixels per CSS pixel. Must be positive.
sourceinterface ViewportInputPan and zoom settings for
createViewportState.readonly screenSize: { readonly width: number; readonly height: number }sourceSize of the interactive surface in CSS pixels.
readonly origin: PointsourceWhere the document origin lands on the surface, in CSS pixels.
readonly zoom: numbersourceCSS pixels per document unit. Must be positive.
readonly devicePixelRatio: numbersourceDevice pixels per CSS pixel. Must be positive.
sourcefunction createViewportState(input: ViewportInput): ViewportStateBuilds a
ViewportStatewith no rotation: document pointpmaps toorigin + p * zoomin CSS pixels, sodocumentToScreenis and a screen point maps back with . ThrowsTypeErrorfor non-finite values andRangeErrorfor a negative size or a zoom or device pixel ratio that is not positive.
sourcefunction assertViewportState(viewport: ViewportState): voidChecks that a
ViewportStateis usable: finite, non-negative screen size, a finite and invertibledocumentToScreentransform, and a positive device pixel ratio. ThrowsTypeErrororRangeError.
sourceconst documentToScreenPoint: (viewport: ViewportState, point: Point) => PointConverts a point from document units to CSS pixels on the surface.
sourceconst screenToDocumentPoint: (viewport: ViewportState, point: Point) => PointConverts a point from CSS pixels on the surface to document units. Throws
RangeErrorwhen the viewport transform cannot be inverted.
sourceconst documentToScreenRect: (viewport: ViewportState, rect: Rect) => RectConverts a document rectangle to the CSS-pixel bounding box of its transformed corners (see
transformRect).
sourceconst screenToDocumentRect: (viewport: ViewportState, rect: Rect) => RectConverts a CSS-pixel rectangle to the document-unit bounding box of its transformed corners (see
transformRect).
sourceconst screenToDevicePoint: (viewport: ViewportState, point: Point) => PointConverts CSS pixels to device pixels by multiplying by the device pixel ratio.
sourceconst deviceToScreenPoint: (viewport: ViewportState, point: Point) => PointConverts device pixels to CSS pixels by dividing by the device pixel ratio.
sourceconst documentToDevicePoint: (viewport: ViewportState, point: Point) => PointConverts a point from document units to device pixels.
sourceconst deviceToDocumentPoint: (viewport: ViewportState, point: Point) => PointConverts a point from device pixels to document units.
sourcefunction transformPoint(transform: TransformMatrix, point: Point): PointApplies an affine transform to a point.
sourcefunction transformRect(transform: TransformMatrix, rect: Rect): RectTransforms the four corners of a rectangle and returns their axis-aligned bounding box. Under rotation the result is larger than the rectangle.
sourcefunction multiplyTransforms( left: TransformMatrix, right: TransformMatrix ): TransformMatrixComposes two affine transforms. The result applies
rightfirst, thenleft. Matrices use the[a, b, c, d, e, f]layout, wherex' = a*x + c*y + eandy' = b*x + d*y + f. In 3×3 form this is the matrix product , so .The arithmetic is the model’s (
multiplyTransformsthere), which the layer transforms shared by the page compiler and the editor are built on.
sourcefunction invertTransform(transform: TransformMatrix): TransformMatrixInverts an affine transform. Throws
RangeErrorwhen the determinant is not finite or is too close to zero. With :
sourcetype ZoomMode = | { readonly kind: "fit" } | { readonly kind: "ratio"; readonly ratio: number }The zoom the user asked for: fit the content in the stage, or a fixed ratio.
sourceinterface ZoomMetricsThe resolved zoom. Build it with
resolveZoomMetrics.readonly actualRatio: numbersourceThe ratio the canvas draws at: CSS pixels per document unit.
readonly minimumRatio: numbersourceThe lowest allowed ratio. Currently always
MIN_ZOOM_RATIO.readonly fitRatio: numbersourceThe ratio that fits the content in the stage.
sourcefunction resolveZoomMetrics(mode: ZoomMode, fitRatio: number): ZoomMetricsThe 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.
fitRatiois the ratio that fits the page content in the stage.
sourceconst initialZoomMetrics: ZoomMetricsStarting zoom metrics: 100%, with a fit ratio of 1.
sourcefunction clampZoomRatio(ratio: number, minimumRatio = MIN_ZOOM_RATIO): numberClamps a ratio to
minimumRatioandMAX_ZOOM_RATIO. A non-finite ratio becomes 1, orminimumRatioif that is larger.
sourcefunction stepZoomRatio(ratio: number, direction: 1 | -1): numberThe next zoom step: times 1.25 when zooming in (
direction1) or times 0.8 when zooming out, clamped and rounded to two decimal places:
sourcefunction formatZoomPercent(ratio: number): stringFormats a ratio as a whole percentage, such as
"125%".
sourcefunction sameZoomMetrics(left: ZoomMetrics, right: ZoomMetrics): booleanTrue when all three fields of two
ZoomMetricsare equal.
sourceconst MIN_ZOOM_RATIO: 0.02The canvas is unbounded, so zoom has fixed limits instead of page-relative ones.
sourceconst MAX_ZOOM_RATIO: 64The 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.
sourceinterface EditorDrivergetCapabilities(): Promise<EditorDriverCapabilities>sourcereset( config: { readonly document: DesignDocument; readonly viewport: ViewportState; } ): Promise<void>sourcesetDocument(document: DesignDocument): Promise<void>sourcesetViewport(viewport: ViewportState): Promise<void>sourcesetTool(tool: EditorTool): Promise<void>sourcesendInput(input: EditorInput): Promise<void>sourceflush(): Promise<EditorSnapshot>sourcegetSnapshot(): Promise<EditorSnapshot>sourcehitTest(screenPoint: Point): Promise<readonly EditorHit[]>sourcegetNodeGeometry(nodeId: NodeId): Promise<NodeGeometry | null>sourcegetTextGeometry(nodeId: NodeId): Promise<TextGeometry | null>sourcehitTestText(query: TextHitQuery): Promise<TextHitResult | null>sourcegetVectorGeometry(nodeId: NodeId): Promise<VectorGeometry | null>sourcehitTestVector(query: VectorHitQuery): Promise<VectorHitTestResult | null>sourcegetAccessibilitySnapshot(): Promise<AccessibilitySnapshotV1 | null>sourceperformAccessibilityAction( request: AccessibilityActionRequestV1 ): Promise<boolean>sourcecaptureFrame(): Promise<RenderedEditorFrame>sourcedispose(): Promise<void>source
sourceinterface EditorDriverCapabilitiesreadonly pointerTypes: readonly EditorPointerType[]sourcereadonly keyboardRepeat: booleansourcereadonly composition: booleansourcereadonly clipboard: readonly EditorClipboardInput["action"][]sourcereadonly textEditing: booleansourceExposes selection and composition state while text editing is active.
readonly textGeometry: booleansourcereadonly textHitTesting: booleansourcereadonly vectorGeometry: booleansourcereadonly vectorHitTesting: booleansourcereadonly vectorBooleanOperations: booleansourcereadonly accessibility: booleansourcereadonly frameCapture: booleansourcereadonly variableDevicePixelRatio: booleansourceWhether reset can recreate the surface at a different device-pixel ratio.
sourcetype 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"
sourceclass UnsupportedEditorCapabilityError extends ErrorA stable failure for input or observations a driver explicitly does not provide.
readonly code: "unsupported-editor-capability"sourcenew (readonly capability: EditorDriverCapability, message: string)source
sourceinterface EditorSnapshotreadonly document: DesignDocumentsourcereadonly activePageId: PageId | nullsourcereadonly selection: EditorSelectionStatesourcereadonly hoveredNodeId: NodeId | nullsourcereadonly textEditing: ActiveTextEditingState | nullsourceCanonical text selection plus any uncommitted IME replacement.
readonly editingTextNodeId: NodeId | nullsourceCompatibility identity; it must agree with
textEditing.nodeIdwhen active.readonly vectorEditing: VectorEditingSnapshot | nullsourceExisting-path component editing and its uncommitted native overlay preview.
readonly editingCodeNodeId: NodeId | nullsourcereadonly activeTool: EditorToolsourcereadonly viewport: ViewportStatesourcereadonly gesture: EditorGestureState | nullsourcereadonly previews: readonly EditorPreviewGeometry[]sourcereadonly lastHit: EditorHit | nullsourcereadonly history: EditorHistoryStatesourcereadonly accessibility: AccessibilitySnapshotV1 | nullsourceExact versioned semantic state exposed by the active editor surface.
readonly revisions: EditorRevisionssource
sourceinterface EditorSelectionStatereadonly pageIds: readonly PageId[]sourcereadonly nodeIds: readonly NodeId[]sourcereadonly primaryNodeId: NodeId | nullsource
sourceinterface EditorHistoryStatereadonly canUndo: booleansourcereadonly canRedo: booleansourcereadonly headCommitId: string | nullsourcereadonly commits: readonly CommitRecord[]source
sourceinterface EditorRevisionsreadonly document: numbersourcereadonly surface: numbersourcereadonly frame: numbersourcereadonly accessibility: numbersource
sourcetype EditorTool = | "move" | "hand" | "scale" | "frame" | "rectangle" | "line" | "arrow" | "connector" | "ellipse" | "polygon" | "star" | "image" | "pen" | "pencil" | "text" | "code" | "text-path" | "comment"
sourcetype EditorGestureKind = | "draw" | "marquee" | "move" | "resize" | "connector-endpoint" | "rotate" | "scale" | "pan" | "text" | "vector"
sourcetype 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" })
sourcetype EditorResizeHandle = | "north-west" | "north" | "north-east" | "east" | "south-east" | "south" | "south-west" | "west"
sourcetype EditorScaleHandle = | "north-west" | "north-east" | "south-east" | "south-west"
sourcetype 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"; }
sourcetype 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; }
sourceinterface RenderedEditorFramereadonly mimeType: "image/png"sourcereadonly width: numbersourcereadonly height: numbersourcereadonly density: numbersourcereadonly 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.
sourceinterface InteractiveRenderSessionloadDocument(load: RenderDocumentLoad): Promise<void>sourceReplaces the canonical document and seeds exact document-store revision continuity.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoNative, through
InteractiveRetainedBackend.apply:taco_render_session_applyapplyDocumentUpdate(update: DocumentSceneUpdate): Promise<void>sourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoNative, through
InteractiveRetainedBackend.apply:taco_render_session_applysetActivePage(pageId: PageId | null): Promise<void>sourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoNative, through
InteractiveRetainedBackend.apply:taco_render_session_applysetViewport(viewport: ViewportState): Promise<void>sourcehitTest(screenPoint: Point): readonly EditorHit[]sourceNative, through
InteractiveRetainedBackend.hitTest:taco_render_session_hit_testgetNodeGeometry(nodeId: NodeId): NodeGeometry | nullsourceNative, through
InteractiveRetainedBackend.getNodeGeometry:taco_render_session_get_node_geometrygetSceneGeometrySnapshot(): SceneGeometrySnapshotsourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoNative, through
InteractiveRetainedBackend.getGeometrySnapshot:taco_render_session_get_geometry_snapshotgetTextGeometry(nodeId: NodeId): TextGeometry | nullsourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infohitTestText(query: TextHitQuery): TextHitResult | nullsourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infogetVectorGeometry(nodeId: NodeId): VectorGeometry | nullsourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infohitTestVector(query: VectorHitQuery): VectorHitTestResult | nullsourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infobooleanVectorPaths(request: VectorBooleanPathRequest): VectorBooleanPathResultsourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infosetOverlays(overlays: EditorOverlayState): Promise<void>sourceNative, 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_overlayrenderFrame(): Promise<RenderFrameReport>sourceNative, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infogetAccessibilitySnapshot(): Promise<AccessibilityNodeSnapshot | null>sourcedispose(): Promise<void>source
sourceinterface RenderDocumentLoadOne atomic canonical document load and its exact resolved resource input.
readonly document: DesignDocumentsourcereadonly rendererDocument?: DesignDocumentsourceRenderer-only projection paired atomically with the canonical document.
readonly rendererNodeOwners?: RendererNodeOwnerssourceExact renderer-node ownership paired atomically with the renderer projection.
readonly rendererQueryTargets?: RendererQueryTargetssourceCanonical public query IDs paired atomically with renderer-only query targets.
readonly commitId: CommitIdsourcereadonly resourceSnapshot: RenderResourceSnapshotsource
sourceinterface DocumentSceneUpdatereadonly reason: TransitionReasonsourcereadonly previousCommitId: CommitIdsourcereadonly currentCommitId: CommitIdsourcereadonly previousDocument: DesignDocumentsourcereadonly document: DesignDocumentsourcereadonly previousRendererDocument?: DesignDocumentsourceExact renderer-only projection committed with
previousDocument.The coordinator continues to own
previousDocumentas 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?: RendererNodeOwnerssourceExact 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?: RendererQueryTargetssourceExact canonical-to-renderer query targets committed with the previous projection.
readonly rendererDocument?: DesignDocumentsourceRenderer-only projection paired atomically with
document.readonly rendererNodeOwners?: RendererNodeOwnerssourceExact renderer-node ownership map paired atomically with
rendererDocument.readonly rendererQueryTargets?: RendererQueryTargetssourceCanonical public query IDs mapped to their renderer-only text/vector nodes.
readonly operations: readonly DocumentOperation[]sourcereadonly changes: ChangeSetsourcereadonly rendererChanges?: ChangeSetsourceExact change set between the previous and current renderer projections. Required whenever either side uses a renderer document or ownership map.
readonly resourceSnapshot: RenderResourceSnapshotsourceComplete renderer resources for
document, resolved from the same immutable revision.
sourceinterface RenderResourcereadonly assetId: stringsourcereadonly mimeType: stringsourcereadonly bytes: Uint8Arraysourcereadonly slice?: RenderFontSlicesourceSet 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.
sourceinterface RenderFontSliceOne file of a font asset split by Unicode coverage.
readonly id: stringsourceUnique within the asset, made of letters, digits, “.”, “_” and “-“.
readonly unicodeRanges: readonly (readonly [number, number])[]sourceSorted, non-overlapping inclusive code point ranges the file covers.
sourceinterface RenderResourceSnapshotComplete, immutable renderer resource input for one canonical document revision.
readonly resources: readonly RenderResource[]source
sourcetype RendererNodeOwners = Readonly<Record<string, NodeId>>Immutable, exhaustive renderer-node to canonical-node ownership.
sourceinterface RendererQueryTargetsOptional 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>>sourcereadonly vector: Readonly<Record<string, NodeId>>source
sourceinterface RenderFrameReportreadonly changedRegions: readonly Rect[]sourceRepainted regions in CSS pixels relative to the render target.
readonly sceneUpdateMs: numbersourcereadonly layoutMs: numbersourcereadonly paintMs: numbersourcereadonly submitMs: numbersourcereadonly paint?: RenderFramePaintStatisticssourcePresent when the backend reports paint statistics.
sourceinterface RenderFramePaintStatisticsWhat one frame painted, for performance instrumentation.
readonly fullPaint: booleansourceThe whole target repainted instead of only the damaged region.
readonly nodesPainted: numbersourcereadonly nodesConsidered: numbersourceNodes examined for this frame, including ones culled before painting.
readonly targetWidth: numbersourceRender target size in device pixels.
readonly targetHeight: numbersourcereadonly retainedPayloadBytes: numbersourceBytes of retained scene payload held by the renderer.
readonly decodedFontEntries: numbersourcereadonly 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.
sourceinterface EditorOverlayStatereadonly selectedNodeIds: readonly NodeId[]sourcereadonly hoveredNodeId: NodeId | nullsourcereadonly gesture: EditorGestureState | nullsourcereadonly previews: readonly EditorPreviewGeometry[]sourcereadonly decorations: readonly EditorNodeDecoration[]sourcereadonly nodePreviews: readonly EditorNodePreview[]sourcereadonly textEditing: | (ActiveTextEditingState & { readonly caretVisible: boolean }) | nullsourceSelection, caret, and IME state painted by the shared renderer.
readonly vectorEditing: VectorEditingSnapshot | nullsourceExisting-path component selection and full node-local preview network.
readonly hitRegions: readonly EditorOverlayHitRegion[]sourceTopmost-first hit regions. Overlay hits always precede retained scene-body hits.
readonly selectionChrome?: EditorSelectionChrome | nullsourceSelection 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 | nullsourceSnap 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;
nullclears it.
sourceinterface EditorCanvasOverlayreadonly anchorNodeId: NodeId | nullsourceThe node whose local coordinates the overlay uses;
nullfor scene coordinates.readonly devicePixelRatio: numbersourcereadonly grid: | { readonly originX: number; readonly originY: number; readonly step: number; readonly rgba: number; } | nullsourceA line after every
stepunits from the origin, across the canvas.readonly lines: readonly EditorOverlayLine[]sourcereadonly marks: readonly EditorOverlayMark[]sourcereadonly titles?: readonly EditorOverlayTitle[]sourceNames drawn above top-level frames; absent means none.
readonly rulers?: EditorOverlayRulerssourceThe rulers along the top and left edges; absent means none.
readonly boxes?: readonly EditorOverlayBox[]sourceFilled, hatched or outlined boxes (auto layout gaps and padding); absent means none.
readonly handles?: readonly EditorOverlayHandle[]sourceTicks with a border (auto layout spacing handles); absent means none.
readonly badges?: readonly EditorOverlayBadge[]sourceValue pills (the value of a spacing drag, at most 16); absent means none.
sourceinterface EditorOverlayLinereadonly orientation: "vertical" | "horizontal"sourcereadonly side: EditorOverlayLineSidesourcereadonly position: numbersourcex of a vertical line, y of a horizontal one.
readonly start: number | nullsourceThe line’s extent along its length;
nullcrosses the whole canvas.readonly end: number | nullsourcereadonly rgba: numbersource0xRRGGBBAA.
readonly dash?: { readonly dashCssPx: number; readonly gapCssPx: number }sourcereadonly scene?: booleansourceScene coordinates instead of coordinates local to the overlay’s anchor.
readonly anchorNodeId?: NodeIdsourceA 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?: booleansourceRun along the anchor node’s own box (0 to its width or height); start and end are ignored.
sourcetype EditorOverlayLineSide = "after" | "before" | "centered"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.
sourceinterface EditorOverlayMarkAn anti-aliased x centered on the pixels of two crossing lines.
readonly x: numbersourcereadonly y: numbersourcereadonly xSide: EditorOverlayLineSidesourcereadonly ySide: EditorOverlayLineSidesourcereadonly halfSizeCssPx: numbersourcereadonly rgba: numbersourcereadonly scene?: booleansource
sourceinterface EditorOverlayHitRegionreadonly hit: EditorHitsourcereadonly 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.
sourceinterface EditorOverlayBoxA 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?: NodeIdsourcereadonly x: numbersourcereadonly y: numbersourcereadonly width: numbersourcereadonly height: numbersourcereadonly fillRgba?: numbersource0xRRGGBBAA.
readonly hatchRgba?: numbersourcereadonly outlineRgba?: numbersource
sourceinterface EditorOverlayHandleA round-ended tick with a border, centred on a point, along the anchor’s local x or y axis.
readonly anchorNodeId?: NodeIdsourcereadonly x: numbersourcereadonly y: numbersourcereadonly axis: "x" | "y"sourcereadonly lengthCssPx: numbersourcereadonly thicknessCssPx: numbersourcereadonly rgba: numbersource0xRRGGBBAA.
readonly borderRgba: numbersource
sourceinterface EditorOverlayBadgeA rounded pill with one line of text, its top-left corner a CSS pixel offset from a point.
readonly anchorNodeId?: NodeIdsourcereadonly x: numbersourcereadonly y: numbersourcereadonly offsetXCssPx: numbersourcereadonly offsetYCssPx: numbersourcereadonly fontSizeCssPx: numbersourcereadonly text: stringsourcereadonly backgroundRgba: numbersource0xRRGGBBAA.
readonly textRgba: numbersource
sourceinterface EditorOverlayRulersThe 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: numbersourcereadonly fontSizeCssPx: numbersourcereadonly minimumMajorCssPx: numbersourcereadonly backgroundRgba: numbersource0xRRGGBBAA colours.
readonly borderRgba: numbersourcereadonly inkRgba: numbersourcereadonly selectionFillRgba: numbersourcereadonly selectionInkRgba: numbersourcereadonly selectionX: readonly { readonly start: number; readonly end: number }[]sourceThe selection’s extents: sorted, apart; a zero-length one marks one value.
readonly selectionY: readonly { readonly start: number; readonly end: number }[]sourcereadonly labels: readonly { readonly axis: "x" | "y"; readonly position: number; readonly backgroundRgba: number; readonly textRgba: number; }[]sourceGuide positions drawn on their ruler as tags (at most 16).
sourceinterface EditorOverlayTitleThe 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: NodeIdsourcereadonly text: stringsourcereadonly rgba: numbersource0xRRGGBBAA.
readonly fontSizeCssPx: numbersourcereadonly gapCssPx: numbersourceSpace between the title’s line box and the frame’s top edge.
sourceinterface EditorSelectionChromereadonly items: readonly EditorSelectionChromeItem[]sourcereadonly strokeRgba: numbersourceDefault colours of items that give none, 0xRRGGBBAA.
readonly handleFillRgba: numbersourcereadonly devicePixelRatio: numbersource
sourceinterface EditorSelectionChromeItemOne outline or handle, resolved against its node when the frame is painted.
readonly nodeId: NodeId | nullsourceNull places the rectangle in scene coordinates instead of node-local ones.
readonly kind: "outline" | "square-handle" | "round-handle"sourcereadonly hover?: booleansourcereadonly dashed?: booleansourcereadonly x: numbersourceAn 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: numbersourcereadonly width?: numbersourcereadonly height?: numbersourcereadonly angle?: numbersourceA handle’s turn on screen in radians, clockwise; 0 when omitted.
readonly strokeRgba?: numbersource0xRRGGBBAA overrides of the chrome’s default colours.
readonly fillRgba?: numbersourcereadonly offsetX?: numbersourceScene-space offset for a preview the retained scene does not hold.
readonly offsetY?: numbersourcereadonly sceneTransform?: TransformMatrixsourceLocal-to-scene matrix of a scene-anchored item; identity when omitted.
readonly nodeTransform?: TransformMatrixsourceItem-to-node matrix of a node-anchored item; identity when omitted.
sourceinterface EditorNodeDecorationreadonly nodeId: NodeIdsourcereadonly role: "hover" | "selection"sourcereadonly primary: booleansourcereadonly handles: "none" | "resize" | "rotate" | "scale" | "vector"source
sourcetype 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; }
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.
sourceinterface SceneGeometrySnapshotreadonly generation: bigintsourceRetained-scene generation; it is intentionally not an editor history ID.
readonly sceneBounds: Rectsourcereadonly painterOrder: readonly NodeId[]sourceBack-to-front canonical paint order.
readonly nodes: readonly NodeGeometry[]sourcereadonly byId: ReadonlyMap<NodeId, NodeGeometry>source
sourceinterface NodeGeometryreadonly nodeId: NodeIdsourcereadonly localBounds: Rectsourcereadonly documentBounds: Rectsourcereadonly visibleDocumentBounds: Rect | nullsourceBounds after effective visibility and ancestor clips, or
nullwhen fully hidden.readonly screenBounds: Rectsourcereadonly visibleScreenBounds: Rect | nullsourceScreen-space form of
visibleDocumentBounds.readonly localToDocument: TransformMatrixsourcereadonly localToScreen: TransformMatrixsourcereadonly painterOrder: numbersourcereadonly effectivelyVisible: booleansourcereadonly effectiveOpacity: numbersource
sourcefunction indexSceneGeometryNodes( nodes: readonly NodeGeometry[] ): ReadonlyMap<NodeId, NodeGeometry>
sourcefunction withSceneGeometryIndex( snapshot: Omit<SceneGeometrySnapshot, "byId"> ): SceneGeometrySnapshot
sourceinterface TextGeometryreadonly nodeId: NodeIdsourcereadonly generation: bigintsourceRetained-scene generation used to reject geometry after a scene update.
readonly coordinateSpace: "node-local"sourcereadonly carets: readonly TextCaretGeometry[]sourcereadonly selection: TextRangeGeometry | nullsourcereadonly composition: TextRangeGeometry | nullsource
sourceinterface TextCaretGeometryreadonly position: TextPositionsourcereadonly localBounds: RectsourceCaret bounds in the edited node’s local coordinate space.
sourceinterface TextRangeGeometryreadonly range: TextRangesourcereadonly localRects: readonly Rect[]sourceOne rectangle per shaped fragment, all in node-local coordinates.
sourceinterface TextHitQueryreadonly nodeId: NodeIdsourcereadonly localPoint: PointsourceQuery position in the text node’s local coordinate space.
sourceinterface TextHitResultreadonly nodeId: NodeIdsourcereadonly generation: bigintsourcereadonly position: TextPositionsourcereadonly inside: booleansourceFalse when the result is the nearest caret outside laid-out text.
sourcefunction assertTextHitQuery(value: unknown): asserts value is TextHitQueryChecks that a value is a
TextHitQuerywith a node id and a finite local point.Retained across vertical moves; node-local x coordinate.
Updated node-local x coordinate for a following vertical move.
Checks that a value is a
TextNavigationQuerywith 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.
sourcetype SelectionMode = "replace" | "toggle" | "add"How
updateNodeSelectioncombines new node ids with the current selection.
sourcefunction updateNodeSelection( current: readonly NodeId[], incoming: readonly NodeId[], mode: SelectionMode ): readonly NodeId[]Combines a selection with incoming node ids.
replacereturns the incoming ids without duplicates,addappends the ones not yet selected, andtoggleremoves each incoming id that is selected and appends each one that is not. Order is kept, so the last id is the primary selection.
sourceinterface CanvasSelectionThe node a press selects, and the node the renderer actually hit to get there.
readonly hitNodeId: NodeIdsourcereadonly targetId: NodeIdsourcereadonly frameTitle?: truesourceThe press is on the frame’s title, which never starts a marquee.
sourcefunction frontmostCanvasSelection( document: DesignDocument, rootId: NodeId, hits: readonly EditorHit[], primarySelectedNodeId: NodeId | null, deepSelect = false ): CanvasSelection | nullWalks
hitsin order and returns the first one thatcanvasSelectionTargetmaps to a node, with the node it selects. PassdeepSelectfor Cmd or Ctrl clicks, which select the innermost node.
sourcefunction frontmostCanvasSelectionTarget( document: DesignDocument, rootId: NodeId, hits: readonly EditorHit[], primarySelectedNodeId: NodeId | null, deepSelect = false ): NodeId | nullLike
frontmostCanvasSelection, but returns only the selected node id.
sourcefunction canvasSelectionTarget( document: DesignDocument, rootId: NodeId, hitNodeId: NodeId, primarySelectedNodeId: NodeId | null, deepSelect = false ): NodeId | nullThe 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.
sourcefunction descendantSelectionTarget( document: DesignDocument, rootId: NodeId, hitNodeId: NodeId, primarySelectedNodeId: NodeId | null ): NodeId | nullThe node a double click selects: exactly one level further in than now.
sourcefunction selectableBodyHits( document: DesignDocument, rootId: NodeId, hits: readonly EditorHit[] ): readonly Extract<EditorHit, { readonly part: "body" }>[]Keeps only body hits on nodes that can be selected: the node and every ancestor below
rootIdare visible and unlocked. Order is kept.
sourcefunction frameMarqueeTarget( document: DesignDocument, rootId: NodeId, selection: CanvasSelection, selectedNodeIds: readonly NodeId[], deepSelect = false ): NodeId | nullThe 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.
sourcefunction marqueeSelectionNodeIds( document: DesignDocument, rootId: NodeId, geometry: SceneGeometrySnapshot, marquee: Rect, nested: boolean, scopeId: NodeId = rootId, selectionScopeId: NodeId | null = null ): readonly NodeId[]Returns intersecting selectable nodes in retained back-to-front painter order.
marqueeis in document units and is tested against each node’s visible document bounds fromgeometry.scopeIdis 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.selectionScopeIdis the one layer the canvas selects within, if any.
sourcefunction selectAllSelectionTargets( document: DesignDocument, rootId: NodeId, primarySelectedNodeId: NodeId | null ): readonly NodeId[]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.
sourcefunction topLevelSelectableNodeIds( document: DesignDocument, rootId: NodeId ): readonly NodeId[]The direct children of the page root that can be selected, in child order. Hidden and locked nodes are left out.
sourcefunction childSelectionTarget( document: DesignDocument, rootId: NodeId, nodeId: NodeId ): NodeId | nullThe 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 belowrootIdare visible and unlocked.
sourcefunction parentSelectionTarget( document: DesignDocument, rootId: NodeId, nodeId: NodeId ): NodeId | nullThe parent of a node, or
nullwhen the parent is the page rootrootId.
sourcefunction siblingSelectionTarget( document: DesignDocument, rootId: NodeId, nodeId: NodeId, direction: 1 | -1 ): NodeId | nullThe sibling that Tab (
direction1) 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.
sourcefunction nodeSelectionPath( document: DesignDocument, rootId: NodeId, nodeId: NodeId ): readonly NodeId[]The ids from just below
rootIddown tonodeId, outermost first. Empty whennodeIdis 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.
sourcefunction createBodyInteractionState( options: { readonly selectedNodeIds?: readonly NodeId[]; readonly hoveredNodeId?: NodeId | null; } = {} ): BodyInteractionStateCreates a
BodyInteractionStatewith no gesture. The last selected id becomes the primary selection.
sourcefunction updateBodyInteraction( environment: BodyInteractionEnvironment, state: BodyInteractionState, input: EditorPointerInput ): BodyInteractionUpdateReduces body-level pointer input without DOM access or document mutation. A completed move is descriptive; the caller owns operations and history.
sourceinterface BodyInteractionEnvironmentThe document, page and renderer state that
updateBodyInteractionreads.readonly document: DesignDocumentsourcereadonly rootId: NodeIdsourceThe root node of the active page.
readonly viewport: ViewportStatesourceMust describe the same screen geometry as
renderSession’s current snapshot.readonly renderSession: BodyRenderQueriessourcereadonly snapThresholdScreenPx?: numbersourceObject-alignment distance in CSS pixels. Defaults to 5.
readonly guideLines?: readonly BodyGuideLine[]sourceRuler 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 | nullsourceWhy a press on a node starts no move, or
nullto 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) => booleansourceWhether 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) => booleansourceWhether 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?: NodeIdsourceThe 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?: numbersourceBoth marquee dimensions must reach this CSS-pixel size. Defaults to 1.
readonly autoLayoutDropMarginScreenPx?: numbersourceExtra CSS-pixel distance around an auto-layout frame that accepts a drop. Defaults to 12.
sourcetype BodyRenderQueries = Pick< InteractiveRenderSession, "hitTest" | "getSceneGeometrySnapshot" >The two render session queries the body interaction needs: hit testing and the retained scene geometry.
sourceinterface BodyInteractionStateState for
updateBodyInteraction. Create it withcreateBodyInteractionState.readonly selection: BodySelectionStatesourcereadonly hoveredNodeId: NodeId | nullsourceThe node a click would select under the pointer, or
null.readonly gesture: BodyInteractionGesture | nullsourceThe gesture in progress, or
nullbetween gestures.
sourceinterface BodySelectionStateThe selected nodes as the body interaction sees them.
readonly nodeIds: readonly NodeId[]sourceSelected node ids without duplicates, in selection order.
readonly primaryNodeId: NodeId | nullsourceThe last id in
nodeIds, ornullwhen nothing is selected.
sourceinterface BodyInteractionUpdateThe result of
updateBodyInteraction.readonly state: BodyInteractionStatesourcereadonly pointerCapture: "none" | "capture" | "release"sourceWhat the caller should do with pointer capture:
capturewhen a gesture starts,releasewhen it ends or is cancelled.readonly completedMove: BodyMoveCompletion | nullsourceSet only on the pointer up that ends a move with a non-zero delta.
readonly refusedMove?: stringsourceWhy 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.
sourceinterface BodyMoveCompletionA finished move, returned once on pointer up. It describes the move; the caller turns it into document operations and history.
readonly kind: "move"sourcereadonly nodeIds: readonly NodeId[]sourcereadonly documentDelta: PointsourceCanonical document-space delta; command/history ownership remains with the caller.
readonly duplicate: booleansourcereadonly autoLayoutDrop: | Pick<BodyAutoLayoutDropTarget, "frameId" | "insertionIndex"> | nullsourcePresent 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: bigintsourceGeneration of the scene geometry snapshot the move started from.
sourcetype BodyInteractionGesture = BodyMarqueeGesture | BodyMoveGestureThe active body gesture: a marquee or a move.
sourceinterface BodyMarqueeGesture extends BodyGestureBaseA drag that draws a selection rectangle. Starts on empty canvas, or on the empty background of a frame that
frameMarqueeTargetpicks.readonly kind: "marquee"sourcereadonly geometry: SceneGeometrySnapshotsourceThe retained snapshot is fixed for the lifetime of this marquee.
readonly additive: booleansourceShift was held at the press: the marquee adds to the selection it started with.
readonly nested: booleansourceCmd or Ctrl was held at the press: the marquee selects the deepest nodes instead of direct children (see
marqueeSelectionNodeIds).readonly frameId: NodeId | nullsourceThe unselected frame this marquee started on, or
nullon empty canvas. The marquee selects inside this frame, and a click that never becomes a drag selects the frame itself.readonly dragging: booleansourceFalse while a press on a frame is still within click distance.
readonly rect: RectsourceThe marquee rectangle in document units.
readonly previewNodeIds: readonly NodeId[]sourceNodes inside the marquee right now, before they combine with the starting selection.
readonly previewSelection: BodySelectionStatesourceThe selection to show while dragging. It becomes the real selection on pointer up.
sourceinterface BodyMoveGesture extends BodyGestureBaseA 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"sourcereadonly geometry: SceneGeometrySnapshotsourceThe retained snapshot is fixed for hit-independent move policies such as drop targets.
readonly nodeIds: readonly NodeId[]sourcereadonly rawCurrentAtScreen: PointsourceThe pointer position in CSS pixels before snapping.
currentAtScreenholds the snapped position.readonly previewDelta: PointsourceHow far the nodes have moved so far, in document units, after snapping.
readonly duplicate: booleansourceAlt (Option) is held: releasing places copies and leaves the originals.
readonly moved: booleansourceTrue once the pointer has moved from the press point.
readonly dragged: booleansourceTrue once the pointer has traveled
DRAG_SCREEN_PXor more from the press point.readonly clickNarrowsToNodeId: NodeId | nullsourceThe 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" } >[]sourceSnap guides to draw, in document units.
readonly initialScreenBounds: AlignmentRectsourceCombined bounds of the moving nodes at the press, in CSS pixels. Snapping works on these bounds.
readonly alignmentTargets: readonly AlignmentTarget[]sourceBounds of other nodes, and ruler guides, that the move can snap to, in CSS pixels.
readonly duplicateSourceTargets: readonly AlignmentTarget[]sourceThe 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 | nullsourceThe auto-layout frame and slot under the pointer, or
null. Snapping is off while this is set.
sourceinterface BodyGuideLineA ruler guide that a move can snap to.
readonly axis: "x" | "y"sourcereadonly position: numbersourceThe guide’s x or y in document units.
readonly frameId?: NodeIdsourceThe 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.
sourceinterface BodyAutoLayoutDropTargetAn auto-layout frame under a moving selection, with its insertion slots.
readonly frameId: NodeIdsourcereadonly insertionIndex: numbersourceThe child index of the slot nearest the pointer.
readonly activeSlot: numbersourceThe position in
slotsof the slot nearest the pointer. Use it, notinsertionIndex, to highlight one indicator, because two slots of a wrapping layout can share an index.readonly slots: readonly BodyAutoLayoutInsertionSlot[]source
sourceinterface BodyAutoLayoutInsertionSlotOne 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: numbersourceThe child index the node would take.
readonly rect: RectsourceRenderer-neutral document-space rectangle for the insertion indicator.
These read what to draw while a gesture is active.
sourcefunction displayedBodySelection(state: BodyInteractionState): BodySelectionStateSelection rendered during a marquee; canonical selection changes only on pointer up.
sourcefunction bodyInteractionGesture( state: BodyInteractionState ): EditorGestureState | nullThe active gesture as a renderer-neutral
EditorGestureState, with points in document units, ornull.
sourcefunction bodyInteractionPreviews( state: BodyInteractionState ): readonly EditorPreviewGeometry[]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.
sourcefunction bodyMoveNodePreviews( state: BodyInteractionState ): readonly BodyMoveNodePreview[]One
BodyMoveNodePreviewper moving node during a move; empty otherwise.
sourceinterface BodyMoveNodePreviewHow 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: NodeIdsourcereadonly documentDelta: Pointsourcereadonly mode: "replace" | "duplicate"source
sourcefunction bodyAutoLayoutDropPreview( state: BodyInteractionState ): BodyAutoLayoutDropTarget | nullThe auto-layout drop target of the active move, or
null.
sourcefunction clearBodyInteractionHover( state: BodyInteractionState ): BodyInteractionStateClears 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.
sourcefunction translateBodyInteractionGesture( state: BodyInteractionState, documentDelta: Point, viewport: ViewportState ): BodyInteractionStateMoves 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.
sourcefunction holdBodyMoveAtPointerThroughPan( state: BodyInteractionState, documentDelta: Point, viewport: ViewportState ): BodyInteractionStateLike
translateBodyInteractionGesture, for a pan that happens while the pointer rests: the page moves bydocumentDeltaand the pointer stays where it is on screen. An active move keeps its layers under the pointer, so its preview delta shrinks bydocumentDelta, 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 astranslateBodyInteractionGesturedoes.
sourcefunction translateSceneGeometry( snapshot: SceneGeometrySnapshot, documentDelta: Point, viewport: ViewportState ): SceneGeometrySnapshottranslateNodeGeometryfor every node of a snapshot, and its scene bounds.
sourcefunction translateNodeGeometry( geometry: NodeGeometry, documentDelta: Point, viewport: ViewportState ): NodeGeometryMoves one node’s geometry by
documentDeltain 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.
sourcefunction translateRect(rect: Rect, delta: Point): RectMoves a rectangle by an offset in its own space.
sourcefunction documentDeltaToScreenDelta( viewport: ViewportState, documentDelta: Point ): PointThe 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.
sourcefunction rectangleFromPoints(start: Point, current: Point): RectThe rectangle between two corner points, with a non-negative width and height whichever way the drag went. Throws
TypeErrorfor non-finite points.
sourcefunction combineRects(rects: readonly Rect[]): Rect | nullThe smallest rectangle that holds every rectangle in the list, or
nullfor an empty list.
sourcefunction boundsForTransformedRect(rect: Rect, transform: TransformMatrix): RectThe axis-aligned bounds of a rectangle after a transform. Same as
transformRect.
sourcefunction constrainLineEndpoint( start: Point, current: Point, constrained: boolean ): PointWhen
constrained(Shift held), snaps the end of a line drawn fromstartto the nearest multiple of 45 degrees, keeping its length. Otherwise returnscurrentunchanged.
sourcefunction constrainBoxEndpoint( start: Point, current: Point, constrained: boolean ): PointWhen
constrained(Shift held), moves the dragged corner so the box drawn fromstartis a square, using the larger side. Otherwise returnscurrentunchanged.
sourceinterface ResizeHandleDirectionWhich 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 | 1sourcereadonly y: -1 | 0 | 1source
sourcefunction directionForResizeHandle( handle: EditorResizeHandle ): ResizeHandleDirectionThe
ResizeHandleDirectionfor a resize handle name.
sourcefunction rotationFromPointerPoints( options: { readonly initialRotation: number; readonly center: Point; readonly start: Point; readonly current: Point; readonly snapIncrement?: number; } ): numberThe rotation in degrees during a rotate drag:
initialRotationplus the angle the pointer has turned aroundcentersincestart, wrapped withnormalizeRotationDegrees. WithsnapIncrement, the result is rounded to a multiple of it (for example 15 while Shift is held). All points must be in the same space. ThrowsRangeErrorfor a snap increment that is not positive. With taking the offset of each point fromcenter:and, when snapping, with =
snapIncrement.
sourcefunction rotatePointAround(point: Point, center: Point, degrees: number): PointRotates a point around a center by
degrees. In a y-down space such as the canvas, positive angles turn clockwise. ThrowsTypeErrorfor non-finite input. With and center :
sourcefunction normalizeRotationDegrees(degrees: number): numberWraps an angle into the range from -180 (included) to 180 (excluded) degrees. Throws
TypeErrorfor 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.
sourcefunction resizeGroup( document: DesignDocument, groupId: NodeId, frame: Rect ): readonly SceneNode[]Resizes a group to
frameby 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 choosesframeso the anchored edge stays fixed.Returns the group followed by every changed descendant, or an empty list when the group cannot be resized.
sourcefunction uniformScaleFactorFromPointer( options: { readonly anchor: Point; readonly start: Point; readonly current: Point; readonly minimumFactor: number; } ): numberThe uniform scale factor during a scale drag. It projects the pointer’s offset from
anchoronto the start offset, so 1 means no change and 2 means twice as far from the anchor. Never belowminimumFactor. Returns 1 whenstartis on the anchor. ThrowsRangeErrorwhenminimumFactoris not positive. With =start−anchorand =current−anchor:
sourcetype ScaleCorner = "north-west" | "north-east" | "south-east" | "south-west"
sourcetype ScaleOrigin = ScaleCorner | "center"The point a scale keeps still: the corner opposite the dragged handle, or the layer’s center when Option/Alt is held.
sourcefunction scaleAnchor(corner: ScaleOrigin, frame: Pick<Rect, "width" | "height">)
sourcefunction scaleSubtree( document: DesignDocument, rootId: NodeId, factor: number, corner: ScaleOrigin ): readonly SceneNode[]
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.
sourcefunction selectionBoxBounds(nodes: readonly SceneNode[]): Rect | nullThe 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.
sourcefunction resizeNodesInBox( document: DesignDocument, nodeIds: readonly NodeId[], from: Rect, to: Rect, boxAngle = 0 ): readonly SceneNode[]Resizes several sibling layers together, like resizing a multi-selection’s bounding box in Figma: the box
frombecomesto(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 fillto(scaleLayerInParentdescribes 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.boxAngleis the box’s turn in degrees (clockwise, screen y down) when it is not upright, as after rotating the selection:fromandtoare then in the box’s own coordinates, the parent’s turned byboxAngleabout 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.
sourcefunction rotateNodesAbout( nodes: readonly SceneNode[], degrees: number, center: Point ): readonly SceneNode[]Turns sibling layers by
degrees(clockwise, screen y down) aroundcenterin their parent’s coordinates, as rotating a multi-selection does in Figma: each layer’s center moves aroundcenterand 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.
sourcefunction scaleNodesAbout( document: DesignDocument, nodeIds: readonly NodeId[], factor: number, anchor: { readonly x: number; readonly y: number } ): readonly SceneNode[]Scales several sibling layers together by
factoraboutanchorin their shared parent’s coordinates, as the scale tool (K) scales a multi-selection: every layer’s position moves away from the anchor byfactor, and each layer scales asscaleSubtreescales one, strokes, radii, effects and text included. Returns every changed node, or an empty list when a layer is missing or locked orfactoris not positive.
sourcefunction connectorWithMovedEnds( node: ConnectorNode, move: (point: Point) => Point ): ConnectorNodeAn 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.
sourcefunction snapObjectBounds( initialBounds: AlignmentRect, rawDeltaX: number, rawDeltaY: number, targets: readonly AlignmentTarget[], threshold: number, movingBaselines: readonly number[] = [] ): ObjectSnapResult
sourceinterface ObjectSnapResultreadonly deltaX: numbersourcereadonly deltaY: numbersourcereadonly guides: readonly AlignmentGuide[]source
sourceinterface AlignmentRectreadonly left: numbersourcereadonly top: numbersourcereadonly right: numbersourcereadonly bottom: numbersource
sourceinterface AlignmentTargetreadonly id: stringsourcereadonly bounds: AlignmentRectsourcereadonly baselines?: readonly number[]sourcereadonly line?: "x" | "y"sourceMakes the target a single line, such as a ruler guide. An “x” line sits at
bounds.leftand only offers a horizontal position; a “y” line sits atbounds.topand 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.
sourcetype AlignmentAnchor = "start" | "center" | "end" | "baseline"Which point of a box lines up: its left/top, center, right/bottom, or a text baseline.
sourceinterface AlignmentGuidereadonly orientation: "horizontal" | "vertical"sourcereadonly position: numbersourcereadonly start: numbersourcereadonly end: numbersourcereadonly movingAnchor: AlignmentAnchorsourceThe 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.
sourcefunction measureDistances( reference: Rect, moving: Rect ): readonly DistanceMeasurement[]Measurements from
referencetomoving, both axis-aligned rectangles in the same coordinate space. Zero distances are left out, so aligned edges produce no measurement.
sourceinterface DistanceMeasurementreadonly axis: MeasurementAxissource“x” measures a horizontal distance, drawn as a horizontal line.
readonly start: numbersourceWhere the line starts and ends on the measured axis;
start < end.readonly end: numbersourcereadonly position: numbersourceThe line’s coordinate on the other axis.
readonly value: numbersourceend - start, the number the label shows.readonly kind: "gap" | "offset" | "inset"sourceWhich kind of distance this is, for labels and tests.
readonly extension: MeasurementExtension | nullsource
sourcetype MeasurementAxis = "x" | "y"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).
sourceinterface MeasurementExtensionreadonly position: numbersourceCoordinate of the moving object’s edge on the measured axis.
readonly start: numbersourceSpan on the other axis, from the moving object to the measurement line.
readonly end: numbersource
sourcefunction formatMeasurementValue(value: number): stringThe 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.
sourceinterface TextPositionA paragraph-relative text location. Offsets always count UTF-16 code units.
readonly paragraphIndex: numbersourcereadonly utf16Offset: numbersourcereadonly affinity: TextAffinitysource
sourcetype TextAffinity = "upstream" | "downstream"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.
sourceinterface TextRangeAnchor and focus preserve selection direction instead of normalizing the range.
readonly anchor: TextPositionsourcereadonly focus: TextPositionsource
sourceinterface TextCompositionStateTransient IME text replacing a canonical document range.
readonly replacementRange: TextRangesourceCanonical range that the composition replaced when it began.
readonly displayRange: TextRangesourceRange occupied by the currently published composition preview.
readonly text: stringsourcereadonly selectionStart: numbersourceUTF-16 offsets within text, mirroring browser composition APIs.
readonly selectionEnd: numbersource
sourceinterface ActiveTextEditingStateThe text node being edited, its current selection, and any active IME composition.
readonly nodeId: NodeIdsourcereadonly selection: TextRangesourcereadonly composition: TextCompositionState | nullsource
sourcefunction assertTextPosition( value: unknown, label = "Text position" ): asserts value is TextPositionChecks that a value is a
TextPosition: integer, non-negative paragraph index and UTF-16 offset, and a known affinity. ThrowsTypeErrororRangeError. It does not check the position against any document.
sourcefunction assertTextRange( value: unknown, label = "Text range" ): asserts value is TextRangeChecks that a value is a
TextRangewhose anchor and focus passassertTextPosition.
sourcefunction assertActiveTextEditingState( value: unknown, label = "Active text editing state" ): asserts value is ActiveTextEditingStateChecks 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.
sourcefunction createRichTextEditingState( input: { readonly nodeId: NodeId; readonly paragraphs: readonly TextParagraph[]; readonly selection: TextRange; readonly layoutGeneration: bigint; } ): RichTextEditingState
sourcefunction reduceRichTextEditing( state: RichTextEditingState, action: RichTextEditingAction ): RichTextEditingTransitionPure rich-text state transition. The controller never measures text, chooses grapheme navigation stops, dispatches document operations, or records history.
sourceinterface RichTextEditingStateImmutable editor state. It contains canonical paragraphs but does not own a document store or history stack; callers apply the returned effects.
readonly nodeId: NodeIdsourcereadonly paragraphs: readonly TextParagraph[]sourcereadonly selection: TextRangesourcereadonly layoutGeneration: bigint | nullsourceThe generation that exactly matches paragraphs, or
nullafter an edit.readonly lastLayoutGeneration: bigintsourceHighest synchronized generation, retained while
layoutGenerationisnull.readonly composition: RichTextCompositionSession | nullsourcereadonly pointerSelection: RichTextPointerSelectionState | nullsourcereadonly pendingRunFormat: RichTextPendingRunFormat | nullsourceFull authored run style used for subsequent collapsed-caret insertion.
readonly preferredLocalX: number | undefinedsourcereadonly nextRequestId: numbersourcereadonly nextTransactionId: numbersource
sourcetype 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; }
sourceinterface RichTextEditingTransitionreadonly state: RichTextEditingStatesourcereadonly effects: readonly RichTextEditingEffect[]source
sourcetype 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; }[]; }
sourcetype 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"
sourcetype 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; }
sourceinterface RichTextPointerSelectionStatereadonly pointerId: numbersourcereadonly anchor: TextPositionsourcereadonly unit?: TextRangesourceThe 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: TextRangesourcereadonly baselinePendingRunFormat: RichTextPendingRunFormat | nullsource
sourcefunction pointerUnitRange( paragraphs: readonly TextParagraph[], position: TextPosition, unit: "word" | "paragraph" ): TextRangeThe 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.
sourceinterface RichTextCompositionSession extends TextCompositionStatereadonly transactionId: numbersourcereadonly baselineParagraphs: readonly TextParagraph[]sourcereadonly baselineSelection: TextRangesourcereadonly previewApplied: booleansourceWhether a paragraph preview has been published to the host.
sourcetype RichTextKeyboardPlatform = "macos" | "windows-linux"
sourcetype RichTextTransactionBoundary = "begin" | "commit" | "cancel"
sourcetype RichTextParagraphEditStage = | "canonical" | "composition-preview" | "composition-commit" | "composition-restore"
sourcefunction activeTextEditingState( state: RichTextEditingState ): ActiveTextEditingStateConverts 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.
sourceinterface RichTextRunFormatPatchreadonly styleId?: RichTextFieldEdit<TextStyleId>sourcereadonly overrides?: { readonly [Key in keyof RichTextRunOverrides]?: RichTextFieldEdit< Exclude<RichTextRunOverrides[Key], undefined> > }sourcereadonly openTypeFeatureEdits?: RichTextTaggedValueEdits<boolean | number>sourceMerges individual tags without replacing unrelated authored feature values.
readonly fontVariationEdits?: RichTextTaggedValueEdits<number>sourceMerges individual axes without replacing unrelated authored variation values.
sourceinterface RichTextParagraphFormatPatchParagraph fields use the same explicit set/remove editing contract.
readonly direction?: RichTextFieldEdit<NonNullable<TextParagraph["direction"]>>sourcereadonly align?: RichTextFieldEdit<NonNullable<TextParagraph["align"]>>sourcereadonly spacingBefore?: RichTextFieldEdit<number>sourcereadonly spacingAfter?: RichTextFieldEdit<number>sourcereadonly indent?: RichTextFieldEdit<NonNullable<TextParagraph["indent"]>>sourcereadonly list?: RichTextFieldEdit<NonNullable<TextParagraph["list"]>>sourcereadonly indentFields?: RichTextIndentFieldEditssourceMerges individual indent values; remove restores that value to zero.
readonly listFields?: RichTextListFieldEditssourceMerges list controls while preserving an untouched marker.
readonly listMarkerFields?: RichTextListMarkerFieldEditssourceMerges marker controls without creating a list implicitly.
sourceinterface RichTextIndentFieldEditsreadonly left?: RichTextFieldEdit<number>sourcereadonly right?: RichTextFieldEdit<number>sourcereadonly firstLine?: RichTextFieldEdit<number>source
sourceinterface RichTextListFieldEditsreadonly kind?: RichTextFieldEdit<"ordered" | "unordered">sourceRemoving kind clears the list; changing it preserves marker authorship.
readonly level?: RichTextFieldEdit<number>sourceRemoving level restores level zero.
readonly start?: RichTextFieldEdit<number>sourceRemoving start restores automatic numbering.
sourceinterface RichTextListMarkerFieldEditsreadonly text?: RichTextFieldEdit<NonNullable<TextListMarker["text"]>>sourceRemoving text restores the renderer’s automatic marker text.
readonly styleId?: RichTextFieldEdit<NonNullable<TextListMarker["styleId"]>>sourceRemoving
styleIdrestores first-run shared-style inheritance.readonly overrides?: RichTextFieldEdit<NonNullable<TextListMarker["overrides"]>>sourceRemoving overrides restores first-run direct-format inheritance.
readonly gap?: RichTextFieldEdit<NonNullable<TextListMarker["gap"]>>sourceRemoving gap restores the renderer’s font-relative automatic gap.
sourcetype RichTextFieldEdit<Value> = | { readonly kind: "set"; readonly value: Value } | { readonly kind: "remove" }Omission leaves a field unchanged; remove restores inheritance.
sourcetype RichTextTaggedValueEdits< Value > = Readonly<Record<string, RichTextFieldEdit<Value>>>
sourcetype RichTextRunOverrides = NonNullable<TextRun["overrides"]>
sourcetype RichTextPendingRunFormat = Readonly<Pick<TextRun, "styleId" | "overrides">>
sourcefunction selectedRichTextRunFormats( state: RichTextEditingState ): readonly RichTextPendingRunFormat[]Enumerates immutable authored run formats intersecting the current selection.
sourcefunction aggregateRichTextSelectionFormatting( state: RichTextEditingState ): RichTextSelectionFormattingReturns authored selection formatting without consulting DOM editing state.
sourceinterface RichTextSelectionFormattingreadonly 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>> >; }sourcereadonly 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
sourcetype RichTextAggregatedValue<Value> = | { readonly kind: "uniform"; readonly value: Value } | { readonly kind: "mixed" }
sourcetype RichTextAggregatedRunOverrides = { readonly [Key in keyof RichTextRunOverrides]-: RichTextAggregatedValue< RichTextRunOverrides[Key] | undefined > }
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.
sourceinterface VectorGeometryreadonly nodeId: NodeIdsourcereadonly generation: bigintsourcereadonly vertices: readonly VectorVertexGeometry[]sourcereadonly handles: readonly VectorCubicHandleGeometry[]sourcereadonly segments: readonly VectorSegmentGeometry[]sourcereadonly regions: readonly VectorRegionGeometry[]source
sourceinterface VectorVertexGeometryreadonly id: stringsourcereadonly kind: "vertex"sourcereadonly localPoint: Pointsourcereadonly screenPoint: Pointsource
sourceinterface VectorCubicHandleGeometryreadonly id: stringsourcereadonly kind: "cubic-handle"sourcereadonly segmentId: stringsourcereadonly vertexId: stringsourcereadonly endpoint: "start" | "end"sourcereadonly localPoint: Pointsourcereadonly screenPoint: Pointsource
sourcetype VectorSegmentGeometry = | VectorLineSegmentGeometry | VectorCubicSegmentGeometry | VectorArcSegmentGeometry
sourceinterface VectorLineSegmentGeometry extends VectorSegmentGeometryBasereadonly kind: "line"source
sourceinterface VectorCubicSegmentGeometry extends VectorSegmentGeometryBasereadonly kind: "cubic"sourcereadonly startHandleId: stringsourcereadonly endHandleId: stringsource
sourceinterface VectorArcSegmentGeometry extends VectorSegmentGeometryBasereadonly kind: "elliptical-arc"sourcereadonly radiusX: numbersourcereadonly radiusY: numbersourcereadonly xAxisRotation: numbersourcereadonly largeArc: booleansourcereadonly sweep: booleansource
sourceinterface VectorRegionGeometryreadonly id: stringsourcereadonly kind: "region"sourcereadonly windingRule: "nonzero" | "evenodd"sourcereadonly localBounds: Rectsourcereadonly screenBounds: Rectsource
sourceinterface VectorHitQueryreadonly nodeId: NodeIdsourcereadonly screenPoint: Pointsourcereadonly toleranceCssPx: numbersource
sourcetype 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" })
sourceinterface VectorHitTestResultreadonly nodeId: NodeIdsourcereadonly generation: bigintsourcereadonly 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.
sourceclass VectorTopologyError extends Errorreadonly code: VectorTopologyErrorCodesourcereadonly componentId: string | nullsourcenew ( code: VectorTopologyErrorCode, message: string, componentId: string | null = null )source
sourcetype VectorTopologyErrorCode = | "DUPLICATE_ID" | "INVALID_PARAMETER" | "MISSING_HANDLE" | "MISSING_LOOP" | "MISSING_REGION" | "MISSING_SEGMENT" | "MISSING_VERTEX" | "UNSAFE_REGION_EDIT" | "UNSUPPORTED_SEGMENT"
sourcefunction addVectorVertex( network: VectorNetwork, vertex: VectorVertex ): VectorNetworkAdds one caller-identified free anchor without changing existing topology.
sourcefunction addVectorSegment( network: VectorNetwork, segment: VectorSegment ): VectorNetworkAdds one line or cubic edge between existing anchors using caller-owned IDs.
sourcefunction updateVectorVertices( network: VectorNetwork, updates: readonly VectorVertexUpdate[] ): VectorNetworkMoves one or more shared vertices without changing endpoint-relative handles.
sourceinterface VectorVertexUpdatereadonly vertexId: VectorVertexIdsourcereadonly x: numbersourcereadonly y: numbersource
sourcefunction updateVectorHandle( network: VectorNetwork, update: VectorHandleUpdate ): VectorNetworkUpdates one control handle and applies its optional tangent constraint atomically.
sourceinterface VectorHandleUpdatereadonly handleId: VectorHandleIdsourcereadonly dx: numbersourcereadonly dy: numbersource
sourcefunction setVectorTangentConstraint( network: VectorNetwork, update: VectorTangentConstraintUpdate ): VectorNetworkCouples two handles at one vertex, replacing either handle’s prior pair.
sourceinterface VectorTangentConstraintUpdatereadonly vertexId: VectorVertexIdsourcereadonly handleIds: readonly [VectorHandleId, VectorHandleId]sourcereadonly drivenHandleId: VectorHandleIdsourceThe driven handle keeps its position; the paired handle is aligned to it.
readonly mode: VectorTangentConstraint["mode"]source
sourcefunction removeVectorTangentConstraints( network: VectorNetwork, handleIds: readonly VectorHandleId[] ): VectorNetworkRemoves every tangent pair involving one of the supplied handles.
sourcefunction deleteVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[] ): VectorNetworkDeletes anchors, every incident edge, affected fill loops, and tangent pairs.
sourcefunction deleteVectorSegments( network: VectorNetwork, segmentIds: readonly VectorSegmentId[], options: DeleteVectorComponentsOptions = {} ): VectorNetworkDeletes edges and any fill loops that depended on them.
sourcefunction deleteVectorRegions( network: VectorNetwork, regionIds: readonly VectorRegionId[] ): VectorNetworkDeletes independently paintable regions without changing their shared topology.
sourceinterface DeleteVectorComponentsOptionsreadonly deleteOrphanVertices?: booleansourceSegment deletion preserves free anchors by default.
sourcefunction addVectorRegion( network: VectorNetwork, region: VectorRegion ): VectorNetworkAdds one independently paintable region over an existing connected chain.
sourcefunction openVectorRegionLoop( network: VectorNetwork, target: VectorRegionLoopTarget, segmentId: VectorSegmentId ): VectorNetworkOpens 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.
sourcefunction closeVectorRegionLoop( network: VectorNetwork, target: VectorRegionLoopTarget, segment: VectorSegment ): VectorNetworkCloses one implicitly filled open contour with a caller-identified line or cubic. The supplied edge may use either orientation across the open gap.
sourceinterface VectorRegionLoopTargetreadonly regionId: VectorRegionIdsourcereadonly loopIndex: numbersource
sourcefunction joinVectorVertices( network: VectorNetwork, sourceVertexId: VectorVertexId, targetVertexId: VectorVertexId, options: JoinVectorVerticesOptions = {} ): VectorNetworkMerges one shared anchor into another and rewires all incident topology.
sourceinterface JoinVectorVerticesOptionsreadonly position?: { readonly x: number; readonly y: number }sourceDefaults to the retained target vertex position.
sourcefunction convertVectorLineToCubic( network: VectorNetwork, segmentId: VectorSegmentId, ids: VectorLineToCubicIds ): VectorNetworkConverts a line to a cubic while preserving its segment ID and region references.
sourceinterface VectorLineToCubicIdsreadonly startHandleId: VectorHandleIdsourcereadonly endHandleId: VectorHandleIdsource
sourcefunction convertVectorCubicToLine( network: VectorNetwork, segmentId: VectorSegmentId ): VectorNetworkConverts a cubic to a line and removes constraints that referenced its handles.
sourcefunction splitVectorSegment( network: VectorNetwork, segmentId: VectorSegmentId, amount: number, ids: VectorSplitIds ): VectorNetworkSplits 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.
sourceinterface VectorSplitIdsreadonly vertexId: VectorVertexIdsourcereadonly segmentId: VectorSegmentIdsourcereadonly firstEndHandleId?: VectorHandleIdsourceRequired when splitting a cubic segment.
readonly secondStartHandleId?: VectorHandleIdsourceRequired when splitting a cubic segment.
sourcetype VectorEditableSegment = Extract<VectorSegment, { kind: "line" | "cubic" }>
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.
sourcefunction createVectorEditingState( options: { readonly nodeId: NodeId; readonly network: VectorNetwork; readonly selection?: VectorEditingSelection; } ): VectorEditingState
sourcefunction reduceVectorEditing( state: VectorEditingState, action: VectorEditingAction ): VectorEditingResult
sourceinterface VectorEditingStatereadonly nodeId: NodeIdsourcereadonly network: VectorNetworksourcereadonly previewNetwork: VectorNetworksourcereadonly selection: VectorEditingSelectionsourcereadonly gesture: VectorEditingGesture | nullsource
sourcetype 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 }
sourceinterface VectorEditingResultreadonly state: VectorEditingStatesourcereadonly effects: readonly VectorEditingEffect[]source
sourcetype 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; }
sourcetype 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"
sourceinterface VectorEditingSelectionreadonly vertexIds: readonly VectorVertexId[]sourcereadonly handleIds: readonly VectorHandleId[]sourcereadonly segmentIds: readonly VectorSegmentId[]sourcereadonly regionIds: readonly VectorRegionId[]sourcereadonly primary: VectorComponentSelection | nullsource
sourceinterface VectorEditingGesturereadonly pointerId: numbersourcereadonly component: Extract< VectorComponentSelection, { readonly kind: "vertex" | "handle" | "segment" } >sourcereadonly startedAt: Pointsourcereadonly currentAt: Pointsourcereadonly sourceNetwork: VectorNetworksourcereadonly draggedVertexIds: readonly VectorVertexId[]sourcereadonly collapseOnClick?: booleansourceA 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?: VectorNetworksourceThe 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?: numbersourceHow 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 ofdistanceSpacewhen that is set.readonly distanceSpace?: TransformMatrixsourceA 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?: booleansourceSet once the pointer has gone past
breakThreshold, and stays set.readonly bend?: VectorBendGesturesourceA Bend tool drag, which shapes handles instead of moving points.
readonly pointBox?: VectorPointBoxGesturesourceA drag of the box around several selected points, which scales or rotates them.
sourcetype VectorComponentHit = Extract< EditorHit, { readonly part: | "vector-anchor" | "vector-handle" | "vector-segment" | "vector-region"; } >
sourcetype 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 }
sourcefunction vectorComponentHitFromQueryHit( nodeId: NodeId, network: VectorNetwork, hit: VectorQueryHit ): VectorComponentHit | nullMaps 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.
sourcefunction createVectorInteractionState( environment: VectorInteractionEnvironment, selection?: VectorEditingSelection ): VectorInteractionState | nullOpens component editing only when scene, vector geometry, and node transform all describe one retained generation.
sourcefunction updateVectorInteraction( environment: VectorInteractionEnvironment, state: VectorInteractionState, input: EditorPointerInput ): VectorInteractionResultReduces existing-path component input. Only pointer-down performs a vector hit query; a gesture uses its frozen inverse transform for every later point.
sourcefunction invalidateVectorInteraction( state: VectorInteractionState, reason: VectorInteractionInvalidationReason ): VectorInteractionResult
sourceinterface VectorInteractionEnvironmentreadonly nodeId: NodeIdsourcereadonly network: VectorNetworksourceExact canonical network object currently owned by
nodeId.readonly viewport: ViewportStatesourceExact viewport object currently installed on
renderSession.readonly renderSession: VectorInteractionRenderQueriessourcereadonly hitToleranceCssPx?: numbersourceNative component-hit radius in CSS pixels. Defaults to 6.
readonly editTool?: VectorEditToolContextsourceThe 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.
idsnames what they add.readonly pointBox?: booleansourceOffer 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.
sourcetype VectorInteractionRenderQueries = Pick< InteractiveRenderSession, "getSceneGeometrySnapshot" | "getVectorGeometry" | "hitTestVector" >
sourceinterface VectorInteractionStatereadonly editing: VectorEditingStatesourcereadonly binding: VectorInteractionBindingsourcereadonly status: "ready" | "awaiting-commit" | "invalidated"source
sourceinterface VectorInteractionBindingreadonly generation: bigintsourcereadonly expectedNetwork: VectorNetworksourcereadonly viewport: ViewportStatesourcereadonly renderSession: VectorInteractionRenderQueriessourcereadonly nodeGeometry: NodeGeometrysourcereadonly screenToLocal: TransformMatrixsource
sourcetype VectorInteractionInvalidationReason = | "node-changed" | "network-changed" | "viewport-changed" | "render-session-changed" | "scene-generation-changed" | "node-geometry-changed" | "vector-query-unavailable" | "coordinate-conversion-failed"
sourceinterface VectorInteractionResultreadonly state: VectorInteractionStatesourcereadonly effects: readonly VectorInteractionEffect[]source
sourcetype 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; }
sourcefunction vectorInteractionLocalPoint( state: VectorInteractionState, screenPoint: Point ): Point | nullConverts one screen point through the interaction’s exact frozen affine inverse.
sourcefunction vectorEditingSnapshot( state: VectorInteractionState ): VectorEditingSnapshot | nullReturns
nullonce the frozen renderer/canonical authority is invalidated.
sourceinterface VectorEditingSnapshotRenderer-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: NodeIdsourcereadonly generation: bigintsourcereadonly coordinateSpace: "node-local"sourcereadonly status: "ready" | "awaiting-commit"sourcereadonly sourceNetwork: VectorNetworksourcereadonly network: VectorNetworksourcereadonly selection: VectorEditingSelectionSnapshotsourcereadonly gesture: VectorEditingGestureSnapshot | nullsourcereadonly pendingHandle?: PenPendingHandle | nullsourceThe handle the Pen is pulling out of a point, or left there for the next segment: drawn as chrome, because no segment of
networkowns it yet. Its point is innetwork. Absent or null when there is none.readonly viewport: ViewportStatesourceExact viewport object frozen with this retained-scene generation.
readonly localToDocument: TransformMatrixsourcereadonly localToScreen: TransformMatrixsourcereadonly screenToLocal: TransformMatrixsource
sourceinterface VectorEditingSelectionSnapshotreadonly vertexIds: readonly VectorVertexId[]sourcereadonly handleIds: readonly VectorHandleId[]sourcereadonly segmentIds: readonly VectorSegmentId[]sourcereadonly regionIds: readonly VectorRegionId[]sourcereadonly primary: VectorEditingComponentSnapshot | nullsource
sourceinterface VectorEditingGestureSnapshotreadonly pointerId: numbersourcereadonly component: Extract< VectorEditingComponentSnapshot, { readonly kind: "vertex" | "handle" | "segment" } >sourcereadonly startedAt: PointsourceFrozen node-local pointer coordinate at gesture start.
readonly currentAt: PointsourceLatest node-local pointer coordinate used by the preview.
sourcetype 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 }
Boolean operations (union, intersection, difference, exclusion) are requested from the renderer through InteractiveRenderSession.booleanVectorPaths.
sourcetype VectorBooleanOperation = | "union" | "intersection" | "difference" | "exclusion"
sourceinterface VectorBooleanPathRequestOne retained-scene Boolean request with caller-owned result ID authority.
readonly primaryNodeId: NodeIdsourcereadonly secondaryNodeId: NodeIdsourcereadonly operation: VectorBooleanOperationsourcereadonly resultIdNamespace: stringsource
sourceinterface VectorBooleanPathResultCanonical primary-local geometry produced from one exact retained generation.
readonly generation: bigintsourcereadonly primaryNodeId: NodeIdsourcereadonly secondaryNodeId: NodeIdsourcereadonly operation: VectorBooleanOperationsourcereadonly 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.
sourceinterface AccessibilitySnapshotV1readonly version: typeof ACCESSIBILITY_SNAPSHOT_VERSIONsourcereadonly documentId: DocumentIdsourcereadonly pageId: PageIdsourcereadonly generation: bigintsourceRetained-scene generation, not an editor history revision.
readonly focusOwner: AccessibilityFocusOwnerV1sourcereadonly root: AccessibilityNodeSnapshotV1source
sourcefunction createAccessibilitySnapshotV1( input: AccessibilitySnapshotInputV1 ): AccessibilitySnapshotV1Builds the renderer-neutral v1 semantic mirror for one active page. It reads but never mutates the canonical document, geometry, or overlay.
sourceinterface AccessibilitySnapshotInputV1readonly document: DesignDocumentsourcereadonly activePageId: PageIdsourcereadonly geometry: SceneGeometrySnapshotsourcereadonly overlay: AccessibilityOverlayStateV1source
sourceinterface AccessibilityNodeSnapshotV1readonly id: NodeIdsourcereadonly role: AccessibilityRoleV1sourcereadonly name: stringsourcereadonly description: string | nullsourcereadonly value: string | nullsourcereadonly screenBounds: RectsourceVisible, clipped bounds in CSS pixels relative to the editor surface.
readonly focused: booleansourcereadonly selected: booleansourcereadonly editing: booleansourcereadonly readOnly: booleansourcereadonly actions: readonly AccessibilityActionV1[]sourcereadonly textSelection: AccessibilityTextSelectionV1 | nullsourcereadonly children: readonly AccessibilityNodeSnapshotV1[]source
sourcetype AccessibilityRoleV1 = typeof ACCESSIBILITY_ROLES_V1[number]
sourceconst ACCESSIBILITY_ROLES_V1: readonly [ "document", "group", "text", "textbox", "code", "image", "video", "shape", "connector" ]
sourcetype AccessibilityActionV1 = typeof ACCESSIBILITY_ACTIONS_V1[number]
sourceconst ACCESSIBILITY_ACTIONS_V1: readonly [ "focus", "select", "toggle-selection", "edit" ]
sourcetype AccessibilityFocusOwnerV1 = typeof ACCESSIBILITY_FOCUS_OWNERS_V1[number]
sourceconst ACCESSIBILITY_FOCUS_OWNERS_V1: readonly [ "none", "react-shell", "canvas", "accessibility-mirror", "text-input" ]
sourcetype AccessibilityEditStateV1 = | { readonly kind: "text"; readonly nodeId: NodeId; readonly selection: AccessibilityTextSelectionV1; } | { readonly kind: "code"; readonly nodeId: NodeId }
sourceinterface AccessibilityOverlayStateV1UI-owned state layered over one exact retained scene generation.
readonly focusOwner: AccessibilityFocusOwnerV1sourcereadonly focusedNodeId: NodeId | nullsourcereadonly selectedNodeIds: readonly NodeId[]sourcereadonly edit: AccessibilityEditStateV1 | nullsourcereadonly pageLayersHidden?: booleansourceTrue 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.
sourceinterface AccessibilityTextPositionV1Paragraph-relative UTF-16 position, independent of renderer layout.
readonly paragraphIndex: numbersourcereadonly utf16Offset: numbersourcereadonly affinity: AccessibilityTextAffinityV1source
sourceinterface AccessibilityTextSelectionV1Anchor and focus retain selection direction.
readonly anchor: AccessibilityTextPositionV1sourcereadonly focus: AccessibilityTextPositionV1source
sourcetype AccessibilityTextAffinityV1 = typeof ACCESSIBILITY_TEXT_AFFINITIES_V1[number]
sourceconst ACCESSIBILITY_TEXT_AFFINITIES_V1: readonly ["upstream", "downstream"]
sourceconst ACCESSIBILITY_SNAPSHOT_VERSION: 1
sourceclass AccessibilitySnapshotErrorV1 extends Errornew (readonly code: AccessibilitySnapshotErrorCodeV1, message: string)source
sourcetype AccessibilitySnapshotErrorCodeV1 = | "invalid-active-page" | "invalid-document-tree" | "invalid-geometry" | "invalid-overlay"
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.
sourcefunction resolveAccessibilityActionAuthorityV1( snapshot: AccessibilitySnapshotV1, request: AccessibilityActionRequestV1 ): AccessibilityActionAuthorityV1Resolves authority only. The caller still owns routing and mutation after this function confirms the exact generation and advertised node action.
sourceinterface AccessibilityActionRequestV1readonly documentId: DocumentIdsourcereadonly pageId: PageIdsourcereadonly generation: bigintsourcereadonly nodeId: NodeIdsourcereadonly action: AccessibilityActionV1source
sourceinterface AccessibilityActionAuthorityV1readonly version: typeof ACCESSIBILITY_SNAPSHOT_VERSIONsourcereadonly documentId: DocumentIdsourcereadonly pageId: PageIdsourcereadonly generation: bigintsourcereadonly nodeId: NodeIdsourcereadonly action: AccessibilityActionV1sourcereadonly node: AccessibilityNodeSnapshotV1source
sourceclass AccessibilityActionAuthorityErrorV1 extends Errornew (readonly code: AccessibilityActionAuthorityErrorCodeV1, message: string)source
sourcetype AccessibilityActionAuthorityErrorCodeV1 = | "invalid-request" | "context-mismatch" | "stale-generation" | "node-not-found" | "action-not-advertised"
sourceinterface AccessibilityNodeSnapshotreadonly id: stringsourcereadonly role: stringsourcereadonly name: stringsourcereadonly value?: stringsourcereadonly bounds?: Rectsourcereadonly focused: booleansourcereadonly selected: booleansourcereadonly actions: readonly string[]sourcereadonly 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.
sourceinterface EditorScenarioreadonly name: stringsourcereadonly requirements?: EditorScenarioRequirementssourcerun(driver: EditorDriver): Promise<void>source
sourceinterface EditorScenarioRequirementsreadonly pointerTypes?: readonly EditorPointerType[]sourcereadonly keyboardRepeat?: booleansourcereadonly composition?: booleansourcereadonly clipboard?: readonly EditorClipboardInput["action"][]sourcereadonly textEditing?: booleansourcereadonly textGeometry?: booleansourcereadonly textHitTesting?: booleansourcereadonly vectorGeometry?: booleansourcereadonly vectorHitTesting?: booleansourcereadonly vectorBooleanOperations?: booleansourcereadonly accessibility?: booleansourcereadonly frameCapture?: booleansourcereadonly variableDevicePixelRatio?: booleansource
sourcefunction runEditorScenarios( driver: EditorDriver, scenarios: readonly EditorScenario[] ): Promise<void>
sourcefunction assertEditorDriverCapabilities( actual: EditorDriverCapabilities, required: EditorScenarioRequirements ): void
sourceconst standardEditorScenarios: readonly EditorScenario[]Backend-neutral, high-signal editing workflows shared by every backend.
sourceconst richTextEditorScenarios: readonly EditorScenario[]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.
sourceconst vectorEditorScenarios: readonly EditorScenario[]Existing-path conformance workflows for backends with authoritative vector queries.
sourceconst vectorBooleanEditorScenarios: readonly EditorScenario[]Native-only Boolean workflows shared by every backend that advertises the capability.
sourceconst accessibilityEditorDriverScenariosV1: readonly EditorScenario[]Backend-neutral accessibility acceptance scenarios for version 1 EditorDrivers.
sourcefunction runAccessibilityEditorDriverScenariosV1( driver: EditorDriver ): Promise<void>Capability-checks and runs the shared accessibility v1
EditorDriverscenarios.
The accessibility oracle scenarios check createAccessibilitySnapshotV1 and resolveAccessibilityActionAuthorityV1, or a replacement for them, without a driver.
sourceconst accessibilityOracleScenariosV1: readonly AccessibilityOracleScenarioV1[]
sourcefunction runAccessibilityOracleScenariosV1( adapter: AccessibilityOracleAdapterV1 = defaultAccessibilityOracleAdapterV1 ): voidRuns without consulting or advertising any current
EditorDrivercapability.
sourceinterface AccessibilityOracleAdapterV1createSnapshot(input: AccessibilitySnapshotInputV1): AccessibilitySnapshotV1sourceresolveAction( snapshot: AccessibilitySnapshotV1, request: AccessibilityActionRequestV1 ): AccessibilityActionAuthorityV1source
sourceinterface AccessibilityOracleScenarioV1readonly name: stringsourcerun(adapter: AccessibilityOracleAdapterV1): voidsource
sourceconst defaultAccessibilityOracleAdapterV1: AccessibilityOracleAdapterV1
Other exports
Exports not yet placed in a section above, in alphabetical order.
sourcefunction abandonVectorSubPath( network: VectorNetwork, vertexId: VectorVertexId, startedVertexId: VectorVertexId | null ): VectorNetwork | nullThe 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).
sourcefunction addPenSegment( network: VectorNetwork, shape: PenSegmentShape ): VectorNetworkAdds 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.
sourcefunction bendVectorSegment( prepared: VectorPreparedSegmentBend, delta: Point ): VectorNetworkA Bend drag on a segment: moves both handles so the point taken hold of at
tmoves bydelta, 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.
sourcefunction bendVectorVertex( prepared: VectorPreparedVertex, vertexId: VectorVertexId, heldEnd: number, drag: Point ): VectorNetworkA 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.
sourcefunction compareVectorTangentConstraints( left: VectorTangentConstraint, right: VectorTangentConstraint ): numberOrders tangent constraints the way the document schema requires.
sourcefunction constrainVectorCutLineEnd(start: Point, end: Point): PointThe 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.
sourcefunction convertVectorArcToCubics( network: VectorNetwork, segmentId: VectorSegmentId, ids: VectorEditIdAllocator ): VectorArcConversionReplaces 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.
sourcefunction cornerVectorVertex( network: VectorNetwork, vertexId: VectorVertexId ): VectorNetworkMakes 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.
sourcefunction createStandardAccessibilityEditorDriverDocumentV1(): DesignDocumentResource-complete canonical document for shared
EditorDriveracceptance. 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.
sourcefunction createStandardAccessibilityScenarioDocumentV1(): DesignDocumentCanonical document shared by semantic-oracle implementations.
sourcefunction createStandardAccessibilityScenarioGeometryV1(): SceneGeometrySnapshotGeometry uses a deliberately different order from the canonical child tree.
sourcefunction createStandardAccessibilityScenarioInputV1(): AccessibilitySnapshotInputV1
sourcefunction createStandardEditorScenarioDocument( kind: StandardEditorScenarioDocument ): DesignDocumentCanonical model documents shared by every interactive rendering backend.
sourcefunction createStandardVectorBooleanScenarioDocument( kind: StandardVectorBooleanScenarioDocument ): DesignDocument
sourcefunction createStandardVectorScenarioDocument( kind: StandardVectorScenarioDocument ): DesignDocumentCanonical existing-path documents used by every capable interactive backend.
sourceconst CROSSING_TOLERANCE: 0.000001The share of the edges’ size below which two points count as one.
sourcetype Cubic = readonly [Point, Point, Point, Point]A cubic Bézier curve as its four control points.
sourcefunction cubicLineCrossings( cubic: Cubic, from: Point, to: Point ): readonly number[]The positions
tin [0, 1] where a cubic crosses the straight cut line fromfromtoto, 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.
sourcefunction cutVectorNetworkAlongLine( network: VectorNetwork, from: Point, to: Point, ids: VectorEditIdAllocator ): VectorCutCuts the network along the straight line from
fromtoto(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.
sourcefunction cutVectorSegment( network: VectorNetwork, segmentId: VectorSegmentId, t: number, ids: VectorEditIdAllocator ): VectorCutCuts a segment at position
t(seeVectorSegmentPoint.t): a point goes in there without changing the shape, and the path breaks at it into two ends.
sourcefunction deleteAndHealVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[], allocate: VectorHealIdAllocator ): VectorNetworkDeletes the given anchors one after another, healing each as described above. Anchors an earlier heal already removed are skipped.
sourcefunction detachVectorVertex( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator ): VectorCutGives 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.
sourcefunction doubleClickSelectionAction( document: DesignDocument, rootId: NodeId, hitNodeId: NodeId, primarySelectedNodeId: NodeId | null, mode: InstanceSelectionMode ): DoubleClickSelectionAction | nullThe 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.
sourcetype DoubleClickSelectionAction = | { readonly kind: "select"; readonly nodeId: NodeId } | { readonly kind: "edit"; readonly nodeId: NodeId } | { readonly kind: "isolate"; readonly instanceId: NodeId }What a double click with the move tool does (components design 5.5).
sourcefunction findUnstoredVectorFaces( network: VectorNetwork ): readonly VectorRegionLoop[]The faces of a network (
findVectorFaces) that no region is stored for yet.
sourcefunction findVectorAreas(network: VectorNetwork): readonly VectorArea[]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
findVectorFacescovers them with loops of whole segments.
sourcefunction findVectorCrossings(network: VectorNetwork): readonly VectorCrossing[]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
atis 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.
sourcefunction findVectorFaces(network: VectorNetwork): readonly VectorRegionLoop[]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.
findVectorAreasgives the areas themselves.
sourcefunction frontFirst<Layer>(storedOrder: readonly Layer[]): readonly Layer[]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), whosearia-posinsetcounts from the front layer.
sourcefunction hitVectorPointBox( box: VectorPointBox, point: Point ): VectorPointBoxHit | nullWhat 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.
sourcefunction insertVectorPoint( network: VectorNetwork, segmentId: VectorSegmentId, t: number, ids: VectorEditIdAllocator ): VectorPointInsertionAdds a point on a segment at position
t(seeVectorSegmentPoint.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.
sourcefunction instanceLayerChildren( document: DesignDocument, id: NodeId ): readonly NodeId[]The resolved children of an instance (its component root’s layers) or of a layer inside one, in paint order; empty for anything else.
sourcefunction instanceLayerParent( document: DesignDocument, id: NodeId ): NodeId | undefinedThe parent of a layer inside an instance: the instance’s own ID for a first-level layer.
undefinedfor a stored ID or one that does not resolve.
sourcefunction instanceLayerPath( document: DesignDocument, id: NodeId ): readonly NodeId[] | undefinedThe virtual IDs from the instance’s first-level layer down to a virtual ID, outermost first. Empty for a stored ID;
undefinedfor a virtual ID that does not resolve.
sourcefunction instanceLayerResolveStats(): InstanceResolveCache["stats"]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.
sourcetype InstanceSelectionMode = "instance" | "group"The “Selecting inside instances” preference (components design 5.5).
sourcefunction invertVectorTransform( transform: TransformMatrix ): TransformMatrix | nullInverts 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.
sourcefunction isInSelectionScope( document: DesignDocument, rootId: NodeId, scopeId: NodeId, nodeId: NodeId ): booleanTrue 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).
sourcefunction isInstanceLayer(document: DesignDocument, id: NodeId): booleanTrue when
idis a stored instance or a nested instance layer selected as a whole.
sourcefunction lassoVectorSelection( network: VectorNetwork, before: VectorEditingSelection, contents: VectorLassoContents, mode: VectorLassoMode ): VectorEditingSelectionThe 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.
sourcefunction listedChildIds( document: DesignDocument, parentId: NodeId ): readonly NodeId[]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.
sourcefunction marqueeVectorSelection( network: VectorNetwork, before: VectorEditingSelection, insideVertexIds: readonly VectorVertexId[], additive: boolean ): VectorEditingSelectionThe 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.
sourcefunction mergeDeletedFaceRegions( before: VectorNetwork, after: VectorNetwork ): VectorNetworkKeeps 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.
beforeis the network just before the delete removed points and segments,afterthe one after it. Every regionbeforehas andafterlacks counts as dropped by that removal, so a region the user deleted on purpose must be gone frombeforetoo, or it could come back over the merged face.
sourcefunction nearestPointOnVectorSegment( network: VectorNetwork, segmentId: VectorSegmentId, target: Point, distanceSpace?: TransformMatrix ): VectorSegmentPointThe point on a segment nearest to
target, in network coordinates.“Nearest” is measured in network units unless
distanceSpaceis 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.
sourcefunction nudgeVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[], localDelta: Point ): VectorNetworkMoves the given anchors by one local-space offset in one network edit.
sourcefunction paintVectorTarget( network: VectorNetwork, target: VectorPaintTarget, regionId: VectorRegionId, layerShowsFill: boolean ): VectorPaintResultA 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 (
fillsomitted) 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 (
layerShowsFillfalse, seevectorFillsShow), filling asks for one on the layer (layerNeedsFill). Every other region that took the layer’s fills, and so showed nothing, is set tofills: [], 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.- A target that shows a fill loses it:
sourceconst PEN_ANGLE_STEP_DEGREES: 45Shift holds the Pen to multiples of this angle, as in Figma.
sourcefunction penArmedVertexId( network: VectorNetwork, selection: VectorEditingSelection ): VectorVertexId | nullThe 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.
sourcefunction pendingPenOutgoingHandle( network: VectorNetwork, selection: VectorEditingSelection, handle: PenOutgoingHandle | null ): PenOutgoingHandle | nullThe 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).
sourcefunction pendingVectorSubPath( network: VectorNetwork, startedVertexId: VectorVertexId | null ): VectorVertexId | nullThe started sub-path that still waits for its first segment:
startedVertexIdwhile 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.
sourceinterface PenHandleOffsetA curve handle as an offset from its point.
readonly dx: numbersourcereadonly dy: numbersource
sourceinterface PenOutgoingHandle extends PenHandleOffsetThe 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: VectorVertexIdsourcereadonly mirrorHandleId: VectorHandleId | nullsourceThe 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.
sourceinterface PenPendingHandle extends PenHandleOffsetA 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.
mirroredalso 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: VectorVertexIdsourcereadonly mirrored: booleansource
sourceinterface PenSegmentShapereadonly id: VectorSegmentIdsourcereadonly start: VectorVertexIdsourcereadonly end: VectorVertexIdsourcereadonly startHandleId: VectorHandleIdsourcereadonly endHandleId: VectorHandleIdsourcereadonly outgoing: PenOutgoingHandle | nullsourceThe handle remembered at the start point, or null when the point has none.
readonly drag: PenHandleOffset | nullsourceHow 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.
sourcefunction placeVectorVerticesOnAxis( network: VectorNetwork, localToParent: TransformMatrix, vertexIds: readonly VectorVertexId[], axis: "x" | "y", value: number ): VectorNetworkPlaces every given anchor at
valueon one parent-space axis, keeping its other coordinate, as typing into the X or Y field does with points selected.
sourcefunction pointInVectorLasso(outline: readonly Point[], point: Point): booleanWhether
pointlies inside the closed outline throughoutline(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.
sourcefunction pointInVectorPolygons( polygons: readonly (readonly Point[])[], windingRule: VectorRegion["windingRule"], point: Point ): booleanWhether a point is inside polygons filled with the given winding rule.
sourcefunction prepareVectorSegmentBend( network: VectorNetwork, segmentId: VectorSegmentId, t: number, ids: VectorEditIdAllocator ): VectorPreparedSegmentBendTurns 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
tis kept with its own position.
sourcefunction prepareVectorVertexHandles( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator ): VectorPreparedVertexTurns the segments at a vertex into cubics without changing the shape: a line gets two zero handles, and an arc becomes its cubic pieces.
sourceconst reboundVectorScenarioViewport: ViewportState
sourcefunction resetRichTextEditStats(): void
sourcefunction resolvedComponentLayersOf( document: DesignDocument, componentId: ComponentId ): ReadonlyMap<NodeId, ResolvedLayer> | undefinedThe layers of a component’s own view (
resolveComponent: the component with every property at its default), by their stored IDs.undefinedwhen 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.
sourcefunction resolvedInstanceOf( document: DesignDocument, instanceId: NodeId ): ResolvedInstance | undefinedThe resolved tree of an instance, or
undefinedwheninstanceIdis not an instance or fails to resolve.
sourcefunction resolvedLayerOf( document: DesignDocument, id: NodeId ): ResolvedLayer | undefinedThe resolved layer an ID names: the root layer for an instance ID, the layer for a virtual ID,
undefinedfor any other stored ID or an ID that does not resolve.
sourceconst richTextEditStats: { runTextConcats: number; formattingKeySorts: number }
sourcefunction scopedSelectionTarget( document: DesignDocument, rootId: NodeId, scopeId: NodeId | null, nodeId: NodeId | null ): NodeId | nullWhat 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.
sourcefunction scopedSelectionTargets( document: DesignDocument, rootId: NodeId, scopeId: NodeId | null, nodeIds: readonly NodeId[] ): readonly NodeId[]scopedSelectionTargetfor several answers: each layer once, in order, the ones outside the scope left out.
sourcefunction selectAllVectorVertices(network: VectorNetwork): VectorEditingSelectionSelects every anchor of the network, as Cmd+A does in Figma’s vector edit mode.
sourcefunction setVectorVertexMirroring( network: VectorNetwork, vertexIds: readonly VectorVertexId[], mirroring: VectorMirroring, ids: VectorEditIdAllocator ): VectorNetworkSets 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.
sourcefunction smoothVectorVertex( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator, mode: VectorTangentConstraint["mode"] = "angle-and-length" ): VectorNetworkMakes 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.
sourcefunction snapPenOffsetToAngle(offset: PenHandleOffset): PenHandleOffsetSnaps 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.
sourcefunction snapPenPointToAngle( anchor: Point, point: Point, localToCanvas: TransformMatrix ): PointMoves
pointso that the line fromanchorto it runs at a multiple of 45 degrees in canvas axes. Both points are in the path’s local units;localToCanvasmaps 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. ThrowsRangeErrorfor a transform that rule refuses, which opens no vector edit mode.pointis 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).
sourcefunction splitCubic(controls: Cubic, t: number): readonly [Cubic, Cubic]Splits a cubic at
twith de Casteljau’s construction; both halves trace it exactly.
sourceconst standardAccessibilityEditorDriverCodeV1: "const shared = 1;"
sourceconst standardAccessibilityEditorDriverIdsV1: Readonly< { document: DocumentId; replacementDocument: DocumentId; page: PageId; root: NodeId; text: NodeId; code: NodeId; shape: NodeId; } >Stable IDs used by the shared
EditorDriveraccessibility acceptance scenario.
sourceconst standardAccessibilityEditorDriverTextV1: "Shared accessibility text"
sourceconst standardAccessibilityScenarioIdsV1: Readonly< { frame: NodeId; text: NodeId; code: NodeId; image: NodeId; shape: NodeId; hidden: NodeId; clipped: NodeId; transparent: NodeId; lockedGroup: NodeId; lockedChild: NodeId; imageAsset: AssetId; } >
sourcetype StandardEditorScenarioDocument = | "blank" | "text-blank" | "rectangle" | "nested" | "snapping" | "aspect-resize" | "rotation" | "connector" | "group-scale" | "auto-layout" | "hit-order" | "hit-policy" | "rich-text"
sourceconst 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; }
sourceconst standardEditorScenarioViewport: ViewportStateThe fixed-DPR slice supported by the SVG driver.
sourceconst standardRichTextFontAssetIds: { readonly latin: AssetId; readonly hebrew: AssetId; readonly japanese: AssetId; readonly korean: AssetId; readonly emoji: AssetId; }
sourceconst standardRichTextSamples: { readonly emojiFamily: "👨👩👧👦"; readonly combiningAcute: "é"; readonly deadKeyComposed: "é"; readonly hebrew: "אבג"; readonly bidi: "abc אבג"; readonly japanese: "日本語"; readonly korean: "한글"; readonly listMarker: "・"; }
sourcetype StandardVectorBooleanScenarioDocument = | "editable" | "explicit-primary-fill" | "three-paths" | "non-path" | "locked-secondary" | "disjoint" | "text-editing"
sourceconst standardVectorBooleanScenarioIds: { readonly primary: NodeId; readonly secondary: NodeId; readonly third: NodeId; readonly nonPath: NodeId; }
sourceconst standardVectorBooleanScenarioViewport: ViewportState
sourcetype StandardVectorScenarioDocument = "editable" | "covered"
sourceconst 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; }
sourceconst standardVectorScenarioViewport: ViewportState
sourcefunction startCountingVectorWork(): voidStarts a count of comparisons at zero. For tests only.
sourcefunction startVectorSubPath( network: VectorNetwork, position: Point, vertexId: VectorVertexId ): VectorNetworkStarts a new, separate sub-path in the network: one point at
positionthat 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.
sourcefunction stopCountingVectorWork(): numberEnds the count a test started and returns it; the checks count nothing again. For tests only.
sourcefunction storeClosedLoopRegions( before: VectorNetwork, after: VectorNetwork, regionId: () => VectorRegionId ): VectorNetworkStores regions for the faces (
findVectorFaces) an edit has just made: the faces ofafterthatbeforedid 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
beforehad 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
beforehad, 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.
sourcefunction toggleVectorRegionFill( network: VectorNetwork, regionId: VectorRegionId ): VectorNetworkSwitches 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.
sourcefunction toggleVectorVertexSmooth( network: VectorNetwork, vertexId: VectorVertexId, ids: VectorEditIdAllocator ): VectorNetworkA Bend click on a point: a point with handles becomes a corner, a corner becomes smooth.
sourcefunction transformVector(transform: TransformMatrix, vector: Point): PointMaps a direction through the linear part of an affine transform (no translation).
sourcefunction transformVectorVertices( network: VectorNetwork, vertexIds: readonly VectorVertexId[], matrix: TransformMatrix ): VectorNetworkMoves 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.
sourceconst VECTOR_HIT_TOLERANCE_CSS_PX: 6How 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.
sourceconst VECTOR_POINT_BOX_HANDLE_REACH: 10How 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.
sourceconst VECTOR_POINT_BOX_ROTATE_REACH: 26How far outside a corner a press still rotates, in box units (CSS pixels).
sourceinterface VectorArcConversionThe result of turning an elliptical arc into cubic pieces.
readonly network: VectorNetworksourcereadonly segmentIds: readonly VectorSegmentId[]sourceThe pieces in order from the arc’s start to its end; the first keeps the arc’s ID.
sourceinterface VectorAreaOne area a network encloses, with every crossing treated as a corner.
readonly outline: readonly Point[]sourceThe boundary as a closed polygon in network coordinates, curves flattened.
readonly area: numbersource
sourcetype 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; })A Bend drag from a point or on a segment. Until the pointer has moved
threshold(network units, or the gesture’sdistanceSpace) it is a click: on a point, that toggles the point between corner and smooth when the press ends.
sourcefunction vectorBendHeldEnd( prepared: VectorPreparedVertex, vertexId: VectorVertexId, drag: Point, distanceSpace?: TransformMatrix ): numberWhich 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
distanceSpacedraws them (seevectorDistance): pass the path’s local-to-screen matrix so the choice matches what the user sees when the path is stretched unevenly or skewed.
sourceinterface VectorCrossingOne place where two segments of a network meet away from a point they share.
readonly point: PointsourceWhere, in network coordinates.
readonly first: { readonly segmentId: VectorSegment["id"]; readonly at: number }sourceThe 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
sourceinterface VectorCutThe cut network and the points left where the cut was made.
readonly network: VectorNetworksourcereadonly vertexIds: readonly VectorVertexId[]source
sourcetype VectorCutLineCrossing = | { readonly kind: "segment"; readonly segmentId: VectorSegmentId; readonly t: number; } | { readonly kind: "vertex"; readonly vertexId: VectorVertexId }One place the cut line crosses the network.
sourcefunction vectorCutLineCrossings( network: VectorNetwork, from: Point, to: Point ): readonly VectorCutLineCrossing[]Every place the straight line from
fromtoto(network coordinates) crosses the network: positions inside segments (seeVectorSegmentPoint.t), and the points it passes through, each listed once.
sourceinterface VectorEditIdAllocatorSupplies fresh, unused IDs for the parts an edit creates.
readonly vertexId: () => VectorVertexIdsourcereadonly segmentId: () => VectorSegmentIdsourcereadonly handleId: () => VectorHandleIdsource
sourcefunction vectorEditTargetVertexIds( network: VectorNetwork, selection: VectorEditingSelection ): readonly VectorVertexId[]Anchors moved by a nudge or a position edit: selected anchors and the ends of selected segments.
sourceinterface VectorEditToolContextreadonly tool: "pen" | "bend" | "cut"sourcereadonly ids: VectorEditIdAllocatorsourcereadonly regionId?: () => VectorRegionIdsourceNames 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.
sourcefunction vectorFillsShow(fills: readonly Paint[] | undefined): booleanWhether 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.
sourcefunction vectorHatchLines( polygons: readonly (readonly Point[])[], windingRule: VectorRegion["windingRule"], spacing: number, maximumLines = 400 ): readonly (readonly [Point, Point])[]Diagonal stripes over the inside of polygons, for marking a hovered region: straight pieces running up to the right,
spacingapart, 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 thanmaximumLinesstripes they are spread wider.
sourcetype VectorHealIdAllocator = () => VectorHealSegmentIdsSupplies fresh, unused IDs for each edge the heal creates.
sourceinterface VectorHealSegmentIdsDelete 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-lengthconstraint, 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: VectorSegmentIdsourcereadonly startHandleId: VectorHandleIdsourcereadonly endHandleId: VectorHandleIdsource
sourcefunction vectorInteractionPointBox( state: VectorInteractionState ): VectorPointBox | nullThe box around the selected points, upright on screen, or null when fewer than two points are selected or they share one place.
sourcefunction vectorLassoContents( network: VectorNetwork, localToScreen: TransformMatrix, outline: readonly Point[] ): VectorLassoContentsThe points inside a lasso drawn on screen, and the segments whose two end points are both inside.
localToScreenmaps the network to the space the outline was drawn in.
sourceinterface VectorLassoContentsThe points and segments a lasso outline takes in.
readonly vertexIds: readonly VectorVertexId[]sourcereadonly segmentIds: readonly VectorSegmentId[]source
sourcetype VectorLassoMode = "replace" | "add" | "subtract"How a lasso changes the selection that was there before the drag.
sourcetype VectorMirroring = "none" | VectorTangentConstraint["mode"]Figma’s handle mirroring setting for a point (“No mirroring”, “Mirror angle”, “Mirror angle and length”;
VectorVertex.handleMirroringin 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.
sourceinterface VectorPaintResultWhat a Paint click changes: the network, and whether the layer itself must be given a fill.
readonly network: VectorNetworksourcereadonly layerNeedsFill: booleansourceThe 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.
sourcefunction vectorPaintShows(paint: Paint): booleanWhether 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.
sourcetype VectorPaintTarget = | { readonly kind: "region"; readonly region: VectorRegion } | { readonly kind: "loop"; readonly loop: VectorRegionLoop }What a Paint click acts on: a stored region, or a face (
findVectorFaces) that has none yet.
sourcefunction vectorPaintTargetAtPoint( network: VectorNetwork, point: Point ): VectorPaintTarget | nullWhat 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.
sourcefunction vectorPaintTargetKey(target: VectorPaintTarget): stringA name for a paint target that lasts while the network keeps its IDs.
sourcefunction vectorPaintTargets( network: VectorNetwork ): readonly VectorPaintTarget[]Every paint target of a network: its regions from back to front, then its faces without a region.
sourcefunction vectorPaintTargetShape( target: VectorPaintTarget ): Pick<VectorRegion, "loops" | "windingRule">The loops and winding rule a paint target fills with.
sourcefunction vectorPaintTargetShowsFill( target: VectorPaintTarget, layerShowsFill: boolean ): booleanWhether 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.
sourcefunction vectorPointBox( network: VectorNetwork, vertexIds: readonly VectorVertexId[], localToBox: TransformMatrix ): VectorPointBox | nullThe 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.
sourceinterface VectorPointBoxreadonly vertexIds: readonly VectorVertexId[]sourceThe selected points the box surrounds, in network order.
readonly bounds: RectsourceThe box in box space. Its width or its height may be zero, not both.
readonly resizeHandles: readonly { readonly handle: VectorPointBoxHandle; readonly center: Point; }[]sourceThe 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; }[]sourceThe corners with a rotate zone outside them: four, or the two ends of a flat box.
sourcetype VectorPointBoxCorner = "nw" | "ne" | "se" | "sw"A corner of the box that points can be rotated from.
sourceinterface VectorPointBoxGestureA drag of a handle or rotate zone of the box around the selected points. The box is fixed as the press found it;
transformis what the drag applies to it so far, in the box’s own space.readonly box: VectorPointBoxsourcereadonly hit: VectorPointBoxHitsourcereadonly localToBox: TransformMatrixsourceFrom network coordinates to the box’s space (the screen), and back.
readonly boxToLocal: TransformMatrixsourcereadonly modifiers: VectorPointTransformModifierssourcereadonly transform: TransformMatrixsource
sourcetype VectorPointBoxHandle = "nw" | "n" | "ne" | "e" | "se" | "s" | "sw" | "w"A resize handle, named by the compass side of the box it sits on.
sourcetype VectorPointBoxHit = | { readonly kind: "resize"; readonly handle: VectorPointBoxHandle } | { readonly kind: "rotate"; readonly corner: VectorPointBoxCorner }
sourcefunction vectorPointBoxHitDirection( box: VectorPointBox, hit: VectorPointBoxHit ): numberThe direction a handle’s cursor runs along (resize) or faces (rotate), in degrees clockwise from the positive x axis of box space.
sourcefunction vectorPointBoxLocalTransform( boxTransform: TransformMatrix, localToBox: TransformMatrix, boxToLocal: TransformMatrix ): TransformMatrixThe 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.
sourcefunction vectorPointBoxPress( state: VectorInteractionState, screenPoint: Point, part: VectorComponentHit["part"] | null ): VectorPointBoxPress | nullWhat a press at
screenPointtakes 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.
sourceinterface VectorPointBoxPressA press on the box around the selected points: the box and the part pressed.
readonly box: VectorPointBoxsourcereadonly hit: VectorPointBoxHitsource
sourcefunction vectorPointBoxTransform( box: Pick<VectorPointBox, "bounds">, hit: VectorPointBoxHit, start: Point, current: Point, modifiers: VectorPointTransformModifiers ): TransformMatrixThe box-space matrix a drag of
hitfromstarttocurrentapplies.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.
sourceinterface VectorPointInsertionWhere a point went in: the vertex now at that place.
readonly network: VectorNetworksourcereadonly vertexId: VectorVertexIdsource
sourceinterface VectorPointTransformModifiersThe modifier keys a point box drag reads.
readonly shift: booleansourcereadonly alt: booleansource
sourceinterface VectorPreparedSegmentBendA segment ready to bend: a cubic, and where on it the pointer took hold.
readonly network: VectorNetworksourcereadonly segmentId: VectorSegmentIdsourcereadonly t: numbersource
sourceinterface VectorPreparedVertexA network whose segments at a vertex are all cubics, ready for handle edits.
readonly network: VectorNetworksourcereadonly ends: readonly VectorVertexHandleEnd[]source
sourcefunction vectorRegionAtPoint( network: VectorNetwork, point: Point ): VectorRegion | nullThe region whose area holds
point(network coordinates): the frontmost one, since regions paint from back to front. Null outside every region.
sourcefunction vectorRegionIsFilled(region: VectorRegion): booleanWhether a region paints: it takes the layer’s fills, or has fills of its own.
sourcefunction vectorRegionOutlines( network: VectorNetwork, region: Pick<VectorRegion, "loops"> ): readonly (readonly Point[])[]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.
sourcefunction vectorSegmentCubics( network: VectorNetwork, segment: VectorSegment ): readonly Cubic[]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).
sourceinterface VectorSegmentPointWhere on a segment a point is nearest.
readonly t: numbersourcePosition along the segment, 0 at its start and 1 at its end. For an arc it runs evenly over its cubic pieces: piece
iofncovers[i / n, (i + 1) / n].readonly point: Pointsourcereadonly distance: numbersourceIn network units, or in the units of the space the search measured in.
sourceinterface VectorVertexHandleEndOne segment end at a vertex, and where its segment leads.
readonly segmentId: VectorSegmentIdsourcereadonly endpoint: "startHandle" | "endHandle"sourcereadonly handleId: VectorHandleIdsourcereadonly far: VectorVertexIdsourceThe vertex at the other end of the segment.
sourcefunction vectorVertexHasHandles( network: VectorNetwork, vertexId: VectorVertexId ): booleanTrue when any segment end at the vertex has a handle of non-zero length.
sourcefunction vectorVertexIdsInScreenRect( network: VectorNetwork, localToScreen: TransformMatrix, rect: Rect ): readonly VectorVertexId[]The anchors whose screen position lies inside
rect(edges included).
sourcefunction vectorVertexIsUnconnected( network: VectorNetwork, vertexId: VectorVertexId ): booleanWhether no segment starts or ends at the point. False for a point the network does not have.
sourcefunction vectorVertexMirroring( network: VectorNetwork, vertexId: VectorVertexId ): VectorMirroring | nullA 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.
sourcefunction vectorVertexParentPositions( network: VectorNetwork, localToParent: TransformMatrix, vertexIds: readonly VectorVertexId[] ): readonly Point[]The parent-space positions of the given anchors, in network order.
sourcefunction vectorVertexSelection( network: VectorNetwork, vertexIds: Iterable<VectorVertexId>, primary?: VectorVertexId ): VectorEditingSelectionA 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 wire | 24 | RENDER_CORE_WIRE_VERSION and TACO_RENDER_WIRE_VERSION; see the wire history |
|---|---|---|
| Vector network wire | 5 | RENDER_CORE_VECTOR_NETWORK_WIRE_VERSION and TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION |
| Canvas overlay layout | 4 | the 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.
sourceclass InteractiveRenderSessionCoordinator implements InteractiveRenderSessionBackend-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
InteractiveRenderSessioninterface. It compiles the active page withActivePageCompilerSession, sends the change batches to anInteractiveRetainedBackend, 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 throwInteractiveRenderSessionBusyErrorwhile a mutation is pending; awaitwhenQueryablefirst. Afterdispose, queries and mutations throwInteractiveRenderSessionDisposedError.new (options: InteractiveRenderSessionCoordinatorOptions)sourceCreates a coordinator with no document loaded.
async whenQueryable(): Promise<void>sourceWaits through queued mutations and any backend-specific synchronous-query barrier.
async exportDocument( document: DesignDocument, pageId: PageId, options: RenderDocumentExportOptions ): Promise<RenderExportResult>sourceExports a page, or one node of it, from a cold compile of
documentwith 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[]>sourceOutlines 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 (seeStrokeOutlineResult). Abudgetis shared by every call of one command: the outline that would take the command past its bytes rejects the call withStrokeOutlineBudgetError, a step that would start after its time is used up rejects it withStrokeOutlineTimeError, 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 withStrokeOutlineTimeErrorand the backend is not asked, so no outlining is added to it.async loadDocument(load: RenderDocumentLoad): Promise<void>sourceReplaces the whole document. The active page is kept when the new document still has it; otherwise the scene is emptied until
setActivePagepicks a page.Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoNative, through
InteractiveRetainedBackend.apply:taco_render_session_applyasync applyDocumentUpdate(update: DocumentSceneUpdate): Promise<void>sourceApplies 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_infoNative, through
InteractiveRetainedBackend.apply:taco_render_session_applyasync setActivePage(pageId: PageId | null): Promise<void>sourceCompiles and shows another page, or an empty scene for
null.Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoNative, through
InteractiveRetainedBackend.apply:taco_render_session_applyasync setViewport(viewport: ViewportState): Promise<void>sourcePasses a new viewport (pan, zoom, canvas size) to the backend.
hitTest(screenPoint: Point): readonly EditorHit[]sourceHits at a screen point: overlay hit regions first, then selectable document nodes, topmost first.
Native, through
InteractiveRetainedBackend.hitTest:taco_render_session_hit_testhitTestIncludingLocked(screenPoint: Point): readonly EditorHit[]sourceThe layers painted at a screen point, topmost first, with the layers
hitTestleaves 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_testgetNodeGeometry(nodeId: NodeId): NodeGeometry | nullsourceThe committed geometry of one document node, or
nullwhen it is not in the scene.Native, through
InteractiveRetainedBackend.getNodeGeometry:taco_render_session_get_node_geometrygetSceneInfo(): RenderSceneInfosourceThe backend’s scene size, counts and generation.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infogetSceneGeometrySnapshot(): SceneGeometrySnapshotsourceThe 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_infoNative, through
InteractiveRetainedBackend.getGeometrySnapshot:taco_render_session_get_geometry_snapshotgetTextGeometry(nodeId: NodeId): TextGeometry | nullsourceCaret and selection geometry for a text node, from the backend’s text layout.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infohitTestText(queryInput: TextHitQuery): TextHitResult | nullsourceThe text position under a point in a text node.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoMoves a text position by one caret step, such as a word or a line.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infogetTextLayoutMetrics(nodeId: NodeId): InteractiveTextLayoutMetrics | nullsourceLine metrics and the suggested auto-size of a text node.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infogetFontMetadata(fontResourceId: string): RenderFontMetadataSnapshot | nullsourceThe OpenType features and variation axes of one loaded font resource.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infogetVectorGeometry(nodeId: NodeId): VectorGeometry | nullsourceThe vertices, handles, segments and regions of a vector node.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infohitTestVector(queryInput: VectorHitQuery): VectorHitTestResult | nullsourceThe vector vertex, handle, segment or region under a screen point.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infobooleanVectorPaths(request: VectorBooleanPathRequest): VectorBooleanPathResultsourceCombines 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_infogetDocumentRevision(): | { readonly document: DesignDocument; readonly commitId: CommitId } | nullsourceThe canonical document and commit the session holds now, or
nullbefore 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 | nullsourceCreates the v1 semantic mirror from TypeScript-owned canonical state and native-authoritative geometry. No backend accessibility query is used.
resolveAccessibilityActionRouteV1( binding: InteractiveAccessibilityBindingV1, request: AccessibilityActionRequestV1 ): InteractiveAccessibilityActionRouteV1sourceResolves 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_infoasync setOverlays(overlays: EditorOverlayState): Promise<void>sourceReplaces 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_overlayasync renderFrame(): Promise<EditorRenderFrameReport>sourcePaints one frame after every earlier mutation has finished.
Native, through
InteractiveRetainedBackend.getSceneInfo:taco_render_session_get_scene_infoasync getAccessibilitySnapshot(): Promise<AccessibilityNodeSnapshot | null>sourceAsks the backend for its accessibility tree for the current viewport.
dispose(): Promise<void>sourceWaits 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.
sourceinterface InteractiveRenderSessionCoordinatorOptionsOptions for
InteractiveRenderSessionCoordinator.readonly backend: InteractiveRetainedBackendsourceThe retained backend that holds the scene and paints it.
readonly viewport: ViewportStatesourceThe 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?: numbersourceGeometric body-hit expansion in CSS pixels. Defaults to four.
readonly rendererProjectionPolicy?: "nodes-only" | "workspace"sourceAllows the editor to substitute a larger workspace root around the protected page root.
readonly userLanguage?: stringsourcereadonly resolveInstance?: InstanceResolversourceSee
CompilePageOptions.resolveInstance; the canvas and export both use it.
sourceinterface InteractiveRetainedBackendLow-level retained backend used by the high-level editor coordinator.
applymust 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.disposeis retry-safe.The web app implements it with
WasmInteractiveRetainedBackend, which keeps the retained scene in aPortableWasmRenderCoreSessionand paints with WebGPU. The Native lines below name the C functions that implementation reaches through the session.readonly capabilities: InteractiveRetainedBackendCapabilitiessourceWhich optional methods this backend implements.
apply(batch: Uint8Array): RenderUpdateReport | Promise<RenderUpdateReport>sourceApplies one change batch from
RenderChangeBatchEncoderto the retained scene and reports the new generation.Native:
taco_render_session_applyapplyWithStaticImageFrames?( batch: Uint8Array, imageAssets: readonly ImageAsset[] ): RenderUpdateReport | Promise<RenderUpdateReport>sourceAtomically 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>sourceAtomically applies one scene batch and both exact media asset sets.
updatePendingVideoFrames?(): RenderUpdateReport | Promise<RenderUpdateReport>sourcePublishes the backend-owned pending video tick without changing scene identity.
getSceneInfo(): RenderSceneInfosourceThe scene size, counts, generation and digest.
getNodeGeometry(nodeId: string): RenderNodeGeometry | undefinedsourceThe bounds and transform of one retained node, or
undefinedwhen the scene has no such node.getGeometrySnapshot(): RenderGeometrySnapshotsourceThe geometry of every retained node, back to front.
hitTest( worldX: number, worldY: number, worldTolerance: number ): readonly RenderHit[]sourceTopmost-first retained body hits.
Native:
taco_render_session_hit_testwhenQueryable?(): Promise<void>sourceResolves when synchronous queries can safely enter the backend runtime.
setViewport(viewport: ViewportState): void | Promise<void>sourceSizes the render target for a new viewport.
setOverlays(overlays: EditorOverlayState): void | Promise<void>sourceReplaces 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_overlayrenderFrame(): EditorRenderFrameReport | Promise<EditorRenderFrameReport>sourcePaints the committed scene and overlays for the current viewport.
getTextGeometry?(nodeId: NodeId, viewport: ViewportState): TextGeometry | nullsourceCaret, selection and composition geometry for a text node.
Native:
taco_render_session_get_text_carets,taco_render_session_get_text_range_rectshitTestText?(query: TextHitQuery): TextHitResult | nullsourceThe text position under a point in a text node.
Moves a text position by one caret step, such as a word or a line.
getTextLayoutMetrics?(nodeId: NodeId): RenderTextLayoutMetrics | undefinedsourceLine metrics and the suggested auto-size of a text node.
getFontMetadata?(fontResourceId: string): RenderFontMetadataSnapshot | undefinedsourceThe OpenType features and variation axes of one font resource.
getVectorGeometry?( nodeId: NodeId, viewport: ViewportState ): VectorGeometry | nullsourceThe vertices, handles, segments and regions of a vector node.
hitTestVector?( query: VectorHitQuery, viewport: ViewportState ): VectorHitTestResult | nullsourceThe vector vertex, handle, segment or region under a screen point.
booleanVectorPaths?(request: VectorBooleanPathRequest): VectorBooleanPathResultsourceCombines two vector paths with a Boolean operation.
exportCompiledScene?( request: RenderCompiledExportRequest ): Promise<RenderExportResult>sourceExports 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_exportoutlineCompiledStrokes?( request: RenderCompiledStrokeOutlineRequest ): Promise<readonly StrokeOutlineResult[]>sourceOutlines 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_strokegetAccessibilitySnapshot?( viewport: ViewportState ): AccessibilityNodeSnapshot | null | Promise<AccessibilityNodeSnapshot | null>sourcedispose(): void | Promise<void>sourceReleases the backend. Safe to call again after a failure.
sourceinterface InteractiveRetainedBackendCapabilitiesWhich optional parts of
InteractiveRetainedBackenda backend implements. The coordinator checks these flags before calling an optional method.readonly staticImageFrames: booleansourceAtomically commits a retained-scene batch with its exact decoded static-image set.
readonly videoFrames: booleansourceSupports atomic mixed-scene installation and same-generation video ticks.
readonly textGeometry: booleansourcereadonly textHitTesting: booleansourcereadonly textLayoutMetrics: booleansourcereadonly fontMetadata: booleansourcereadonly vectorGeometry: booleansourcereadonly vectorHitTesting: booleansourcereadonly vectorBooleanOperations: booleansourcereadonly accessibility: booleansource
sourcetype InteractiveRenderAdvancedCapability = | "text-geometry" | "text-hit-testing" | "text-navigation" | "text-layout-metrics" | "font-metadata" | "vector-geometry" | "vector-hit-testing" | "vector-boolean-operations" | "accessibility"An optional query or feature a retained backend may lack. The coordinator throws
InteractiveRenderCapabilityErrorwith this name when the editor calls a method the backend does not support.
sourceinterface InteractiveTextLayoutMetrics extends RenderTextLayoutMetricsText layout metrics for one text node, named by its document node ID.
readonly nodeId: NodeIdsource
sourcetype VectorBooleanOperation = | "union" | "intersection" | "difference" | "exclusion"A Boolean operation on two vector paths.
sourceinterface VectorBooleanPathRequestCombines the paths of two nodes. Every vertex, segment and region ID in the result starts with
resultIdNamespacefollowed by a colon.readonly primaryNodeId: NodeIdsourcereadonly secondaryNodeId: NodeIdsourcereadonly operation: VectorBooleanOperationsourcereadonly resultIdNamespace: stringsource
sourceinterface VectorBooleanPathResultThe vector network a Boolean operation produced, for one scene generation.
readonly generation: bigintsourcereadonly primaryNodeId: NodeIdsourcereadonly secondaryNodeId: NodeIdsourcereadonly operation: VectorBooleanOperationsourcereadonly vectorNetwork: VectorNetworksource
sourceinterface StrokeOutlineRequestOne 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: NodeIdsourcereadonly strokeIndex: numbersourcereadonly resultIdNamespace: stringsource
sourcetype StrokeOutlineResult = | { readonly outlined: true; readonly vectorNetwork: VectorNetwork } | { readonly outlined: false; readonly reason: string }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.
sourceinterface RenderCompiledStrokeOutlineRequestA cold page batch and the strokes to outline in it.
readonly batch: Uint8Arraysourcereadonly strokes: readonly StrokeOutlineRequest[]sourcereadonly budget?: StrokeOutlineBudgetsourceWhat 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 withStrokeOutlineTimeError.
sourceclass InteractiveRenderCapabilityError extends ErrorStructured failure for an advanced editor surface the backend does not implement.
readonly capability: InteractiveRenderAdvancedCapabilitysourcenew (capability: InteractiveRenderAdvancedCapability)source
sourceclass InteractiveRenderTransitionError extends ErrorA rejected document-store transition; no renderer or compiler state is advanced.
readonly code: InteractiveRenderTransitionErrorCodesourcereadonly expectedCommitId: CommitId | nullsourcereadonly actualCommitId: CommitId | nullsourcenew ( code: InteractiveRenderTransitionErrorCode, message: string, commitIds: { readonly expected: CommitId | null; readonly actual: CommitId | null; } = { expected: null, actual: null } )source
sourcetype 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"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.
sourceclass InteractiveRenderSessionDisposedError extends ErrorA deterministic use-after-dispose failure.
new ()source
sourceclass InteractiveRenderSessionBusyError extends ErrorSynchronous scene queries are unavailable while a queued mutation is unsettled.
new ()source
sourceclass InteractiveRenderQueryBusyError extends ErrorA synchronous backend query attempted while its native runtime is yielding.
readonly stage: stringsourcereadonly operation: string | nullsourcenew (stage: string, operation: string | null = null)source
sourceclass InteractiveRenderResourceError extends ErrorA 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).
assetIdnames the asset.readonly assetId: stringsourcenew (assetId: string, message: string)source
sourceclass InteractiveRenderGeometryError extends ErrorA backend query returned geometry the coordinator cannot trust: a result for another node, a stale scene generation, or a value the editor cannot represent.
nodeIdnames the queried node when there is one.readonly nodeId: string | nullsourcenew (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.
sourceinterface InteractiveAccessibilityBindingV1An 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_VERSIONsourcereadonly document: DesignDocumentsourcereadonly commitId: CommitIdsourcereadonly pageId: PageIdsourcereadonly viewport: ViewportStatesourcereadonly generation: bigintsourcereadonly snapshot: AccessibilitySnapshotV1source
sourceinterface InteractiveAccessibilityActionRouteV1Pure UI intent returned only after exact context and action authority checks.
readonly version: typeof ACCESSIBILITY_SNAPSHOT_VERSIONsourcereadonly binding: InteractiveAccessibilityBindingV1sourcereadonly command: InteractiveAccessibilityActionCommandV1source
sourcetype 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"; }The UI command an accessibility action resolves to.
sourceclass InteractiveAccessibilityContextError extends ErrorA semantic action whose coordinator-owned scene identity is no longer current.
new (readonly code: InteractiveAccessibilityContextErrorCode, message: string)source
sourcetype InteractiveAccessibilityContextErrorCode = | "INVALID_BINDING" | "DOCUMENT_IDENTITY_MISMATCH" | "COMMIT_MISMATCH" | "ACTIVE_PAGE_MISMATCH" | "VIEWPORT_IDENTITY_MISMATCH" | "GENERATION_MISMATCH" | "CANONICAL_NODE_MISSING" | "EDIT_TARGET_MISMATCH"Why
InteractiveAccessibilityContextErrorwas 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.
sourceinterface RenderSceneInfoThe retained scene’s size in document units, its node and resource counts, and the generation and digest that identify its current contents.
Native:
TacoRenderSceneInforeadonly width: numbersourcereadonly height: numbersourcereadonly nodeCount: numbersourcereadonly resourceCount: numbersourcereadonly generation: bigintsourcereadonly sceneDigest: bigintsource
sourceinterface RenderUpdateReportThe result of applying one batch: counts, the new scene generation and digest, and the area that changed.
Native:
TacoRenderUpdateReportreadonly commandsApplied: numbersourcereadonly nodeCount: numbersourcereadonly resourceCount: numbersourcereadonly generation: bigintsourcereadonly sceneDigest: bigintsourcereadonly dirtyBounds: RenderDirtyBoundssource
sourceinterface RenderDirtyBoundsThe world-space area the last batch changed: the old and new bounds of the changed nodes and their union.
Native:
TacoRenderDirtyBoundsreadonly oldBounds?: RenderRectsourceDirty rectangles are scene/world-space bounds.
readonly newBounds?: RenderRectsourcereadonly combinedBounds?: RenderRectsource
sourceinterface RenderRectreadonly x: numbersourcereadonly y: numbersourcereadonly width: numbersourcereadonly height: numbersource
sourceinterface RenderNodeGeometryThe bounds, transform, paint order and visibility of one retained node.
Native:
TacoRenderNodeGeometry,TacoRenderGeometryRecordreadonly id: stringsourcereadonly kind: RetainedNodeRecord["kind"]sourcereadonly localBounds?: RenderRectsourcereadonly worldBounds?: RenderRectsourcereadonly visibleWorldBounds?: RenderRectsourceWorld-space bounds after visibility and ancestor clips are applied.
readonly subtreeVisibleBounds?: RenderRectsourceThe union of
visibleWorldBoundsover 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: Transformsourcereadonly painterOrder: bigintsourcereadonly effectivelyVisible: booleansourcereadonly effectiveOpacity: numbersource
sourceinterface RenderGeometrySnapshotOne immutable, back-to-front geometry view for a committed scene generation.
readonly generation: bigintsourcereadonly nodes: readonly RenderNodeGeometry[]source
sourceinterface RenderHitOne node under a hit-test point.
Native:
TacoRenderHitreadonly id: stringsourcereadonly kind: RetainedNodeRecord["kind"]sourcereadonly painterOrder: bigintsourcereadonly visibleWorldBounds: RenderRectsource
sourceinterface ViewportMappingOptionsHow to fit the scene into a viewport, for
RenderCoreSession.mapViewport.Native:
TacoRenderViewportOptionsreadonly viewportWidth: numbersourcereadonly viewportHeight: numbersourcereadonly mode: "fit" | "fill" | "stretch"sourcereadonly alignX?: numbersourceZero aligns to the leading edge and one to the trailing edge.
readonly alignY?: numbersource
sourceinterface ViewportMappingThe transforms between scene and viewport coordinates, and the scene’s bounds inside the viewport. For a scene of size and a viewport of size , the scales start as and ;
fitsets both to ,fillto , andstretchkeeps them. With alignment (default 0.5):and
sceneViewportBoundsis .Native:
TacoRenderViewportMappingreadonly sceneToViewport: Transformsourcereadonly viewportToScene: Transformsourcereadonly sceneViewportBounds: RenderRectsource
sourceinterface RgbaRenderTargetreadonly kind: "rgba"sourcereadonly width: numbersourcereadonly height: numbersourcereadonly rowBytes: numbersourcereadonly pixels: Uint8Arraysource
sourceinterface RenderFrameReportreadonly backend: "skia-cpu" | "skia-webgpu"sourcereadonly width: numbersourcereadonly height: numbersource
sourceinterface RenderTextPositionA paragraph-relative authored text position. Offsets count UTF-16 code units.
readonly paragraphIndex: numbersourcereadonly utf16Offset: numbersourcereadonly affinity: RenderTextAffinitysource
sourcetype RenderTextAffinity = "upstream" | "downstream"Which side of a line break or bidirectional boundary a caret belongs to.
Native:
TacoRenderTextAffinity
sourceinterface RenderTextRangeAnchor and focus preserve the authored selection direction.
readonly anchor: RenderTextPositionsourcereadonly focus: RenderTextPositionsource
sourceinterface RenderTextCaretGeometryreadonly position: RenderTextPositionsourcereadonly localBounds: RenderRectsource
sourceinterface RenderTextCaretSnapshotreadonly generation: bigintsourcereadonly carets: readonly RenderTextCaretGeometry[]source
sourceinterface RenderTextRangeGeometryreadonly generation: bigintsourcereadonly localRects: readonly RenderRect[]source
sourceinterface RenderTextHitResultreadonly generation: bigintsourcereadonly position: RenderTextPositionsourcereadonly inside: booleansource
One caret movement for
RenderCoreSession.navigateText. The C codes follow this order, from 0.
sourceinterface RenderTextLayoutMetricsreadonly generation: bigintsourcereadonly autoResize: RetainedTextNode["autoResize"]sourcereadonly frameWidth: numbersourcereadonly frameHeight: numbersourcereadonly contentWidth: numbersourcereadonly contentHeight: numbersourcereadonly suggestedWidth: numbersourcereadonly suggestedHeight: numbersourcereadonly paragraphCount: numbersourcereadonly lineCount: numbersourcereadonly caretCount: numbersource
sourceinterface RenderFontMetadataSnapshotreadonly fontResourceId: stringsourcereadonly generation: bigintsourcereadonly featureTags: readonly string[]sourcereadonly axes: readonly RenderFontAxisMetadata[]source
sourceinterface RenderFontAxisMetadatareadonly tag: stringsourcereadonly minimum: numbersourcereadonly defaultValue: numbersourcereadonly maximum: numbersource
sourceinterface RenderVectorGeometrySnapshotreadonly generation: bigintsourcereadonly vertices: readonly RenderVectorVertexGeometry[]sourcereadonly handles: readonly RenderVectorCubicHandleGeometry[]sourcereadonly segments: readonly RenderVectorSegmentGeometry[]sourcereadonly regions: readonly RenderVectorRegionGeometry[]source
sourceinterface RenderVectorVertexGeometryreadonly kind: "vertex"sourcereadonly id: stringsourcereadonly localPoint: { readonly x: number; readonly y: number }sourcereadonly screenPoint: { readonly x: number; readonly y: number }source
sourceinterface RenderVectorCubicHandleGeometryreadonly kind: "cubic-handle"sourcereadonly id: stringsourcereadonly segmentId: stringsourcereadonly vertexId: stringsourcereadonly endpoint: "start" | "end"sourcereadonly localPoint: { readonly x: number; readonly y: number }sourcereadonly screenPoint: { readonly x: number; readonly y: number }source
sourcetype RenderVectorSegmentGeometry = | RenderVectorLineSegmentGeometry | RenderVectorCubicSegmentGeometry | RenderVectorArcSegmentGeometry
sourceinterface RenderVectorLineSegmentGeometry extends RenderVectorSegmentGeometryBasereadonly kind: "line"source
sourceinterface RenderVectorCubicSegmentGeometry extends RenderVectorSegmentGeometryBasereadonly kind: "cubic"sourcereadonly startHandleId: stringsourcereadonly endHandleId: stringsource
sourceinterface RenderVectorArcSegmentGeometry extends RenderVectorSegmentGeometryBasereadonly kind: "elliptical-arc"sourcereadonly radiusX: numbersourcereadonly radiusY: numbersourcereadonly xAxisRotation: numbersourcereadonly largeArc: booleansourcereadonly sweep: booleansource
sourceinterface RenderVectorRegionGeometryreadonly kind: "region"sourcereadonly id: stringsourcereadonly windingRule: "nonzero" | "evenodd"sourcereadonly localBounds: RenderRectsourcereadonly screenBounds: RenderRectsource
sourceinterface RenderVectorHitQueryreadonly screenPoint: { readonly x: number; readonly y: number }sourcereadonly toleranceCssPx: numbersourcereadonly documentToScreen: Transformsource
sourceinterface RenderVectorHitSnapshotreadonly generation: bigintsourcereadonly hits: readonly RenderVectorHit[]source
sourcetype 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" })
sourcetype RenderVectorBooleanOperation = | "union" | "intersection" | "difference" | "exclusion"A Boolean operation on two vector paths. The C codes are union 1, intersection 2, difference 3, exclusion 4.
Native:
TacoRenderVectorBooleanOperation
sourceinterface RenderVectorBooleanPathRequestreadonly primaryNodeId: stringsourcereadonly secondaryNodeId: stringsourcereadonly operation: RenderVectorBooleanOperationsourcereadonly resultIdNamespace: stringsource
sourceinterface RenderVectorBooleanPathSnapshotreadonly generation: bigintsourcereadonly vectorNetwork: VectorNetworkRecordsource
sourceinterface RenderStrokeOutlineRequestAsks for one stroke of a path or container node as the filled shape it paints.
strokeIndexcounts the node’s strokes in paint order, hidden ones included, which is the stroke’s index in the model’sVisualStyle.strokes. Every vertex, segment, handle and region ID of the result starts withresultIdNamespacefollowed by a colon.Native:
TacoRenderStrokeOutlineQueryreadonly nodeId: stringsourcereadonly strokeIndex: numbersourcereadonly resultIdNamespace: stringsource
sourceinterface RenderStrokeOutlineSnapshotThe 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:
TacoRenderStrokeOutlineResultreadonly generation: bigintsourcereadonly 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.
sourceinterface RenderSelectionChromeThe selection outlines and transform handles the renderer paints over the scene.
RenderCoreSession.setSelectionChromeencodes it into the byte layout thatSessionCache.setSelectionChromereads.Native:
taco_render_session_set_selection_chrome,SessionCache.setSelectionChromereadonly items: readonly RenderSelectionChromeItem[]sourcereadonly strokeRgba: numbersourceDefault colours of items that give none, 0xRRGGBBAA.
readonly handleFillRgba: numbersourcereadonly devicePixelRatio: numbersource
sourceinterface RenderSelectionChromeItemOne 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 | nullsourceNull places x and y (and an outline’s width and height) in scene coordinates.
readonly kind: "outline" | "square-handle" | "round-handle"sourcereadonly hover?: booleansourcereadonly dashed?: booleansourcereadonly x: numbersourceAn 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: numbersourcereadonly width?: numbersourcereadonly height?: numbersourcereadonly angle?: numbersourceA handle’s turn on screen in radians, clockwise (screen y points down). 0 when omitted.
readonly strokeRgba?: numbersource0xRRGGBBAA; the chrome’s
strokeRgbawhen omitted.readonly fillRgba?: numbersourceA handle’s fill, 0xRRGGBBAA; the chrome’s
handleFillRgbawhen omitted.readonly offsetX?: numbersourceScene-space offset for a preview the retained scene does not hold.
readonly offsetY?: numbersourcereadonly sceneTransform?: TransformsourceLocal-to-scene matrix of a scene-anchored item (
nodeIdnull), so a preview of a rotated node keeps a rotated outline. Identity when omitted.readonly nodeTransform?: TransformsourceItem-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.
sourcefunction encodeSelectionChrome(chrome: RenderSelectionChrome): Uint8ArrayEncodes the selection chrome in the layout
taco_render_session_set_selection_chromereads (version 3, wire version 20; seerender_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
sourceinterface RenderCanvasOverlayEditor 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 | nullsourceThe 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: numbersourcereadonly grid: RenderOverlayGrid | nullsourcereadonly lines: readonly RenderOverlayLine[]sourcereadonly marks: readonly RenderOverlayMark[]sourcereadonly titles?: readonly RenderOverlayTitle[]sourceNames drawn above top-level frames. Absent means none.
readonly rulers?: RenderOverlayRulerssourceThe rulers along the top and left edges. Absent means none.
readonly boxes?: readonly RenderOverlayBox[]sourceFilled, hatched or outlined boxes, such as auto layout gaps. Absent means none.
readonly handles?: readonly RenderOverlayHandle[]sourceShort ticks with a border, such as auto layout spacing handles. Absent means none.
readonly badges?: readonly RenderOverlayBadge[]sourceSmall value pills, such as the value of a spacing drag. Absent means none.
sourcefunction encodeCanvasOverlay(overlay: RenderCanvasOverlay): Uint8ArrayEncodes the canvas overlay in the layout
taco_render_session_set_canvas_overlayreads (version 4; seerender_core.h). An empty overlay encodes as no bytes.SessionCache.setCanvasOverlaydecodes it.Native:
taco_render_session_set_canvas_overlay,SessionCache.setCanvasOverlay
sourceinterface RenderOverlayLineOne straight overlay line, drawn over whole device pixels.
readonly orientation: "vertical" | "horizontal"sourceA vertical line has a constant x; a horizontal one a constant y.
readonly side: RenderOverlayLineSidesourcereadonly position: numbersourcex of a vertical line, y of a horizontal one.
readonly start: number | nullsourceWhere the line starts and ends along its length;
nullcrosses the whole canvas.readonly end: number | nullsourcereadonly rgba: numbersource0xRRGGBBAA.
readonly dash?: { readonly dashCssPx: number; readonly gapCssPx: number }sourceDash and gap lengths in CSS pixels; omitted for a solid line.
readonly scene?: booleansourceScene coordinates instead of coordinates local to the overlay’s anchor.
readonly anchorNodeId?: stringsourceA 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?: booleansourceRun along the anchor node’s own box (0 to its width or height); start and end are ignored.
sourcetype RenderOverlayLineSide = "after" | "before" | "centered"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.
sourceinterface RenderOverlayMarkAn anti-aliased x centered where two overlay lines cross.
readonly x: numbersourcereadonly y: numbersourcereadonly xSide: RenderOverlayLineSidesourceThe sides of the crossing lines: the x is centered on their pixels.
readonly ySide: RenderOverlayLineSidesourcereadonly halfSizeCssPx: numbersourcereadonly rgba: numbersourcereadonly scene?: booleansource
sourceinterface RenderOverlayGridA line after every
stepunits from the origin, across the whole canvas.readonly originX: numbersourcereadonly originY: numbersourcereadonly step: numbersourcereadonly 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.
sourceinterface RenderOverlayBoxA 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?: stringsourceOmitted uses the overlay’s anchor.
readonly x: numbersourcereadonly y: numbersourcereadonly width: numbersourcereadonly height: numbersourcereadonly fillRgba?: numbersource0xRRGGBBAA; omitted draws no fill, hatching or outline.
readonly hatchRgba?: numbersourcereadonly outlineRgba?: numbersource
sourceinterface RenderOverlayHandleA 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?: stringsourcereadonly x: numbersourcereadonly y: numbersourcereadonly axis: "x" | "y"source“x” runs along the anchor’s local x axis, “y” along its y axis.
readonly lengthCssPx: numbersourcereadonly thicknessCssPx: numbersourcereadonly rgba: numbersource0xRRGGBBAA.
readonly borderRgba: numbersource
sourceinterface RenderOverlayBadgeA 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?: stringsourcereadonly x: numbersourcereadonly y: numbersourcereadonly offsetXCssPx: numbersourcereadonly offsetYCssPx: numbersourcereadonly fontSizeCssPx: numbersourcereadonly text: stringsourcereadonly backgroundRgba: numbersource0xRRGGBBAA.
readonly textRgba: numbersource
sourceinterface RenderOverlayRulersThe 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: numbersourceThe rulers’ thickness.
readonly fontSizeCssPx: numbersourcereadonly minimumMajorCssPx: numbersourceMajor ticks are a 1-2-5 step at least this far apart.
readonly backgroundRgba: numbersource0xRRGGBBAA colours.
readonly borderRgba: numbersourcereadonly inkRgba: numbersourcereadonly selectionFillRgba: numbersourcereadonly selectionInkRgba: numbersourcereadonly selectionX: readonly RenderOverlayRulerSpan[]sourceSorted extents that do not touch; a zero-length one marks a single value.
readonly selectionY: readonly RenderOverlayRulerSpan[]sourcereadonly labels: readonly RenderOverlayRulerLabel[]sourceAt most 16.
sourceinterface RenderOverlayRulerSpanAn extent on a ruler, in the overlay anchor’s units.
readonly start: numbersourcereadonly end: numbersource
sourceinterface RenderOverlayRulerLabelA 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: numbersourceIn the overlay anchor’s units.
readonly backgroundRgba: numbersource0xRRGGBBAA.
readonly textRgba: numbersource
sourceinterface RenderOverlayTitleThe 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: stringsourcereadonly text: stringsourcereadonly rgba: numbersource0xRRGGBBAA.
readonly fontSizeCssPx: numbersourceThe font size, which is also the height of the title’s line box.
readonly gapCssPx: numbersourceSpace between the bottom of the line box and the frame’s top edge.
sourceinterface RenderTextEditingOverlayOne generation-stamped transient selection, caret, and optional IME range.
readonly selection: RenderTextRangesourcereadonly composition?: RenderTextRangesourcereadonly caretVisible: booleansource
sourceinterface RenderVectorEditingOverlayreadonly network: VectorNetworkRecordsourcereadonly selection: RenderVectorEditingSelectionsourcereadonly pendingHandle?: RenderVectorPendingHandle | nullsourceThe Pen’s pending handle, when there is one.
readonly viewport: { readonly documentToScreen: Transform; readonly screenWidthCssPx: number; readonly screenHeightCssPx: number; readonly screenPixelDensity: number; }source
sourceinterface RenderVectorEditingSelectionThe selected vector components by kind, and the primary one (or
null).readonly vertexIds: readonly string[]sourcereadonly handleIds: readonly string[]sourcereadonly segmentIds: readonly string[]sourcereadonly regionIds: readonly string[]sourcereadonly primary: RenderVectorEditingComponent | nullsource
sourcetype 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 }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.
sourceinterface RenderDocumentExportOptionsWhat
InteractiveRenderSessionCoordinator.exportDocumentexports and how.readonly format: RenderExportFormatsourcereadonly scale?: numbersourceOutput units per document unit. Defaults to 1.
readonly rootNodeId?: stringsourceExports this node’s visible bounds and subtree; omitted means the page.
readonly options?: RenderExportOptionssource
sourceinterface RenderExportOptionsExport choices beyond the format, scale and root.
readonly fixedSize?: { readonly dimension: "width" | "height"; readonly value: number; }sourceA 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?: booleansourceWith a root, paint every visible node inside the root’s bounds, not just the root’s subtree, so overlapping layers are included.
readonly imageSampling?: RenderExportImageSamplingsourcereadonly svgNodeMarkers?: booleansourcereadonly pdfRasterDpi?: numbersourcereadonly maxPixels?: numbersourceThe most pixels a raster export (
rgbaorpng) may have. An export over it is refused withRenderExportTooLargeErroronce 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.
sourceinterface RenderCompiledExportRequestA cold page batch plus what a fresh session needs to paint and export it.
readonly batch: Uint8Arraysourcereadonly imageAssets: readonly ImageAsset[]sourceEvery image asset the batch references, for decoding into frames.
readonly format: RenderExportFormatsourcereadonly scale: numbersourceOutput units per document unit.
readonly rootNodeId?: stringsourceExports this node’s visible bounds and subtree; omitted means the page.
readonly options?: RenderExportOptionssourceOther options for the export itself; see
RenderExportOptions.
sourceinterface RenderExportRequestOne rectangular scene region painted through the shared painter. Editing overlays never export and the background stays transparent.
Native:
TacoRenderExportRequestreadonly format: RenderExportFormatsourcereadonly x: numbersourceThe region in scene coordinates.
readonly y: numbersourcereadonly width: numbersourcereadonly height: numbersourcereadonly scale?: numbersourceOutput units per scene unit: raster pixels, SVG user units, PDF points. Defaults to 1.
readonly rootNodeId?: stringsourceWhen set, only this node and its descendants paint.
readonly imageSampling?: RenderExportImageSamplingsourceHow image fills and media frames are resampled; the screen’s “linear” by default.
readonly svgNodeMarkers?: booleansourceSVG only: surround each painted node’s elements with node markers. See
PROTOCOL.md.readonly pdfRasterDpi?: numbersourcePDF only: the DPI at which blurs and shadows are rasterized; 72 by default.
sourceinterface RenderExportResultThe bytes of one export and their size.
Native:
TacoRenderExportResultreadonly format: RenderExportFormatsourcereadonly bytes: Uint8ArraysourceRGBA rows are tightly packed and unpremultiplied; other formats are whole files.
readonly pixelWidth: numbersourceceil(region * scale); raster formats use it as their pixel size.
readonly pixelHeight: numbersourcereadonly rowBytes: numbersourceBytes per RGBA row; zero for every other format.
sourcetype RenderExportFormat = "rgba" | "png" | "svg" | "pdf"Scene export targets. PNG needs a build with a PNG encoder; RGBA never does.
Native:
TacoRenderExportFormat
sourcetype RenderExportImageSampling = "linear" | "cubic" | "nearest"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.
sourcefunction compilePageBatch( document: DesignDocument, pageId: PageId, options: CompilePageOptions ): CompiledPageBatchCompiles 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
RenderDocumentCompileErrorrather than disappearing.
sourcefunction compileIncrementalPageBatch( previous: DesignDocument, current: DesignDocument, changes: ChangeSet, pageId: PageId, options: CompilePageOptions ): CompiledPageUpdateCompiles 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.
sourceclass ActivePageCompilerSessionStateful 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)sourceCreates an empty session; call
resetbeforeupdate.get initialized(): booleansourceWhether
resethas compiled a page.reset(document: DesignDocument, pageId: PageId): CompiledActivePageBatchsourceCompiles and caches a complete active page, including the reset command.
update( document: DesignDocument, documentChanges: ChangeSet ): CompiledActivePageUpdatesourceCompiles a validated transition using its authoritative changed-entity index. Unchanged siblings are neither traversed nor converted.
auditAgainstColdCompilation( document: | DesignDocument | undefined = this.#active?.document ): ActivePageAuditReportsourcePerforms the deliberately O(document) correctness audit outside
update().clear(): voidsourceDrops the cached page; the next call must be
reset.
sourceinterface CompilePageOptionsWhere the page compiler gets asset bytes and language preferences.
readonly getAssetBytes: (asset: Asset) => Uint8Array | undefinedsourceSupplies 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[] | undefinedsourceSupplies 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?: stringsourceThe 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; seepreferredCjkFamily.readonly resolveInstance?: InstanceResolversourceDerives an instance’s layers (design 4.1). Defaults to
resolveInstancefrom@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.
sourceinterface CompileFontSliceOne 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: stringsourceUnique within its asset. It must not contain “#”.
readonly unicodeRanges: readonly (readonly [number, number])[]sourceSorted, non-overlapping inclusive code point ranges the file covers.
readonly bytes: Uint8Arraysource
sourceinterface CompiledPageBatchA complete page as one change batch that resets the retained scene.
readonly batch: Uint8ArraysourceThe encoded batch, ready for
RenderCoreSession.apply.readonly nodeIds: readonly NodeId[]sourceEvery compiled node, in traversal order.
readonly mediaAssetIds: readonly string[]sourceImage and video assets the scene uses; the host attaches their frames.
readonly resourceIds: readonly string[]sourceWire resource IDs registered in the batch (fonts and font files).
sourcetype CompiledPageUpdate = CompiledIncrementalPageBatch | PageFullReloadRequiredThe result of
compileIncrementalPageBatch.
sourceinterface CompiledIncrementalPageBatchA page change encoded as a batch that updates the retained scene in place.
readonly kind: "incremental"sourcereadonly batch: Uint8ArraysourceA batch with no reset command, including for an unchanged page.
readonly operations: PageIncrementalOperationssourcereadonly nodeIds: readonly NodeId[]sourcereadonly mediaAssetIds: readonly string[]sourcereadonly resourceIds: readonly string[]source
sourceinterface PageIncrementalOperationsThe commands an incremental batch contains, by node and resource ID.
readonly removeNodeIds: readonly NodeId[]sourceTop-level previous subtrees removed by wire commands.
readonly upsertNodeIds: readonly NodeId[]sourceCurrent nodes inserted or replaced in canonical traversal order.
readonly transformNodeIds?: readonly NodeId[]sourceCurrent nodes whose retained payload is unchanged except for transform.
readonly registerResourceIds: readonly string[]sourcereadonly removeResourceIds: readonly string[]source
sourceinterface PageFullReloadRequiredThe change cannot be sent incrementally; compile the whole page again.
readonly kind: "full-reload-required"sourcereadonly reasons: readonly PageFullReloadReason[]source
sourceinterface PageFullReloadReasonOne reason a page needs a full reload, with a readable message.
readonly code: PageFullReloadReasonCodesourcereadonly message: stringsource
sourcetype PageFullReloadReasonCode = | "PREVIOUS_PAGE_NOT_FOUND" | "CURRENT_PAGE_NOT_FOUND" | "PAGE_ROOT_RESIZED" | "PAGE_ROOT_CHANGED" | "THEME_CHANGED" | "PAGE_VARIABLE_MODES_CHANGED"Why an incremental update was refused and the page must be compiled again.
sourceinterface CompiledActivePageBatch extends CompiledPageBatchA full page batch from
ActivePageCompilerSession.reset, with work counters.readonly metrics: ActivePageCompileMetricssource
sourceinterface CompiledActivePageIncrementalBatch extends CompiledIncrementalPageBatchAn incremental batch from
ActivePageCompilerSession.update, with work counters.readonly metrics: ActivePageCompileMetricssource
sourcetype CompiledActivePageUpdate = | CompiledActivePageIncrementalBatch | PageFullReloadRequiredThe result of
ActivePageCompilerSession.update.
sourceinterface ActivePageCompileMetricsDeterministic work counters reported by the stateful active-page compiler.
readonly nodesInspected: numbersourceCanonical nodes selected by the validated change set and hierarchy index.
readonly nodesCompiled: numbersourceSelected nodes converted from the canonical model.
readonly nodesReused: numbersourceReserved for accepted cached records selected without recompilation.
readonly nodesUpserted: numbersourceNode upsert commands encoded into this batch.
readonly resourcesResolved: numbersourcePlatform asset resolutions performed during this compilation.
readonly resourcesRegistered: numbersourceResource payloads encoded into this batch.
readonly resourcesRemoved: numbersourcereadonly batchBytes: numbersource
sourceinterface ActivePageAuditReportThe result of
ActivePageCompilerSession.auditAgainstColdCompilation: whether the cached records match a fresh compile, and which ones do not.readonly matchesColdCompilation: booleansourcereadonly nodesInspected: numbersourcereadonly mismatchedNodeIds: readonly NodeId[]sourcereadonly mismatchedResourceIds: readonly string[]source
sourceclass RenderDocumentCompileError extends ErrorA deliberate boundary error; unsupported model data must never disappear.
readonly code: RenderDocumentCompileErrorCodesourcereadonly nodeId: NodeId | undefinedsourcenew (code: RenderDocumentCompileErrorCode, message: string, nodeId?: NodeId)source
sourcetype 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"
sourcefunction compileRetainedPaint( paint: Paint, nodeId: NodeId, label: string, document?: DesignDocument ): RetainedPaintRecordLowers 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.
sourcefunction parseRetainedColor(value: string, nodeId: NodeId): RgbaColorParses the deterministic CSS-color subset accepted by the first shared wire.
sourcefunction fontSliceResourceId(assetId: string, sliceId: string): stringThe wire resource ID of one file of a font asset split by Unicode coverage.
sourcefunction fontResourceAsset( document: DesignDocument, resourceId: string ): Asset | undefinedThe 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.
sourcefunction textNodeCodePoints(node: TextualNode): Set<number>Every code point a text node can draw: its runs, explicit list markers, and the characters of generated markers (“\u2022” and “1.”).
sourceconst RETAINED_IDENTITY_TRANSFORM: Transform
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.
sourcefunction connectorLineGeometry( document: DesignDocument, node: Extract<SceneNode, { type: "connector" }>, parentId: NodeId | undefined ): ConnectorLineGeometryThe line
nodedraws, 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.parentIdis the connector’s parent. Throws the compiler’sRenderDocumentCompileErrorwhen an attachment cannot be drawn.
sourceinterface ConnectorLineGeometryThe line a connector draws, in the connector’s own coordinates.
readonly start: { readonly x: number; readonly y: number }sourceWhere the line starts, after an attached start is placed on its layer’s edge.
readonly end: { readonly x: number; readonly y: number }sourceWhere 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; }sourceThe box around the whole line, bends and curves included, without stroke or end caps.
sourcefunction connectorRoutePoints( node: Extract< SceneNode, { type: "connector"; } >, curveSteps = 24, document?: DesignDocument ): readonly { readonly x: number; readonly y: number }[]The route
nodedraws, as points along it in the connector’s own coordinates, from start to end: the corners of a bent route, and each curve split intocurveStepsstraight 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.
sourcetype RetainedNodeRecord = | RetainedPathNode | RetainedTextNode | RetainedContainerNode | RetainedMediaNode
sourceinterface RetainedNodeBasereadonly id: stringsourcereadonly parentId?: stringsourcereadonly zOrder: numbersourcereadonly visible: booleansourcereadonly blendMode?: RetainedBlendModesourceWire version 10 composites a node with a non-normal blend mode through one isolated layer. Omitted means normal.
readonly mask?: booleansourceWire 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: Transformsourcereadonly style: RetainedNodeStylesource
sourceinterface RetainedNodeStylereadonly opacity: numbersourcereadonly fill?: RgbaColorsourceThe compact common-node record carries
fill. Wire version 5 can instead appendfillsto a node upsert for an exact ordered paint list. The two representations are mutually exclusive.readonly fills?: readonly RetainedPaintRecord[]sourcereadonly strokes?: readonly RetainedStrokeRecord[]sourceOrdered 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[]sourceWire version 6 appends ordered shadows; wire version 9 adds inner ones.
readonly layerBlur?: numbersourceWire 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?: numbersourceWire 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.
sourceinterface RetainedContainerNode extends RetainedNodeBaseA renderer-neutral hierarchy and clipping node introduced by wire version 2.
readonly kind: "container"sourcereadonly width: numbersourcereadonly height: numbersourcereadonly clip: booleansourcereadonly cornerRadius?: numbersourceWire 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]sourceWire 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?: numbersourceCorner smoothing from 0 (circular arcs) to 1 (continuous curvature). Each smoothed corner consumes up to
(1 + smoothing) * radiusof edge, capped at half the shorter side. Zero or omitted keeps circular arcs.
sourceinterface RetainedPathNode extends RetainedNodeBasereadonly kind: "path"sourcereadonly vectorNetwork: VectorNetworkRecordsourcereadonly vertexCornerRadii?: readonly RetainedVertexCornerRadius[]sourceWire 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?: RetainedStrokeCapssourceWire 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.
sourceinterface RetainedTextNode extends RetainedNodeBasereadonly kind: "text"sourcereadonly width: numbersourcereadonly height: numbersourcereadonly wrap: "none" | "word" | "character"sourcereadonly verticalAlign: "top" | "middle" | "bottom"sourcereadonly overflow: "clip" | "visible"sourcereadonly autoResize: "none" | "height" | "width-and-height"sourcereadonly styles: readonly RichTextStyleRecord[]sourcereadonly paragraphs: readonly RichTextParagraphRecord[]source
sourceinterface RetainedMediaNode extends RetainedNodeBaseScene-only media metadata introduced by wire version 4.
readonly kind: "media"sourcereadonly mediaKind: "image" | "video"sourcereadonly fit: "contain" | "cover" | "fill"sourcereadonly width: numbersourcereadonly height: numbersourcereadonly assetId: stringsourcereadonly crop?: { readonly x: number; readonly y: number; readonly width: number; readonly height: number; }source
sourcetype Transform = readonly [ a: number, b: number, c: number, d: number, tx: number, ty: number ]An affine matrix in the same
[a, b, c, d, tx, ty]layout as the model’sTransformMatrix. It maps a point as .
sourcetype RgbaColor = readonly [ red: number, green: number, blue: number, alpha: number ]
sourcetype RetainedBlendMode = | "normal" | "darken" | "multiply" | "color-burn" | "lighten" | "screen" | "color-dodge" | "overlay" | "soft-light" | "hard-light" | "difference" | "exclusion" | "hue" | "saturation" | "color" | "luminosity"Wire version 10 blend modes, in CSS
mix-blend-modeterms. The wire codes (0 to 15 in this order) are the C++BlendModevalues.Native:
BlendMode
sourcetype RetainedPaintRecord = | RetainedSolidPaintRecord | RetainedLinearGradientPaintRecord | RetainedRadialGradientPaintRecord | RetainedAngularGradientPaintRecord | RetainedImagePaintRecordOne fill or stroke paint. The wire kind codes (solid 1, linear 2, radial 3, angular 4, image 5) are the C++
PaintKindvalues.Native:
PaintKind,RetainedPaint
sourceinterface RetainedPaintCompositingWire 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.
The renderer skips a hidden paint but keeps its place in the list.
readonly blendMode?: RetainedBlendModesourceBlends 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.
sourceinterface RetainedSolidPaintRecord extends RetainedPaintCompositingreadonly kind: "solid"sourcereadonly color: RgbaColorsourcereadonly opacity: numbersource
sourceinterface RetainedLinearGradientPaintRecord extends RetainedPaintCompositingreadonly kind: "linear-gradient"sourcereadonly angle: numbersourcereadonly stops: readonly { readonly offset: number; readonly color: RgbaColor; }[]sourcereadonly opacity: numbersource
sourceinterface RetainedRadialGradientPaintRecord extends RetainedPaintCompositingWire version 8 adds radial and angular gradients keyed to the shape bounds.
readonly kind: "radial-gradient"sourcereadonly centerX: numbersourceCenter as fractions of the shape bounds.
readonly centerY: numbersourcereadonly radiusX: numbersourceEllipse radii as fractions of the shape bounds; both positive.
readonly radiusY: numbersourcereadonly stops: readonly { readonly offset: number; readonly color: RgbaColor; }[]sourcereadonly opacity: numbersource
sourceinterface RetainedAngularGradientPaintRecord extends RetainedPaintCompositingreadonly kind: "angular-gradient"sourcereadonly centerX: numbersourcereadonly centerY: numbersourcereadonly angle: numbersourceStart angle in degrees, clockwise from the positive x axis.
readonly stops: readonly { readonly offset: number; readonly color: RgbaColor; }[]sourcereadonly opacity: numbersource
sourceinterface RetainedImagePaintRecord extends RetainedPaintCompositingWire 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"sourcereadonly assetId: stringsourcereadonly fit: RetainedImagePaintFitsourcereadonly opacity: numbersource
sourcetype RetainedImagePaintFit = "contain" | "cover" | "fill"How an image paint fits its shape. The wire codes (0, 1, 2 in this order) are the C++
MediaFitvalues.Native:
MediaFit
sourceinterface RetainedStrokeRecordreadonly color: RgbaColorsourcereadonly width: numbersourcereadonly position?: RetainedStrokePositionsourceWire version 6 appends the stroke position. Omitting it, or naming
center, keeps the compact wire-v5 encoding.readonly dash?: readonly number[]sourceWire version 6 appends dash intervals. An odd-length pattern repeats to an even cycle, matching SVG stroke-dasharray.
readonly paint?: RetainedPaintRecordsourceWire version 6 can append a full stroke paint. When omitted,
colorremains 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]sourceWire 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.
widthshould then hold the largest weight, which bounds and hit tests use. Only containers accept it.readonly join?: RetainedStrokeJoinsourceWire 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,
dashCapsets only the dash ends; the path’s own ends take its caps. Side weights ignore the join.readonly dashCap?: RetainedDashCapsourceWire version 23: the ends of every dash; see
join.readonly miterLimit?: numbersourceWire 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; seejoin.
sourcetype RetainedStrokePosition = "center" | "inside" | "outside"Retained stroke placement relative to the shape boundary. The wire codes (0, 1, 2 in this order) are the C++
StrokePositionvalues.Native:
StrokePosition
sourcetype RetainedStrokeCap = | "none" | "round" | "square" | "line-arrow" | "triangle-arrow" | "reversed-triangle" | "circle-arrow" | "diamond-arrow"Wire version 13 stroke end caps, in Figma’s terms. The wire codes (0 to 7 in this order) are the C++
StrokeCapvalues.Native:
StrokeCap
sourceinterface RetainedStrokeCapsreadonly start: RetainedStrokeCapsourcereadonly end: RetainedStrokeCapsource
sourcetype RetainedStrokeJoin = "miter" | "bevel" | "round"Wire version 23 stroke joins. The wire codes (0 to 2 in this order) are the C++
StrokeJoinvalues.Native:
StrokeJoin
sourcetype RetainedDashCap = "none" | "round" | "square"Wire version 23 dash ends. The wire codes (0 to 2 in this order) are the C++
DashCapvalues.Native:
DashCap
sourceinterface RetainedShadowRecordreadonly inner?: booleansourceWire 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: numbersourcereadonly y: numbersourcereadonly blur: numbersourcereadonly spread: numbersourcereadonly color: RgbaColorsource
sourceinterface RetainedVertexCornerRadiusWire version 12: one rounded vertex of a path, by stable vertex ID.
readonly vertexId: stringsourcereadonly radius: numbersourcePositive; the renderer clamps it to half of the shorter adjacent segment.
sourceinterface VectorNetworkRecordreadonly vertices: readonly VectorVertexRecord[]sourcereadonly segments: readonly VectorSegmentRecord[]sourcereadonly constraints?: readonly VectorTangentConstraintRecord[]sourcereadonly regions: readonly VectorRegionRecord[]source
sourceinterface VectorVertexRecordreadonly id: stringsourcereadonly x: numbersourcereadonly y: numbersource
sourceinterface VectorHandleRecordreadonly id: stringsourcereadonly dx: numbersourcereadonly dy: numbersource
sourcetype VectorSegmentRecord = | VectorLineSegmentRecord | VectorCubicSegmentRecord | VectorEllipticalArcSegmentRecord
sourceinterface VectorLineSegmentRecordreadonly id: stringsourcereadonly kind: "line"sourcereadonly start: stringsourcereadonly end: stringsource
sourceinterface VectorCubicSegmentRecordreadonly id: stringsourcereadonly kind: "cubic"sourcereadonly start: stringsourcereadonly end: stringsourcereadonly startHandle: VectorHandleRecordsourcereadonly endHandle: VectorHandleRecordsource
sourceinterface VectorEllipticalArcSegmentRecordExact compatibility geometry retained for migration and SVG interchange.
readonly id: stringsourcereadonly kind: "elliptical-arc"sourcereadonly start: stringsourcereadonly end: stringsourcereadonly radiusX: numbersourcereadonly radiusY: numbersourcereadonly xAxisRotation: numbersourcereadonly largeArc: booleansourcereadonly sweep: booleansource
sourceinterface VectorRegionRecordreadonly id: stringsourcereadonly windingRule: "nonzero" | "evenodd"sourcereadonly loops: readonly VectorRegionLoopRecord[]sourcereadonly fills?: readonly RetainedPaintRecord[]sourceOmitted inherits the path fill; an empty list explicitly disables this region’s fill.
sourceinterface VectorRegionLoopRecordreadonly segments: readonly VectorSegmentReferenceRecord[]sourcereadonly implicitClose?: truesource
sourceinterface VectorSegmentReferenceRecordreadonly segmentId: stringsourcereadonly direction: "forward" | "reverse"source
sourceinterface VectorTangentConstraintRecordreadonly vertexId: stringsourcereadonly handleIds: readonly [string, string]sourcereadonly mode: "angle" | "angle-and-length"source
sourceinterface RichTextStyleRecordreadonly fontResourceIds: readonly string[]sourcePrimary font first, followed by deterministic shaping fallbacks.
readonly language?: stringsourcereadonly fontSize: numbersourcereadonly lineHeight: numbersourceAbsolute renderer-space line height; styles may use different values.
readonly letterSpacing: numbersourcereadonly weight: numbersourcereadonly italic?: booleansourcereadonly underline?: booleansourcereadonly strikethrough?: booleansourcereadonly color: RgbaColorsourcereadonly openTypeFeatures?: Readonly<Record<string, boolean | number>>sourcereadonly fontVariations?: Readonly<Record<string, number>>source
sourceinterface RichTextParagraphRecordreadonly direction: "auto" | "ltr" | "rtl"sourcereadonly align: "left" | "center" | "right" | "justify"sourcereadonly spacingBefore: numbersourcereadonly spacingAfter: numbersourcereadonly indent?: { readonly left: number; readonly right: number; readonly firstLine: number; }sourcereadonly list?: { readonly kind: "ordered" | "unordered"; readonly level: number; readonly start?: number; readonly marker?: { readonly text?: string; readonly styleIndex?: number; readonly gap?: number; }; }sourcereadonly runs: readonly RichTextRunRecord[]sourceEmpty paragraphs and empty style-carrying runs are preserved.
sourceinterface RichTextRunRecord
sourcetype ResourceKind = "font" | "image" | "blob"The kind of a registered resource. The wire codes (font 1, image 2, blob 3) are the C++
ResourceKindvalues.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.
sourceclass RenderChangeBatchEncoderBuilds 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;finishreturns the bytes. Every value is checked againstWIRE_LIMITSand the node rules as it is added, so a bad node throws here rather than in the renderer. SeePROTOCOL.mdfor the byte layout.The renderer applies a batch with
RenderCoreSession.apply, which callstaco_render_session_apply.RetainedSession.applyreads it with aWireReaderand applies all commands or none.Native:
taco_render_session_apply,RetainedSession.apply,WireReaderresetScene(width: number, height: number, clearResources = true): thissourceRemoves every node and sets the scene size in document units. With
clearResources(the default) it also removes every resource.resizeScene(width: number, height: number): thissourceChanges 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): thissourceAdds or replaces a resource, such as a font file, under
id.removeResource(id: string): thissourceRemoves one resource.
upsertPath(node: RetainedPathNode): thissourceAdds or replaces a path node: its vector network, style, corner radii and caps.
upsertText(node: RetainedTextNode): thissourceAdds or replaces a text node with its styles and paragraphs.
upsertContainer(node: RetainedContainerNode): thissourceAdds or replaces a frame or group: size, clipping and corner radius.
upsertMedia(node: RetainedMediaNode): thissourceAdds or replaces an image or video node. Its pixels arrive as media frames.
removeNode(id: string): thissourceRemoves a node and all of its descendants.
setTransform(id: string, transform: RetainedNodeBase["transform"]): thissourceChanges only the transform of an existing node.
finish(): Uint8ArraysourceWrites the batch header and returns the complete batch bytes.
sourceconst 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; } >Size and count limits for one change batch and for the whole retained scene. The encoder throws a
RangeErrorbefore it writes past a limit, and the C++ reader rejects any batch that exceeds the same limits (thekMaximum*constants insession.h).retainedPayloadBytesbounds the whole scene: nodes, identifiers, vector and text data, and resources.
sourceconst RENDER_CORE_WIRE_VERSION: 24The change batch version
RenderChangeBatchEncoderwrites.Native:
TACO_RENDER_WIRE_VERSION
sourceconst RENDER_CHANGE_COMMAND_HEADER_BYTES: 8The size of each command header:
u8opcode, three reserved bytes,u32payload length.
sourceconst RENDER_CORE_VECTOR_NETWORK_WIRE_VERSION: 5Frozen wire version for headerless standalone
VectorNetworkpayloads.
sourcefunction encodeVectorNetworkRecord(network: VectorNetworkRecord): Uint8ArrayEncodes one standalone
VectorNetworkpayload for transient native overlays and Boolean results. Its frozen version isRENDER_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 withdecodeVectorNetwork.Native:
decodeVectorNetwork
sourcefunction decodeVectorNetworkRecord(bytes: Uint8Array): VectorNetworkRecordDecodes exactly one canonical standalone
VectorNetwork, such as a Boolean result the C++ side wrote withencodeVectorNetwork. 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:
encodeVectorNetworkNative, through
encodeVectorNetworkRecord:decodeVectorNetwork
Wire version history
Each raise of the scene wire version, newest first, with the commit that made it.
| Version | Commit title | Introduced in |
|---|---|---|
24 | fix(render-core): tight paint bounds for lines/connectors; exports cover everything a layer and its children paint | d00b191 #121 2026-10-04 |
23 | feat(web): Stroke settings panel with dashes, end points and joins, and round Pencil strokes | dad417a #88 2026-09-28 |
22 | feat(web): add connectors: Connector tool, key-point attach points, pinned and re-attachable ends, and line end styles | f6199e2 #73 2026-09-28 |
21 | feat(web): transform a multi-selection through one box, as in Figma | b833870 #72 2026-09-27 |
20 | refactor(render-core): paint transform handles with the size and angle the editor sends | 0d6ea45 #71 2026-09-27 |
19 | feat(model): hide paints and give each paint a blend mode | c753479 #59 2026-09-26 |
18 | feat(model): give each rectangle and frame stroke its own side weights | 7382f68 #55 2026-09-26 |
17 | feat(web): auto layout gap and padding handles on the canvas, drawn by the renderer | a292d32 #67 2026-09-26 |
16 | feat(web): draw the rulers in the renderer and hit-test rulers and guides | 7b2e247 #64 2026-09-26 |
15 | feat(web): draw frame titles in the renderer and hit-test them to select, move and rename frames | 555ae8d #62 2026-09-26 |
14 | feat(model): round each corner of rectangles and frames on its own | e6739c6 #54 2026-09-26 |
13 | feat: draw lines, arrows, ellipses, polygons and stars | 4c61909 #20 2026-09-23 |
12 | feat(render-core): round vector vertices with per-vertex corner radii | 837342a 2026-09-18 |
11 | feat(render-core): support corner smoothing and rounded frames | a9795ca 2026-09-17 |
10 | feat(render-core): support blend modes and sibling masks | b90c0f5 2026-09-17 |
9 | feat(render-core): support inner shadows, layer blur, and background blur | ebc9460 2026-09-17 |
8 | feat(render-core): support radial, angular, and image paints | 142ea63 2026-09-17 |
7 | feat(render-core): support multiple strokes per node | d037d4d 2026-09-17 |
6 | feat(render-core): support positioned strokes | 9bac561 2026-08-18 |
5 | feat(render-core): add wire-v5 retained styling | 5aa0d67 2026-08-12 |
4 | feat(render-core): retain media scene metadata | d4dee2d 2026-08-12 |
3 | feat(renderer): retain rich text in wire v3 | 4a4a289 2026-08-10 |
2 | feat(renderer): add interactive C++ foundation | 5e38c7a 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.
sourceinterface RenderCoreModuleA loaded renderer build that can create sessions.
readonly capabilities: RenderCoreCapabilitiessourceWhat this build supports.
createSession(): RenderCoreSessionsourceCreates a session with an empty scene.
Native:
taco_render_session_destroyNative, in helpers:
taco_render_session_create
sourceinterface RenderCoreSessionLow-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
expectedGenerationfail when the scene has changed since then, so a stale result cannot be mistaken for a current one. Queries returnundefinedwhen the node or resource does not exist.PortableWasmRenderCoreSessionis the Wasm implementation.apply(batch: Uint8Array): RenderUpdateReportsourceApplies one change batch from
RenderChangeBatchEncoder. The batch applies completely or not at all; a rejected batch throws.Native:
taco_render_session_applyNative, in helpers:
taco_render_session_get_last_dirty_bounds,taco_render_session_copy_last_errorapplyWithStaticImageFrames( batch: Uint8Array, expectedGeneration: bigint, frames: readonly RenderStaticImageFrame[] ): RenderUpdateReportsourceAtomically publishes the batch and candidate generation’s exact static-image set.
Native:
taco_render_session_apply_with_static_image_framesNative, in helpers:
taco_render_session_get_last_dirty_bounds,taco_render_session_copy_last_errorapplyWithMediaFrames( batch: Uint8Array, expectedGeneration: bigint, frames: readonly RenderMediaFrame[] ): RenderUpdateReportsourceAtomically publishes a batch and its exact mixed image/video frame set.
Native:
taco_render_session_apply_with_media_framesNative, in helpers:
taco_render_session_get_last_dirty_bounds,taco_render_session_copy_last_errorupdateVideoFrames( expectedGeneration: bigint, frames: readonly RenderVideoFrame[] ): RenderUpdateReportsourceReplaces only video frames while retaining the exact scene identity.
Native:
taco_render_session_update_video_framesNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorgetSceneInfo(): RenderSceneInfosourceThe scene size, counts, generation and digest.
Native:
taco_render_session_get_scene_infoNative, in helpers:
taco_render_session_copy_last_errorgetNodeGeometry(id: string): RenderNodeGeometry | undefinedsourceThe bounds and transform of one node.
Native:
taco_render_session_get_node_geometryNative, in helpers:
taco_render_session_copy_last_errorgetGeometrySnapshot(): RenderGeometrySnapshotsourceThe geometry of every node, back to front.
Native:
taco_render_session_get_geometry_snapshotNative, in helpers:
taco_render_session_copy_last_errorNative, through
decodeWasmGeometryRecords:TacoRenderGeometryRecordhitTest(x: number, y: number, worldTolerance?: number): readonly RenderHit[]sourceReturns topmost-first geometric hits using an explicit world-space tolerance.
Native:
taco_render_session_hit_testNative, in helpers:
taco_render_session_copy_last_errorNative, through
decodeWasmHitRecords:TacoRenderHitgetTextCarets( nodeId: string, expectedGeneration?: bigint ): RenderTextCaretSnapshot | undefinedsourceEvery caret position in a text node, with its geometry.
Native:
taco_render_session_get_text_caretsNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorgetTextRangeGeometry( nodeId: string, range: RenderTextRange, expectedGeneration?: bigint ): RenderTextRangeGeometry | undefinedsourceThe rectangles that cover a text range, one or more per line.
Native:
taco_render_session_get_text_range_rectsNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorhitTestText( nodeId: string, localX: number, localY: number, expectedGeneration?: bigint ): RenderTextHitResult | undefinedsourceThe text position nearest a point in the node’s local coordinates.
Native:
taco_render_session_hit_test_textNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorMoves a text position by one caret step.
preferredLocalXkeeps the column when moving up or down across lines.Native:
taco_render_session_navigate_textNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorgetTextLayoutMetrics( nodeId: string, expectedGeneration?: bigint ): RenderTextLayoutMetrics | undefinedsourceLine metrics and the suggested auto-size of a text node.
Native:
taco_render_session_get_text_layout_metricsNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorgetFontMetadata( fontResourceId: string, expectedGeneration?: bigint ): RenderFontMetadataSnapshot | undefinedsourceThe OpenType feature tags and variation axes of one font resource.
Native:
taco_render_session_get_font_metadataNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorsetTextEditingOverlay( nodeId: string, overlay: RenderTextEditingOverlay, expectedGeneration?: bigint ): voidsourcePaints 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_overlayNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorclearTextEditingOverlay(expectedGeneration?: bigint): voidsourceRemoves the text editing overlay.
Native:
taco_render_session_clear_text_editing_overlayNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorsetSelectionChrome(chrome: RenderSelectionChrome | null): booleansourceReplaces the selection outlines and handles the renderer paints over the scene, or clears them with
null. Returnsfalsewhen the loaded module predates native selection chrome, so the host keeps its own.Native:
taco_render_session_set_selection_chromeNative, in helpers:
taco_render_session_copy_last_errorNative, through
encodeSelectionChrome:taco_render_session_set_selection_chrome,SessionCache.setSelectionChromesetCanvasOverlay(overlay: RenderCanvasOverlay | null): voidsourceReplaces 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_overlayNative, in helpers:
taco_render_session_copy_last_errorNative, through
encodeCanvasOverlay:taco_render_session_set_canvas_overlay,SessionCache.setCanvasOverlaysetCanvasOverlayFont(bytes: Uint8Array): booleansourceSets the font file (TrueType, OpenType or WOFF2 bytes) frame titles in the canvas overlay are drawn with. Returns
falsewhen the loaded module cannot draw titles.Native:
taco_render_session_set_canvas_overlay_fontNative, in helpers:
taco_render_session_copy_last_errorgetVectorGeometry( nodeId: string, documentToScreen: Transform, expectedGeneration?: bigint ): RenderVectorGeometrySnapshot | undefinedsourceThe vertices, handles, segments and regions of a vector node, with screen positions from
documentToScreen.Native:
taco_render_session_get_vector_geometryNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorNative, through
decodeWasmVectorGeometry:TacoRenderVectorVertex,TacoRenderVectorCubicHandle,TacoRenderVectorSegment,TacoRenderVectorRegionhitTestVector( nodeId: string, query: RenderVectorHitQuery, expectedGeneration?: bigint ): RenderVectorHitSnapshot | undefinedsourceThe vector vertices, handles, segments and regions near a screen point.
Native:
taco_render_session_hit_test_vectorNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorNative, through
decodeWasmVectorHits:TacoRenderVectorHitbooleanVectorPaths( request: RenderVectorBooleanPathRequest, expectedGeneration?: bigint ): RenderVectorBooleanPathSnapshotsourceCombines the paths of two nodes and returns the resulting vector network.
Native:
taco_render_session_boolean_vector_pathsNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorNative, through
decodeVectorNetworkRecord:encodeVectorNetworksetVectorEditingOverlay( nodeId: string, overlay: RenderVectorEditingOverlay, expectedGeneration?: bigint ): voidsourcePaints the vector editing overlay (vertices, handles and selection) of one node. The next scene change clears it.
Native:
taco_render_session_set_vector_editing_overlayNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorNative, through
encodeVectorNetworkRecord:decodeVectorNetworkclearVectorEditingOverlay(expectedGeneration?: bigint): voidsourceRemoves the vector editing overlay.
Native:
taco_render_session_clear_vector_editing_overlayNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errormapViewport(options: ViewportMappingOptions): ViewportMappingsourceComputes how the scene fits a viewport of a given size.
Native:
taco_render_session_map_viewportNative, in helpers:
taco_render_session_copy_last_errorrender(target: RgbaRenderTarget): RenderFrameReportsourcePaints 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_rgbaNative, in helpers:
taco_render_session_copy_last_errorgetLastError(): stringsourceCopies the session-owned diagnostic string; no native memory escapes.
Native, in helpers:
taco_render_session_copy_last_errordispose(): voidsourceReleases the session. Later calls throw.
Native:
taco_render_session_destroy
sourceinterface RenderCoreCapabilitiesWhat the loaded renderer build supports, read from the C capability structs. Check a flag with
requireRenderCapabilitybefore calling the matching session method.Native:
TacoRenderCapabilities,TacoRenderExtendedCapabilitiesreadonly abiVersion: numbersourcereadonly wireVersion: numbersourceRetained scene/change batch wire version.
readonly vectorNetworkWireVersion: numbersourceHeaderless
VectorNetworkpayload version used by overlays and Boolean results.readonly retainedScene: booleansourcereadonly structuredPaths: booleansourcereadonly styledTextRecords: booleansourcereadonly embeddedResources: booleansourcereadonly skia: booleansourcereadonly nativeRaster: booleansourcereadonly wasm: booleansourcereadonly webgpu: booleansourcereadonly skparagraph: booleansourcereadonly harfbuzz: booleansourcereadonly icu: booleansourcereadonly geometryQueries: booleansourcereadonly hitTesting: booleansourcereadonly dirtyBounds: booleansourcereadonly textCarets: booleansourcereadonly textRangeRects: booleansourcereadonly textHitTesting: booleansourcereadonly textLayoutMetrics: booleansourcereadonly textEditingOverlays: booleansourcereadonly fontMetadata: booleansourcereadonly incrementalRendering: booleansourcereadonly renderDiagnostics: booleansourcereadonly staticImageFrames: booleansourcereadonly videoFrames: booleansourcereadonly vectorGeometry: booleansourcereadonly vectorHitTesting: booleansourcereadonly vectorEditingOverlays: booleansourcereadonly vectorBooleanOperations: booleansource
sourcetype RequiredRenderCapability = | "skia" | "nativeRaster" | "wasm" | "webgpu" | "skparagraph" | "harfbuzz" | "icu" | "textCarets" | "textRangeRects" | "textHitTesting" | "textNavigation" | "textLayoutMetrics" | "textEditingOverlays" | "fontMetadata" | "incrementalRendering" | "renderDiagnostics" | "staticImageFrames" | "videoFrames" | "vectorGeometry" | "vectorHitTesting" | "vectorEditingOverlays" | "vectorBooleanOperations"A capability flag
requireRenderCapabilitycan check.
sourcefunction requireRenderCapability( capabilities: RenderCoreCapabilities, capability: RequiredRenderCapability ): voidThrows
RenderCoreCapabilityErrorunlesscapabilitieshas the flag.
sourceclass RenderCoreCapabilityError extends ErrorThrown by
requireRenderCapabilitywhen the build lacks a capability.readonly capability: RequiredRenderCapabilitysourcenew (capability: RequiredRenderCapability)source
sourcefunction createPortableWasmRenderCore( module: PortableWasmExports, options: PortableWasmFacadeOptions = {} ): PortableWasmRenderCoreModuleWraps a loaded portable Wasm module; see
PortableWasmRenderCoreModule.
sourceclass PortableWasmRenderCoreModule implements RenderCoreModuleA
RenderCoreModuleover 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; usecreateOwnedWasmRenderCoreSessionfor those.readonly capabilities: RenderCoreCapabilitiessourcenew (module: PortableWasmExports, options: PortableWasmFacadeOptions = {})sourceNative, 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_capabilitiescreateSession(): PortableWasmRenderCoreSessionsourceNative:
taco_render_session_destroyNative, in helpers:
taco_render_session_create
sourcefunction createOwnedWasmRenderCoreSession( module: PortableWasmExports, options: PortableWasmFacadeOptions = {} ): PortableWasmRenderCoreSessionCreates a second, facade-owned session in a module that also carries a renderer, such as the Skia WebGPU build.
createPortableWasmRenderCorerefuses 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_destroyNative, in helpers:
taco_render_wasm_abi_version,taco_render_wasm_wire_version,taco_render_wasm_vector_network_wire_version,taco_render_wasm_portable_capabilities
sourcefunction attachPortableWasmRenderCoreSession( module: PortableWasmExports, session: number, options: PortableWasmFacadeOptions = {} ): PortableWasmRenderCoreSessionAttaches 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
sourceinterface PortableWasmRenderCoreSession extends RenderCoreSessionThe Wasm implementation of
RenderCoreSession. Each method copies its input into Wasm memory, calls onetaco_render_session_*function, checks the result strictly, and copies the output back, so no Wasm memory escapes.readonly capabilities: RenderCoreCapabilitiessourceWhat the loaded module supports.
readonly transferMetrics: WasmBatchStagingMetricssourceCounters for the reused buffer that carries batches into Wasm memory.
exportScene(request: RenderExportRequest): RenderExportResultsourcePaints one scene region into a raster, SVG, or PDF target.
Native:
taco_render_session_export,taco_render_session_copy_export_bytesNative, in helpers:
taco_render_session_copy_last_erroroutlineStroke( request: RenderStrokeOutlineRequest, expectedGeneration?: bigint, budget?: StrokeOutlineBudget ): RenderStrokeOutlineSnapshot | undefinedsourceOne stroke of a path or container node as the filled shape it paints (see
RenderStrokeOutlineSnapshot), orundefinedwhen the node does not exist. ThrowsPortableWasmStatusErrorwhen the node has no such stroke, is a text or media node, or the stroke cannot be outlined; itsdetailis the renderer’s reason. Throws a plain error when the renderer build predates the query. With abudget, the outline’s size on the wire is charged to it before the outline is copied out and decoded; one that does not fit throwsStrokeOutlineBudgetError.Native:
taco_render_session_outline_strokeNative, in helpers:
taco_render_session_get_scene_info,taco_render_session_copy_last_errorNative, through
decodeVectorNetworkRecord:encodeVectorNetwork
sourceinterface PortableWasmFacadeOptions extends WasmBatchStagingOptionsOptional 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?: numbersourcereadonly maximumTextQueryRecords?: numbersourcereadonly maximumQueryIdentifierBytes?: numbersourcereadonly maximumVectorQueryRecords?: numbersourcereadonly maximumVectorIdentifierBytes?: numbersourcereadonly maximumErrorBytes?: numbersource
sourceinterface PortableWasmExports extends WasmBatchMemoryThe Emscripten module surface the facade needs: the Wasm heap,
mallocandfree, and the exportedtaco_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_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 ): numbersourceAbsent from modules built before native selection chrome.
_taco_render_session_set_canvas_overlay?( session: number, bytes: number, size: number ): numbersourceAbsent from modules built before native overlay lines.
_taco_render_session_set_canvas_overlay_font?( session: number, bytes: number, size: number ): numbersourceAbsent 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 ): numbersourceAbsent 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 ): numbersourceOptional: 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 ): numbersourceAbsent from renderer builds made before stroke outlines; no capability flag announces it, so
outlineStrokechecks for the export._taco_render_wasm_vector_network_wire_version(): numbersource_taco_render_wasm_wire_version(): numbersource_taco_render_wasm_portable_capabilities(): numbersource
sourceclass PortableWasmAbiError extends ErrorThe 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
sourceclass PortableWasmStatusError extends Errorreadonly operation: stringsourcereadonly status: RenderCoreStatussourcereadonly statusName: stringsourcereadonly detail: stringsourcenew (operation: string, status: RenderCoreStatus, detail = "")source
sourceconst 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; } >The status codes every C function returns, by name. The values are the
TacoRenderStatusconstants.Native:
TacoRenderStatus
sourcetype RenderCoreStatus = (typeof RENDER_CORE_STATUS)[keyof typeof RENDER_CORE_STATUS]One
RENDER_CORE_STATUSvalue.
sourceconst RENDER_CORE_ABI_VERSION: 1The C ABI version this package speaks. The Wasm module must report the same.
Native:
TACO_RENDER_ABI_VERSION
sourceconst 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; } >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 exampleupdateReportBytesisTacoRenderUpdateReport); the C++ side checks most of them withstatic_assert.Native:
TacoRenderCapabilities,TacoRenderSessionOptions,TacoRenderUpdateReport,TacoRenderSceneInfo,TacoRenderNodeGeometry,TacoRenderGeometryRecord,TacoRenderDirtyBounds,TacoRenderHitand 36 more
TacoRenderViewportOptions,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
sourceconst RENDER_FONT_METADATA_MAX_FEATURES: 131070The most OpenType feature tags a font metadata query returns.
sourceconst RENDER_FONT_METADATA_MAX_AXES: 65535The most variation axes a font metadata query returns.
sourcefunction decodeWasmGeometryRecords( recordBytes: Uint8Array, recordCount: number, identifierBytes: Uint8Array ): readonly RenderNodeGeometry[]Decodes the geometry records that
taco_render_session_get_geometry_snapshotwrites, with their node IDs, without keeping references to Wasm memory.Native:
TacoRenderGeometryRecord
sourcefunction decodeWasmHitRecords( recordBytes: Uint8Array, hitCount: number, identifierBytes: Uint8Array ): readonly RenderHit[]Decodes topmost-first ABI-v1 hit records without retaining Wasm memory.
Native:
TacoRenderHit
sourcefunction decodeWasmVectorGeometry( vertexBytes: Uint8Array, vertexCount: number, handleBytes: Uint8Array, handleCount: number, segmentBytes: Uint8Array, segmentCount: number, regionBytes: Uint8Array, regionCount: number, identifierBytes: Uint8Array, generation: bigint ): RenderVectorGeometrySnapshotStrictly decodes one generation-stamped vector-geometry payload.
Native:
TacoRenderVectorVertex,TacoRenderVectorCubicHandle,TacoRenderVectorSegment,TacoRenderVectorRegion
sourcefunction decodeWasmVectorHits( hitBytes: Uint8Array, hitCount: number, identifierBytes: Uint8Array, generation: bigint ): RenderVectorHitSnapshotStrictly decodes vector hits in native priority, distance, and order.
Native:
TacoRenderVectorHit
sourcetype RenderMediaFrame = | ({ readonly kind: "image" } & RenderStaticImageFrame) | ({ readonly kind: "video" } & RenderVideoFrame)One member of a globally asset-ID-sorted mixed image/video frame set.
sourcetype RenderStaticImageFrame = | RenderStaticImageFrameReuse | RenderStaticImageFrameRgba8888One member of the complete, logical-asset-ID-deduplicated static image set.
sourceinterface RenderStaticImageFrameRgba8888One tightly packed, unpremultiplied RGBA8888 sRGB replacement image.
readonly disposition: "rgba8888"sourcereadonly assetId: stringsourcereadonly contentRevision: bigintsourcereadonly width: numbersourcereadonly height: numbersourcereadonly pixels: Uint8Arraysource
sourceinterface RenderStaticImageFrameReuseReuses the exact active image whose caller-owned content revision matches.
readonly disposition: "reuse-active"sourcereadonly assetId: stringsourcereadonly contentRevision: bigintsource
sourcetype RenderVideoFrame = RenderVideoFrameReuse | RenderVideoFrameRgba8888One exact decoded video-frame revision; replacements advance monotonically.
sourceinterface RenderVideoFrameRgba8888 extends RenderStaticImageFrameRgba8888One tightly packed decoded video-frame replacement.
sourceinterface RenderVideoFrameReuse extends RenderStaticImageFrameReuseReuses 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.
sourceclass ReusableWasmBatchStagingBufferReuses 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 = {})sourceget metrics(): WasmBatchStagingMetricssourceThe current counters.
write(batch: Uint8Array): StagedWasmBatchsourceCopies a batch into the buffer, growing it when needed.
apply<Result>( batch: Uint8Array, applyBatch: (pointer: number, byteLength: number) => Result ): ResultsourceWrites a batch and passes its pointer and length to
applyBatch.dispose(): voidsourceFrees the Wasm allocation. Later writes throw.
sourceinterface WasmBatchMemoryMinimal Emscripten memory surface needed to transfer retained wire batches.
readonly HEAPU8: Uint8Arraysource_malloc(bytes: number): numbersource_free(pointer: number): voidsource
sourceinterface StagedWasmBatchWhere a staged batch sits in Wasm memory.
readonly pointer: numbersourcereadonly byteLength: numbersource
sourceinterface WasmBatchStagingOptionsStaging buffer size bounds in bytes. The maximum defaults to
WIRE_LIMITS.batchBytes.readonly minimumCapacityBytes?: numbersourcereadonly maximumCapacityBytes?: numbersource
sourceinterface WasmBatchStagingMetricsCounters that show how often the staging buffer allocates and copies.
readonly allocations: numbersourceNative allocations made over the lifetime of this staging buffer.
readonly copies: numbersourceJavaScript-to-Wasm
HEAPU8.setoperations.readonly bytesCopied: numbersourcereadonly capacityBytes: numbersourcereadonly peakBatchBytes: numbersourcereadonly heapGrowthBytes: numbersourceObserved 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.
sourceconst AUTOMATIC_FALLBACK_FAMILIES: readonly [ "Noto Sans", "Noto Sans SC", "Noto Sans TC", "Noto Sans HK", "Noto Sans JP", "Noto Sans KR", "Noto Color Emoji" ]The built-in Noto families automatic fallback can draw with.
sourcetype AutomaticFallbackFamily = typeof AUTOMATIC_FALLBACK_FAMILIES[number]One of
AUTOMATIC_FALLBACK_FAMILIES.
sourcefunction automaticFallbackFamilies( cjk: CjkFamily, emoji: boolean ): readonly AutomaticFallbackFamily[]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 (seeisEmojiGrapheme) 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.
sourcefunction automaticFallbackAsset(family: AutomaticFallbackFamily): FontAssetThe 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.
sourcefunction automaticFallbackAssetById(id: string): FontAsset | undefinedThe automatic fallback asset with this ID, if the ID is one.
sourceconst AUTOMATIC_FALLBACK_ASSET_ID_PREFIX: "automatic-fallback/"Asset IDs that start with this prefix are reserved for automatic fallback.
sourcefunction isAutomaticFallbackFamily( family: string ): family is AutomaticFallbackFamilyWhether
familyis one ofAUTOMATIC_FALLBACK_FAMILIES.
sourcefunction preferredCjkFamily( styleLanguage: string | undefined, nodeCodePoints: Iterable<number>, userLanguage: string | undefined ): CjkFamilyChooses which CJK family draws a Han character that several of them have. The glyph forms differ by region, so the choice follows, in order:
- the language set on the text style (“ja”, “zh-TW”, ...);
- the text node itself: kana means Japanese and Hangul means Korean;
- the user’s language (the browser’s
navigator.language); - otherwise Simplified Chinese.
sourcefunction cjkFamilyForLanguage( language: string | undefined ): CjkFamily | undefinedThe 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.
sourcefunction graphemeClusters(text: string): string[]Splits text into grapheme clusters, the units Skia picks a font for.
sourcefunction isEmojiGrapheme(grapheme: string): booleanWhether 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.
sourcefunction essentialCodePoints(grapheme: string): number[]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.
sourceconst RENDER_FEATURE_POLICY_VERSION: 25The 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.
sourceinterface RenderFeaturePolicyreadonly feature: stringsourceStable semantic identifier used by tests and developer diagnostics.
readonly code: RenderFeaturePolicyCodesourceStable machine-readable code. Do not rename it when implementation status changes.
readonly scope: RenderFeatureScopesourcereadonly disposition: RenderFeatureDispositionsourcereadonly note: stringsourceThe exact boundary or ownership rule for the current retained renderer.
sourcetype RenderFeaturePolicyCode = `TACO_RENDER_POLICY_${string}`
sourcetype RenderFeatureDisposition = | "rendered" | "deliberate-static-policy" | "application-overlay" | "structured-rejection"
sourcetype RenderFeatureScope = "node" | "style" | "asset" | "document" | "editor"
sourceconst RENDER_FEATURE_POLICIES: readonly RenderFeaturePolicy[]Flat registry for diagnostics, documentation generation, and invariant tests.
sourceconst 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; }; } >
sourcetype SceneNodePolicyKey = SceneNodePolicyKeyFor<SceneNode>Adding a
SceneNodeor shape discriminant requires an explicit policy here.
sourceconst 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; }
sourceconst PAINT_FEATURE_POLICIES: { readonly solid: RenderFeaturePolicy; readonly "linear-gradient": RenderFeaturePolicy; readonly "radial-gradient": RenderFeaturePolicy; readonly "angular-gradient": RenderFeaturePolicy; readonly image: RenderFeaturePolicy; }
sourceconst STROKE_POSITION_FEATURE_POLICIES: { readonly center: RenderFeaturePolicy; readonly inside: RenderFeaturePolicy; readonly outside: RenderFeaturePolicy; }
sourceconst LAYOUT_FEATURE_POLICIES: { readonly absolute: RenderFeaturePolicy; readonly vertical: RenderFeaturePolicy; readonly horizontal: RenderFeaturePolicy; readonly grid: RenderFeaturePolicy; }
sourceconst MEDIA_FIT_FEATURE_POLICIES: { readonly contain: RenderFeaturePolicy; readonly cover: RenderFeaturePolicy; readonly fill: RenderFeaturePolicy; }
sourceconst CONNECTOR_ROUTING_FEATURE_POLICIES: { readonly straight: RenderFeaturePolicy; readonly elbow: RenderFeaturePolicy; readonly curve: RenderFeaturePolicy; }
sourceconst 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; }
sourceconst STROKE_JOIN_FEATURE_POLICIES: { readonly miter: RenderFeaturePolicy; readonly bevel: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; }What the renderer does with each stroke join (wire version 23).
sourceconst STROKE_DASH_CAP_FEATURE_POLICIES: { readonly none: RenderFeaturePolicy; readonly round: RenderFeaturePolicy; readonly square: RenderFeaturePolicy; }What the renderer does with each dash end of a dashed stroke (wire version 23).
sourceconst CODE_DECORATION_FEATURE_POLICIES: { readonly emphasis: RenderFeaturePolicy; readonly addition: RenderFeaturePolicy; readonly deletion: RenderFeaturePolicy; readonly annotation: RenderFeaturePolicy; }
sourceconst VECTOR_SEGMENT_FEATURE_POLICIES: { readonly line: RenderFeaturePolicy; readonly cubic: RenderFeaturePolicy; readonly "elliptical-arc": RenderFeaturePolicy; }
sourceconst ASSET_KIND_FEATURE_POLICIES: { readonly image: RenderFeaturePolicy; readonly video: RenderFeaturePolicy; readonly font: RenderFeaturePolicy; }
sourceconst ASSET_SOURCE_FEATURE_POLICIES: { readonly url: RenderFeaturePolicy; readonly data: RenderFeaturePolicy; }
sourceconst 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; }Adding a bindable model property requires a deliberate renderer policy.
sourceconst 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; }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.
sourcefunction compilePageRecords( document: DesignDocument, pageId: PageId, options: CompilePageOptions ): readonly RetainedNodeRecord[]The retained node records a page compiles to, in paint order, before they are encoded;
compilePageBatchencodes the same records. For tests and tools that compare compiled scenes.
sourceconst compilerApplyStats: { orderPathComputations: number; hierarchyRebuilds: number; resourceMapCopies: number; reusableResourceScans: number; incrementalAncestorWalks: number; }
sourcefunction createStrokeOutlineBudget( maximumNetworkBytes: number, maximumRendererMilliseconds: number ): StrokeOutlineBudgetA new budget for one command, with nothing charged yet.
since 0.26.0sourcefunction documentWithEditorInstances( canonical: DesignDocument, renderer: DesignDocument | undefined ): DesignDocumentThe 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.
sourcefunction expandPageInstances( document: DesignDocument, pageId: PageId, resolver: InstanceResolver = resolveInstance, cache?: InstanceResolveCache ): InstanceExpansionWalks a page’s stored tree, expands every instance on it and writes the bound fields of layers under modes other than the active ones.
sourcefunction exportFromLoadedSession( session: ExportSession, request: Omit< RenderCompiledExportRequest, "batch" | "imageAssets" > ): RenderExportResultExports 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 (rgbaorpng) whose pixel size is over the limit is refused withRenderExportTooLargeErrorbeforeexportSceneis 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_errorNative, through
PortableWasmRenderCoreSession.exportScene:taco_render_session_export,taco_render_session_copy_export_bytes,taco_render_session_copy_last_error
sourcefunction exportPixelSize( region: Pick< RenderRect, | "width" | "height" >, scale: number ): { readonly pixelWidth: number; readonly pixelHeight: number }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.
sourcetype ExportSession = Pick< PortableWasmRenderCoreSession, "getSceneInfo" | "getNodeGeometry" | "exportScene" >What
exportFromLoadedSessionuses of a renderer session.
sourcefunction inheritedVectorRegionFills( document: DesignDocument, nodeId: NodeId ): readonly Paint[] | undefinedThe 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).
undefinedwhen 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.
sourceinterface InstanceExpansionA 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: DesignDocumentsourceThe input document itself when the page has no instances and no layer needs other modes.
readonly instances: ReadonlyMap<NodeId, ExpandedInstance>sourcereadonly inheritedModes: ReadonlyMap<NodeId, VariableModes>sourceThe 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>sourcePage layers with their bound fields written for their modes.
readonly instancesByComponent: ReadonlyMap<ComponentId, ReadonlySet<NodeId>>sourceThe reverse index of design 4.3: every component to the instances that draw it, directly or through nesting (
ResolvedInstance.componentIds).readonly textSizesVersion: numbersourcegetDrawnTextSizesVersionwhen 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.
sourceinterface InstanceExpansionUpdateWhat
updatePageInstancesreturns.readonly expansion: InstanceExpansionsourcereadonly changes: ChangeSetsourcechangesin terms of the expanded documents, virtual layers included.
sourcetype InstanceResolver = ( document: DesignDocument, instanceId: NodeId, options?: ResolveInstanceOptions ) => ResolvedInstanceDerives the layers an instance shows. The page compiler calls it once per instance on the page;
resolveInstancefrom@taco-slides/modelis the default (design 4.1).
sourceinterface MeasuredExportWhat an export will draw, as
measureExportFromLoadedSessionworks it out.readonly region: RenderRectsourceThe part of the page the export shows, in page units.
readonly scale: numbersourceHow many output units one page unit becomes.
readonly pixelWidth: numbersourceThe 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
sourcefunction measureExportFromLoadedSession( session: Pick< ExportSession, "getSceneInfo" | "getNodeGeometry" >, request: Pick< RenderCompiledExportRequest, "rootNodeId" | "scale" | "options" > ): MeasuredExportMeasures 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_errorNative, through
RenderCoreSession.getNodeGeometry:taco_render_session_get_node_geometry,taco_render_session_copy_last_error
sourcefunction outlineStrokesInThrowawaySession( createSession: () => StrokeOutlineSession, request: RenderCompiledStrokeOutlineRequest, modelVectorNetwork: ( network: VectorNetworkRecord ) => VectorNetwork, now: () => number = () => performance.now() ): readonly StrokeOutlineResult[]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.budgetthe time every step takes is measured withnow(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 withStrokeOutlineTimeError. 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_errorNative, through
RenderCoreSession.getSceneInfo:taco_render_session_get_scene_info,taco_render_session_copy_last_errorNative, through
PortableWasmRenderCoreSession.outlineStroke:taco_render_session_outline_stroke,taco_render_session_get_scene_info,taco_render_session_copy_last_errorNative, through
RenderCoreSession.dispose:taco_render_session_destroy
sourceclass RenderExportTooLargeError extends ErrorA raster export would have more pixels than
RenderExportOptions.maxPixelsallows. It is thrown before the renderer is asked to draw, so no picture was allocated.readonly pixelWidth: numbersourcereadonly pixelHeight: numbersourcereadonly maxPixels: numbersourcenew (pixelWidth: number, pixelHeight: number, maxPixels: number)source
sourceinterface RenderVectorPendingHandleA 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: stringsourceThe point the handle belongs to; it must be in the previewed network.
readonly dx: numbersourceThe handle’s offset from its point, in the path’s local units.
readonly dy: numbersourcereadonly mirrored: booleansourceAlso draw the exact mirror of the handle about its point.
sourcefunction resetCompilerApplyStats(): void
sourcefunction resetSceneGeometrySnapshotStats(): void
sourcefunction resetWasmGeometryDecodeStats(): void
sourcefunction resolvedVisualStyle( document: DesignDocument, style: VisualStyle | undefined ): VisualStyleThe 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.
sourcefunction retainInstances( expansion: InstanceExpansion, drawn: (id: NodeId) => boolean ): InstanceExpansionDrops instances the compiler did not draw, such as ones on another page.
sourceconst sceneGeometrySnapshotStats: { nodeArrayCopies: number }
sourceinterface StrokeOutlineBudgetHow 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: numbersourceremainingNetworkBytes: numbersourcereadonly maximumRendererMilliseconds: numbersourceHow 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 withStrokeOutlineTimeErrorinstead. 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
sourceclass StrokeOutlineBudgetError extends ErrorThrown by
outlineStrokefor the outline that would take a command past itsStrokeOutlineBudget. The outline was sized and nothing else: it was not copied out of the renderer or decoded, and the budget is unchanged.readonly networkByteCount: numbersourcenew (networkByteCount: number, budget: StrokeOutlineBudget)source
sourcetype StrokeOutlineSession = Pick< PortableWasmRenderCoreSession, "apply" | "getSceneInfo" | "outlineStroke" | "dispose" >What
outlineStrokesInThrowawaySessionuses of a renderer session.
sourceclass StrokeOutlineTimeError extends ErrorThrown 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
StrokeOutlineBudgetallows, byInteractiveRenderSessionCoordinator.outlineDocumentStrokesandoutlineStrokesInThrowawaySession.outlinedStrokeCountof the request’sstrokeCountstrokes were answered before it; their answers are dropped with the request.readonly outlinedStrokeCount: numbersourcereadonly strokeCount: numbersourcenew ( budget: StrokeOutlineBudget, outlinedStrokeCount: number, strokeCount: number )source
sourcefunction timedStrokeOutlineStep<Result>( budget: StrokeOutlineBudget | undefined, progress: { readonly outlinedStrokeCount: number; readonly strokeCount: number; }, step: () => Result, now: () => number = () => performance.now() ): ResultRuns one step of a command’s work on the thread that draws the editor and counts the time it takes, measured with
nowin milliseconds, down in the command’sbudget, whether the step answers or throws. A step is not started once no time is left:StrokeOutlineTimeErroris 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.
sourcefunction updatePageInstances( previous: InstanceExpansion, current: DesignDocument, changes: ChangeSet, resolver: InstanceResolver = resolveInstance, cache?: InstanceResolveCache ): InstanceExpansionUpdateBrings 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 inchangesmay be on another page; the compiler drops their layers, andretainInstancesprunes them after.
sourceconst wasmGeometryDecodeStats: { identifierSetInserts: number }Decodes ABI-v1 geometry records from bounded heap slices. The returned objects do not retain the supplied Wasm memory and never contain pointers.
since 0.23.0sourcefunction withCanonicalComponentChanges( rendererChanges: ChangeSet | undefined, canonicalChanges: ChangeSet ): ChangeSetThe 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.
sourcefunction registerTacoDesignScheme(): voidRegisters the
taco-designscheme. Call it before the app is ready.Electron accepts
protocol.registerSchemesAsPrivilegedonce per app, so an app with schemes of its own must not call this: it adds the exportedtacoDesignSchemeobject to its own call instead.
sourceconst tacoDesignScheme: { readonly scheme: "taco-design"; readonly privileges: { readonly standard: true; readonly secure: true; readonly supportFetchAPI: true; readonly stream: true; readonly codeCache: true; }; }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.jsresolve, and storage works.secure: the page is a secure context, which WebGPU needs.supportFetchAPI: the renderer file is downloaded withfetch.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.
sourcefunction handleTacoDesignScheme( session: Session, options: { readonly appDirectory?: string } = {} ): voidServes
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.
createTacoDesignViewcalls this for its own partition; call it yourself only when you make theWebContentsViewyourself. Calling it again for the same session and folder does nothing.options.appDirectorynames another folder of editor files to serve than the package’s own, and must be an absolute path.
sourceconst TACO_DESIGN_SCHEME: "taco-design"The custom scheme the editor’s files are served from.
sourceconst TACO_DESIGN_HOST: "app"The only host the scheme answers for.
sourceconst TACO_DESIGN_ORIGIN: "taco-design://app"The origin of the editor page; permission rules compare against it.
sourceconst TACO_DESIGN_APP_URL: "taco-design://app/"The address
createTacoDesignViewloads.
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.
sourceconst appDirectory: stringThe folder of editor files inside this package, served as
taco-design://app/.
sourceconst preloadPath: stringThe 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.
sourcefunction createTacoDesignView( options: TacoDesignViewOptions = {} ): TacoDesignViewMakes a
WebContentsViewthat 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.
sourceinterface TacoDesignViewOptionsThe options of
createTacoDesignView. Every one can be left out.readonly document?: unknownsourceThe 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 rejectsreadywithinvalid-documentorschema-too-new, and the view shows why.readonly requestTimeoutMs?: numbersourceHow long one request (
getDocument()and the others) waits for its answer. Default 30 000 ms.readonly partition?: stringsourceThe 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?: stringsourceA 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?: numbersourceHow long one load of the editor’s page may take, from its beginning to a drawn editor, before it ends with
ready-timeout:readyrejects for the first load,reload()for a later one. Default 60 000 ms.readonly startupProgress?: TacoDesignStartupProgressDrawersourceWho 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 thestartupProgressevent. The event is sent either way.readonly onDownload?: (item: DownloadItem) => voidsourceCalled 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?: ShortcutOverridessourceChanges to the editor’s keyboard shortcuts: a command ID mapped to its new keys (
"CmdOrCtrl+Shift+D"), to a list of keys, or tonullto switch that shortcut off.getShortcuts()lists the command IDs. Keys that cannot be read throwinvalid-shortcut-keyshere; an unknown command ID, a command that cannot be changed, or two commands on the same keys rejectreadywith the matching error code.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.
sourceinterface TacoDesignViewWhat
createTacoDesignViewreturns: 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
readyfirst. It rejects with aTacoDesignErrorwhen the editor never started or transport fails. Design calls also report domain errors as documented ondesignRequest.readonly view: WebContentsViewsourceA plain
WebContentsView: add it to a window and give it bounds.readonly ready: Promise<TacoDesignReadyState>sourceResolves when the editor’s renderer reports ready. Rejects with a
TacoDesignErrorwhen 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>sourceThe 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>sourceReplaces the open document. Undo history, selection and zoom start fresh. Rejects with
invalid-documentorschema-too-newand 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 onegetDocument()resolved with. Otherwise the call rejects withrevision-changedand nothing changes.newDocument(options?: TacoDesignSetDocumentOptions): Promise<DocumentInfo>sourceReplaces 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, thechangeevent has the origin"host", and with{ ifRevision }the document is replaced only at that revision (revision-changedotherwise). Resolves with the new document’s ID, title, page count and revision. Read it withgetDocument()when you want to store it.getState(): Promise<EditorState>sourceThe versions the editor speaks and a few facts about the open document.
designRequest( request: DesignRequest, options?: TacoDesignRequestOptions ): Promise<DesignReply>sourceSends 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>sourceUndoes one step.
doneis false when there was nothing to undo.redo(): Promise<TacoDesignHistoryResult>sourceRedoes one step.
doneis false when there was nothing to redo.exportLayers( options: TacoDesignExportOptions ): Promise<TacoDesignExportedFile[]>sourceDraws 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 withunknown-layer,unknown-page,unsupported-format,invalid-request,export-too-large(more than 8192 x 8192 pixels),renderer-failedorexport-failed. A document with no page has nothing to export: every target isunknown-pageorunknown-layer.getSelection(): Promise<SelectionSummary>sourceThe page that is shown and the selected layers, each with its ID, name and kind.
zoomToFit( options?: { readonly target?: "page" | "selection"; } ): Promise<{ done: boolean }>sourceZooms the canvas so that the whole page fits, or the selected layers with
{ target: "selection" }.doneis false when the selection was asked for and nothing is selected, and when the document has no page.focusCanvas(): Promise<FocusSummary>sourceGives 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[]>sourceThe 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[]>sourceReplaces this app’s changes to the shortcuts and resolves with the list as it is afterwards.
overridesis 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 withunknown-shortcut-command,invalid-shortcut-keys,shortcut-keys-in-useorshortcut-not-customizable;error.pathis the command ID. The changes are kept by this object, not by the page: they last as long as the view and survivereload().The setting for keys that are both an editor shortcut and a menu accelerator.
setMenuShortcutConflict(conflict: MenuShortcutConflict): voidsourceChanges that setting. Any other value than the three throws
invalid-option.reload(): Promise<TacoDesignReloaded>sourceLoads the editor’s page again without losing work. The page first ends editing and freezes (it accepts no edit, and refuses
setDocument(),undo()andredo()withnot-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 afailureevent. Resolves when the new page is ready as it is afterready: the editor answers and its renderer has drawn, so anexportLayers()right after it works.handedOveris 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, soreload()rejects with every errorreadycan reject with, as soon as it is known (load-failed,app-files-missing,renderer-failed,page-crashed,ready-timeoutafterreadyTimeoutMs, and the others), and thefailurelisteners hear the same error. It also rejects withdisposedwhen the view is disposed meanwhile, and withload-failedwhen 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] ): () => voidsourceListens 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 afterready, 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(): voidsourceGives the keyboard to the editor’s view.
dispose(): voidsourceCloses the view’s page. The view cannot be used afterwards.
sourceinterface TacoDesignReadyStateWhat
readyresolves with.readonly url: stringsourceThe address the editor was loaded from.
readonly build: TacoDesignBuildInfo | nullsourceWhat the editor was built from, or null when the folder has no build record.
readonly protocolVersion: numbersourceThe version of the host protocol the editor and this package agreed on.
readonly documentSchemaVersion: stringsourceThe newest document schema version the editor reads, and the one it writes.
readonly rendererWireVersion: numbersourceThe version of the format the editor and its renderer talk in.
readonly document: DocumentInfosourceThe open document: its id, title, page count, revision, and whether undo and redo are possible.
sourceinterface TacoDesignBuildInfoWhat the assemble script wrote next to the editor files.
readonly documentSchemaVersion: stringsourceThe document schema version the editor reads and writes, such as
0.27.0.readonly rendererWireVersion: numbersourceThe version of the format the editor and its renderer talk in.
readonly commit: stringsourceThe commit the editor was built from.
sourceconst DEFAULT_READY_TIMEOUT_MS: 60000How long
readywaits 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.
sourcetype DocumentJson =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: stringsourceThe document schema version the document is written in, such as
0.27.0.readonly id: stringsourceThe document’s ID.
readonly title: stringsourceThe document’s title.
readonly [key: string]: unknownsourceEverything else in the document: its pages, layers, components and pictures.
sourceinterface TacoDesignDocumentSnapshotWhat
getDocument()resolves with.readonly document: DocumentJsonsourceThe whole document as plain JSON, without undo history. Write it with
JSON.stringify.readonly revision: numbersourceThe revision the document was read at. It counts every change made so far, also those whose
changeevent has not arrived yet, so achangeevent’s revision can be lower. Do not compare the two to decide whether to save before closing: always read the document one last time.
sourceinterface TacoDesignSetDocumentOptionsWhat
setDocument()takes besides the document, and whatnewDocument()takes.readonly ifRevision?: numbersourceReplace the document only while the editor’s document is at this revision (the
revisionthatgetDocument()resolved with). At any other revision the call rejects withrevision-changedand nothing changes: an edit was made since that read. Revisions start at 0 again on every page load, so use one read after the lastreloadedevent.
sourceinterface TacoDesignSetDocumentResult extends DocumentInfoWhat
setDocument()resolves with.readonly migratedFrom?: stringsourceThe schema version the document arrived in, when the editor had to bring it up to date.
sourceinterface TacoDesignHistoryResult extends DocumentInfoWhat
undo()andredo()resolve with.readonly done: booleansourceFalse when there was nothing to undo or redo.
sourceinterface DocumentInfoA few facts about the open document, small enough to send with every change.
readonly id: stringsourceThe document’s ID.
readonly title: stringsourceThe document’s title.
readonly pageCount: numbersourceHow many pages the document has.
readonly revision: numbersourceGoes up by one on every change to the document and on every
setDocument. Starts at 0 on each page load.readonly canUndo: booleansourceTrue when there is a step to undo.
readonly canRedo: booleansourceTrue when there is a step to redo.
sourceinterface EditorStateThe versions the editor speaks and a few facts about the open document: the answer to a
getStaterequest.readonly design?: DesignCapabilitysourceOptional read/planning capability; absent in older editors.
readonly protocolVersion: numbersourceThe version of the host protocol the editor and the host agreed on.
readonly documentSchemaVersion: stringsourceThe newest document schema version the editor reads, and the one it writes.
readonly rendererWireVersion: numbersourceThe version of the format the editor and its renderer talk in.
readonly build: stringsourceNames the build of the editor’s page, for error reports.
readonly document: DocumentInfosourceThe 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
sourceinterface TacoDesignEventsThe events of a
TacoDesignView, each with the listener it calls. The names are whatTacoDesignView.ontakes.design: (event: DesignEvent) => voidsourceStamped invalidations for the current page, not a resumable synchronization log.
change: (change: TacoDesignChange) => voidsourceThe 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) => voidsourceOther 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) => voidsourceThe keyboard moved: into or out of the editor, or between its canvas, a text field and the rest.
failure: (error: TacoDesignError) => voidsourceThe editor stopped working after
ready, or could not be loaded again.error.codesays how. It worked and stopped:renderer-failed(the canvas stopped drawing;detailis the renderer’s own code),editor-crashed(the editor shows its crash screen) orpage-crashed(the page’s process ended). A reload failed: any codereadycan reject with (load-failed,app-files-missing,ready-timeout,renderer-failed,page-crashedand the others); areload()that was waiting rejects with the same error.reload()brings the editor back.reloaded: (document: TacoDesignReloaded) => voidsourceThe editor’s page was loaded again (its crash screen’s Reload button, or
reload()) and is ready as it is afterready: the editor answers and its renderer has drawn.handedOversays which document it shows.saveRequested: () => voidsourceThe 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 withgetDocument()and store it.startupProgress: (progress: TacoDesignStartupProgress) => voidsourceHow 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 beforereadyresolves. A start that fails has no last report:readyrejects instead, and no report of that page load follows the rejection, so loading is over whenreadysettles either way. A page that is loaded again reports its progress again, in the same way: the last report arrives beforereloadedis told andreload()resolves, and a reload that fails has no last report and tellsfailureinstead.
sourceinterface TacoDesignChange extends DocumentInfoWhat a
changelistener gets: a few facts about the document, never the document.readonly origin: "user" | "host"source"host": caused bysetDocument()ornewDocument()."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.
sourceinterface TacoDesignExportOptionsWhat
exportLayers()takes: one layer or one page, a format, and the choices of the editor’s own Export section.readonly nodeId?: stringsourceThe layer to export, with everything inside it. Give this or
pageId.readonly pageId?: stringsourceThe page to export whole. Give this or
nodeId.readonly format: HostExportFormatsourceThe file format:
"png","jpeg","svg"or"pdf".readonly scale?: numbersourcePNG and JPEG: pixels per document unit. Default 1. Give at most one of
scale,widthandheight.readonly width?: numbersourcereadonly height?: numbersourcereadonly quality?: "low" | "medium" | "high"sourceJPEG: compression. PDF: resolution of effects that cannot be drawn as shapes. Default: high for JPEG, medium for PDF.
readonly resampling?: "detailed" | "basic"sourceHow pictures inside the layer are sampled. Default
"detailed".readonly ignoreOverlappingLayers?: booleansourceFor a layer: true (default) draws the layer alone, false also what else lies within its bounds.
readonly includeIdAttribute?: booleansourceSVG:
idattributes made from layer names. Default false.readonly suffix?: stringsourceAdded to the file name after the layer’s or page’s name.
readonly timeoutMs?: numbersourceHow long to wait for the export. Default 120 000 ms.
sourcetype HostExportFormat = "png" | "jpeg" | "svg" | "pdf"The file formats an export can be written in.
sourceinterface TacoDesignExportedFileOne exported file.
readonly fileName: stringsource<layer or page name><suffix>.<extension>, safe as a file name.readonly mimeType: stringsourceimage/png,image/jpeg,image/svg+xmlorapplication/pdf.readonly bytes: Uint8ArraysourceThe 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.
sourceinterface SelectionSummaryThe page that is shown and the selected layers: the answer to a
getSelectionrequest, and what a selection event carries.readonly pageId: string | nullsourceThe page that is shown, or null when the document has none.
readonly nodes: readonly SelectedLayer[]sourceThe selected layers, in the order they were selected. Empty when nothing is selected.
sourceinterface SelectedLayerOne selected layer.
readonly id: stringsourceThe 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: stringsourceThe layer’s name, as the layers panel shows it.
readonly type: stringsourceThe layer’s kind, the same word as its
typein 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?: stringsourceFor a layer of type
"shape": which shape, the same word as itsshapein the document ("rectangle","ellipse","line","path", ...).readonly componentId?: stringsourceFor a layer of type
"instance": the ID of the component it shows, as in the document’scomponents. For an instance inside another instance this is the component in effect after a swap by the outer one.readonly componentName?: stringsourceFor a layer of type
"instance": the name of that component.
sourceinterface FocusSummaryWhere the keyboard is: the answer to a
focusCanvasrequest, and what a focus event carries.readonly focused: booleansourceTrue while the editor’s page has the keyboard.
readonly target: "canvas" | "text" | "other" | nullsourceWhat 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.
sourceinterface TacoDesignReloaded extends DocumentInfoWhat a
reloadedlistener gets, and whatreload()resolves with.readonly handedOver: booleansourceTrue: 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.
sourceclass TacoDesignError extends ErrorThe error
readyrejects with, and the package’s functions throw.readonly code: TacoDesignErrorCodesourceWhat went wrong. Decide by this, never by the message, which is written for a person.
readonly url: string | undefinedsourceThe address that was being loaded.
readonly errorCode: number | undefinedsourceChromium’s network error number, such as
-2forERR_FAILED.readonly errorDescription: string | undefinedsourceChromium’s network error name, such as
ERR_FAILED.readonly httpStatus: number | undefinedsourceThe HTTP status the page answered with.
readonly rendererErrorCode: string | undefinedsourceThe editor’s own renderer error code, such as
WEBGPU_UNAVAILABLE.readonly path: string | undefinedsourceWhere a document is wrong, as a JSON path such as
$.nodes.n1.opacity.readonly detail: string | undefinedsourceThe document model’s own error code, such as
INVALID_SCHEMA; forrenderer-failedafterready, the renderer’s own code.new ( code: TacoDesignErrorCode, message: string, details: TacoDesignErrorDetails = {} )sourceMakes an error from a code, a message written for a person, and the extra facts that are known.
sourcetype 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"Why the editor could not be shown.
app-files-missing: the folder of editor files has noindex.html(the package was not assembled, orappDirectoryis wrong).load-failed: Electron could not load the page (errorCodeanderrorDescriptionare Chromium’s), or the page answered with an HTTP error status (httpStatus).scheme-not-registered: the page is not a secure context, which meanstacoDesignSchemewas 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: theappDirectoryoption 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 (rendererErrorCodeis the editor’s own code, such asWEBGPU_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. Beforereadyit rejectsready; afterwards it reaches thefailurelisteners.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.pathsays where it is wrong (a JSON path such as$.title) anddetailholds 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
failurelisteners, afterready:renderer-failed: the canvas stopped drawing (detailis the renderer’s own code).editor-crashed: the editor threw and shows its crash screen.page-crashed: see above.
sourceinterface TacoDesignErrorDetailsThe extra facts a
TacoDesignErrorcan carry besides its code and its message. Which of them are set depends on the code.readonly url?: stringsourceThe address that was being loaded.
readonly errorCode?: numbersourceChromium’s network error number, such as
-2forERR_FAILED.readonly errorDescription?: stringsourceChromium’s network error name, such as
ERR_FAILED.readonly httpStatus?: numbersourceThe HTTP status the page answered with.
readonly rendererErrorCode?: stringsourceThe editor’s own renderer error code, such as
WEBGPU_UNAVAILABLE.readonly path?: stringsourceWhere a document is wrong, as a JSON path such as
$.nodes.n1.opacity.readonly detail?: stringsourceThe 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.
sourceinterface TacoDesignStartupProgressWhat a
startupProgresslistener gets.readonly fraction: numbersourceThe share of start-up that is finished, 0 to 1. It never goes down within one page load.
readonly step: stringsourceThe step in progress, in order:
page-code,fonts,document,module-download,module-create,creating-session,installing-page, anddonein the last report. Later versions may add steps, so showlabeland do not switch on this.readonly label: stringsourceA few plain words for the step, such as “Downloading the renderer”.
readonly indeterminate: booleansourceTrue while the step in progress cannot say how long it is (a download without a known size).
fractionthen stays where it was: show a bar that moves without a percentage.
sourcetype TacoDesignStartupProgressDrawer = "editor" | "host"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 31const 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 141Changing 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.
- The old keys of a command that is moved or switched off stop working, and the editor no longer claims them: a menu accelerator on those keys runs.
- Nothing is applied when something is wrong. The call rejects with a
TacoDesignErrorwhosepathis the command ID:unknown-shortcut-command,invalid-shortcut-keys,shortcut-keys-in-use(two commands would run on the same key press) orshortcut-not-customizable(edit.paste). A wrongshortcutsoption rejectsTacoDesignView.readythe same way. - The app keeps the changes, the editor’s page does not. The view object holds them and gives them to the page again after every reload. Store them in the app’s own settings if they should outlast the app.
- They do not apply while text is being typed. While a text field or text on the canvas has the keyboard (the
focusevent says"text"), every key means what it means in a text field. The one exception is Save.
sourcetype ShortcutOverrides =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
nullto switch the shortcut off. Commands that are not named keep their default keys.readonly [commandId: string]: string | readonly string[] | nullsource
sourceinterface ShortcutCommandInfoOne command of the editor’s shortcut list.
Key combinations are written like Electron accelerators, for example
CmdOrCtrl+Shift+D;shortcuts.tsdescribes the spelling.readonly id: stringsourceThe stable name of the command, for example
edit.duplicate. Part of the public API.readonly title: stringsourceWhat the command does, in a few words of English.
readonly defaultKeys: readonly string[]sourceThe keys the command has when the embedding app changes nothing.
readonly keys: readonly string[]sourceThe keys in use now. Empty when the shortcut is switched off.
readonly customizable: booleansourceFalse 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.
- Only editor shortcuts are touched. A key that is no editor command (Cmd+Q, Cmd+W, the app’s own accelerators on free keys) is handled by the menu as in any Electron app.
- Nothing is done while a text field or text on the canvas has the keyboard, except for Save: Cmd+Z, Cmd+C, Cmd+X, Cmd+V and Cmd+A must reach the text.
- Paste (Cmd+V) is never touched, because on macOS the system’s paste comes from the menu’s
pasterole. - A changed shortcut counts with its new keys.
sourcetype MenuShortcutConflict = "editor" | "app" | "both"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.
sourceconst MENU_SHORTCUT_CONFLICTS: readonly MenuShortcutConflict[]Every value
menuShortcutConflictcan have.
A menu for an app that shows the editor
- Make Undo and Redo plain items, not roles. The
undorole runs the page’s text undo, which does nothing on the canvas. CallTacoDesignView.undoandTacoDesignView.redowhen the canvas or a panel has the keyboard, and do what the role does when a text field has it. - Keep Cut, Copy, Paste and Select All as roles. Text fields need them, and on macOS paste in the editor needs the
pasterole to exist. - Leave out the zoom roles (
zoomIn,zoomOut,resetZoom): they scale the whole view. Cmd with plus, minus and 0 zoom the canvas. - Call one save function from the menu’s Save item and from the
saveRequestedlistener. With"both"it would run twice; switchfile.saveoff (shortcuts: { "file.save": null }) then. - Give no
reloadrole an accelerator: Cmd+R is Rename in the editor, and a reload that does not go throughTacoDesignView.reloadloses the edits since the last read.
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 110The 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.
sourceconst CONTENT_SECURITY_POLICY: stringSent 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.comis the only other site, used to fetch a Google font the first time a document needs it.
sourceconst ALLOWED_PERMISSIONS: readonly string[]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.
sourceconst DEFAULT_PARTITION: "persist:taco-design"The partition used when the app names none.
persist:keeps it on disk.
sourceconst HOST_PROTOCOL_VERSION: 1Counts 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), thesaveRequestedevent and the four shortcut error codes, and after thenewDocumentrequest.
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.
sourceconst DESIGN_PROTOCOL_VERSION: "taco.design/1.0.0"Version of the design envelope, independent of document schema and renderer wire versions.
sourcefunction createDesignClient( send: (request: DesignRequest) => Promise<DesignReply>, target: DesignBinding, requestId: () => string )A typed transport adapter; the host supplies the binding returned by discovery.
sourceclass DesignError extends Errorreadonly committed: falsesourceRead and planning failures never commit a document change.
new (readonly code: string, message: string, readonly path = "$")source
sourcetype DesignArguments< K extends DesignOperation > = Infer<(typeof operations)[K]["input"]>Arguments validated by the catalog for an operation.
sourcetype DesignBinding = Infer<typeof bindingSchema>Identifies one editor and one document opening; never substitute a new epoch on retry.
sourceinterface DesignCapabilityCapability advertised by getState; public mutation remains disabled.
readonly versions: readonly string[]sourcereadonly binding: DesignBindingsourcereadonly mutationEnabled: falsesource
sourcetype DesignCommand = Infer<typeof commandSchema>A duplicate or translate command accepted by the read-only planner.
sourceinterface DesignEventStamped invalidation; not a resumable synchronization log.
readonly binding: DesignBindingsourcereadonly revision: numbersourcereadonly kind: "reset" | "transition"source
sourcetype DesignOperation = keyof typeof operationsOperations supported by this version of the read and planning catalog.
sourcetype 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; }; }Domain errors retain their code, path and committed flag, separate from transport failures.
sourcetype DesignRequest = { [K in DesignOperation]: { readonly protocol: typeof DESIGN_PROTOCOL_VERSION; readonly requestId: string; readonly target: DesignBinding; readonly operation: K; readonly arguments: DesignArguments<K>; } }[DesignOperation]A versioned request with an explicit document-opening target.
sourcetype DesignResult< K extends DesignOperation > = Infer<(typeof operations)[K]["output"]>Result validated by the catalog for an operation.
sourcetype DesignTarget = Infer<typeof targetReferenceSchema>A canonical node or a layer within a component instance.
sourceinterface TacoDesignRequestOptionsLimits the entire design call, including startup and capability discovery.
readonly timeoutMs?: numbersourceDefault 30 seconds. Must be a positive integer no greater than 2,147,483,647.
readonly signal?: AbortSignalsourceStops 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.
source#define TACO_RENDER_ABI_VERSION 1uVersion of this C ABI.
TacoRenderSessionOptions.abi_versionmust equal it, andTacoRenderCapabilities.abi_versionreports it.TypeScript:
RENDER_CORE_ABI_VERSION
source#define TACO_RENDER_WIRE_VERSION 24uNewest 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
source#define TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION 5uHeaderless standalone
VectorNetworkpayload version.TypeScript:
RENDER_CORE_VECTOR_NETWORK_WIRE_VERSION
source#define TACO_RENDER_EXTENDED_CAPABILITIES_VERSION 8uNewest 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).
wasmsourceTacoRenderStatus taco_render_get_capabilities( TacoRenderCapabilities *capabilities ) noexceptFills the version-1 capability struct. The caller sets
struct_size; it must be at least sizeof(TacoRenderCapabilities), or the call returnsINVALID_ARGUMENT. Needs no session.Called from TS, in helpers of:
PortableWasmRenderCoreModule.constructor,createOwnedWasmRenderCoreSession,attachPortableWasmRenderCoreSession
sourcestruct TacoRenderCapabilitiesWhat this build supports, written by
taco_render_get_capabilities. The caller setsstruct_size. Eachuint8_tfield 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 byTacoRenderExtendedCapabilitiesinstead.TypeScript:
PORTABLE_WASM_ABI_LAYOUT,RenderCoreCapabilitiesuint32_t struct_sizesourceuint32_t abi_versionsourceuint32_t wire_versionsourceuint8_t retained_scenesourceuint8_t structured_pathssourceuint8_t styled_text_recordssourceuint8_t embedded_resourcessourceuint8_t skiasourceuint8_t native_rastersourceuint8_t wasmsourceuint8_t webgpusourceuint8_t skparagraphsourceuint8_t harfbuzzsourceuint8_t icusourceuint8_t geometry_queriessourceuint8_t hit_testingsourceuint8_t dirty_boundssourceuint8_t[2] reservedsource
wasmsourceTacoRenderStatus taco_render_get_extended_capabilities( uint32_t capabilities_version, TacoRenderExtendedCapabilities *capabilities ) noexceptFills the extended capabilities up to
capabilities_version(1 toTACO_RENDER_EXTENDED_CAPABILITIES_VERSION). Only the firstTACO_RENDER_EXTENDED_CAPABILITIES_VERSION_N_SIZEbytes are written, so an older caller with a smaller struct stays safe. ReturnsUNSUPPORTED_VERSIONfor an unknown version andINVALID_ARGUMENTwhenstruct_sizeis smaller than that version’s size. Needs no session.Called from TS, in helpers of:
PortableWasmRenderCoreModule.constructor,createOwnedWasmRenderCoreSession,attachPortableWasmRenderCoreSession
sourcestruct TacoRenderExtendedCapabilitiesAppend-only capabilities that are intentionally separate from the frozen ABI-version-1
TacoRenderCapabilitieslayout. Callers request one declared version and providestruct_size; all reserved bytes are written as zero.TypeScript:
PORTABLE_WASM_ABI_LAYOUT,RenderCoreCapabilitiesuint32_t struct_sizesourceuint32_t capabilities_versionsourceuint32_t text_query_versionsourceuint8_t text_caretssourceuint8_t text_range_rectssourceuint8_t text_hit_testingsourceuint8_t text_layout_metricssourceuint8_t text_editing_overlayssourceuint8_t[14] reservedsourceuint32_t vector_query_versionsourceVersion 2 append-only fields. Version 1 callers expose only the first 32 bytes.
uint8_t vector_geometrysourceuint8_t vector_hit_testingsourceuint8_t[2] reserved_v2sourceuint32_t vector_editing_overlay_versionsourceVersion 3 append-only fields. Version 2 callers expose only the first 40 bytes.
uint8_t vector_editing_overlayssourceuint8_t[3] reserved_v3sourceuint32_t vector_boolean_versionsourceVersion 4 append-only fields. Version 3 callers expose only the first 48 bytes.
uint8_t vector_boolean_operationssourceuint8_t[3] reserved_v4sourceuint32_t font_metadata_versionsourceVersion 5 append-only fields. Version 4 callers expose only the first 56 bytes.
uint8_t font_metadatasourceuint8_t[3] reserved_v5sourceuint32_t incremental_render_versionsourceVersion 6 append-only fields. Version 5 callers expose only the first 64 bytes.
uint8_t incremental_renderingsourceuint8_t render_diagnosticssourceuint8_t[2] reserved_v6sourceuint32_t static_image_frame_versionsourceVersion 7 append-only fields. Version 6 callers expose only the first 72 bytes.
uint8_t static_image_framessourceuint8_t[3] reserved_v7sourceuint32_t video_frame_versionsourceVersion 8 append-only fields. Version 7 callers expose only the first 80 bytes.
uint8_t video_framessourceuint8_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.
opaquesourcestruct TacoRenderSessionAn 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_createand free it withtaco_render_session_destroy. A session is not thread-safe; call it from one thread at a time.
wasmsourceTacoRenderStatus taco_render_session_create( const TacoRenderSessionOptions *options, TacoRenderSession **session ) noexceptCreates an empty session and stores it in
*session(NULLon failure). options may beNULL. When given, a differentabi_versionreturnsUNSUPPORTED_VERSIONand astruct_sizesmaller than sizeof(TacoRenderSessionOptions) returnsINVALID_ARGUMENT.Called from TS:
InteractiveRetainedBackend.exportCompiledScene,InteractiveRetainedBackend.outlineCompiledStrokesCalled from TS, in helpers of:
RenderCoreModule.createSession,PortableWasmRenderCoreModule.createSession,createOwnedWasmRenderCoreSession
sourcestruct TacoRenderSessionOptionsOptional settings for
taco_render_session_create. Setstruct_sizeto sizeof(TacoRenderSessionOptions) andabi_versiontoTACO_RENDER_ABI_VERSION. There are no other options yet.TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t abi_versionsourceuint32_t[6] reservedsource
wasmsourcevoid taco_render_session_destroy(TacoRenderSession *session) noexceptFrees a session and everything it owns.
NULLis allowed.Called from TS:
RenderCoreSession.dispose,RenderCoreModule.createSession,PortableWasmRenderCoreModule.createSession,createOwnedWasmRenderCoreSessionCalled from TS, through
RenderCoreSession.dispose:outlineStrokesInThrowawaySession
sourceenum TacoRenderStatusThe result of every C function that can fail. Most failures also store a message that
taco_render_session_copy_last_errorreturns;taco_render_status_namegives the constant’s name.TypeScript:
RENDER_CORE_STATUSTACO_RENDER_STATUS_OK = 0sourceTACO_RENDER_STATUS_INVALID_ARGUMENT = 1sourceA null pointer, a
struct_sizethat is too small, or a bad value.TACO_RENDER_STATUS_UNSUPPORTED_VERSION = 2sourceAn ABI, wire or payload version this build does not accept.
TACO_RENDER_STATUS_MALFORMED_BATCH = 3sourceBatch bytes that are truncated or do not follow the wire format.
TACO_RENDER_STATUS_LIMIT_EXCEEDED = 4sourceA size, count or byte limit was exceeded.
TACO_RENDER_STATUS_INVALID_SCENE = 5sourceThe scene, or data the call needs from it, is not valid.
TACO_RENDER_STATUS_NOT_FOUND = 6sourceThe named node or resource does not exist.
TACO_RENDER_STATUS_CAPABILITY_UNAVAILABLE = 7sourceThis build cannot do it, for example painting without Skia.
TACO_RENDER_STATUS_BUFFER_TOO_SMALL = 8sourceAn output buffer is too small; the required sizes are still written.
TACO_RENDER_STATUS_INTERNAL_ERROR = 9sourceA native failure, such as an allocation failure or an exception.
TACO_RENDER_STATUS_STALE_GENERATION = 10sourceThe call’s
expected_generationis not the scene’s current generation.
wasmsourcesize_t taco_render_session_copy_last_error( const TacoRenderSession *session, char *destination, size_t destination_size ) noexceptCopies 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. ANULLsession 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.exportSceneand 21 more
RenderCoreSession.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.getLastErrorCalled from TS, through
RenderCoreSession.apply:outlineStrokesInThrowawaySessionCalled from TS, through
RenderCoreSession.getSceneInfo:outlineStrokesInThrowawaySession,measureExportFromLoadedSessionCalled from TS, through
PortableWasmRenderCoreSession.outlineStroke:outlineStrokesInThrowawaySessionCalled from TS, through
RenderCoreSession.getNodeGeometry:measureExportFromLoadedSessionCalled from TS, through
measureExportFromLoadedSession:exportFromLoadedSessionCalled from TS, through
PortableWasmRenderCoreSession.exportScene:exportFromLoadedSession
sourceconst char *taco_render_status_name(TacoRenderStatus status) noexceptReturns a static name for a status, such as “OK” or “
STALE_GENERATION”, or “UNKNOWN_STATUS” for a value outsideTacoRenderStatus.
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.
wasmsourceTacoRenderStatus taco_render_session_apply( TacoRenderSession *session, const uint8_t *batch, size_t batch_size, TacoRenderUpdateReport *report ) noexceptApplies 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 usuallyMALFORMED_BATCH,UNSUPPORTED_VERSION,LIMIT_EXCEEDEDorINVALID_SCENE. report may beNULL. 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_framesinstead, and a generation change clears the text and vector editing overlays.Called from TS:
InteractiveRetainedBackend.apply,InteractiveRetainedBackend.exportCompiledScene,InteractiveRetainedBackend.outlineCompiledStrokes,RenderChangeBatchEncoder,RenderCoreSession.applyCalled from TS, through
InteractiveRetainedBackend.apply:InteractiveRenderSession.loadDocument,InteractiveRenderSessionCoordinator.loadDocument,InteractiveRenderSession.applyDocumentUpdate,InteractiveRenderSessionCoordinator.applyDocumentUpdate,InteractiveRenderSession.setActivePage,InteractiveRenderSessionCoordinator.setActivePageCalled from TS, through
RenderCoreSession.apply:outlineStrokesInThrowawaySessionImplemented by:
RetainedSession.state,RetainedSession.apply,SessionCache.recordSuccessfulApply,SessionCache.invalidateMediaFrames,SessionCache.recordSceneUpdateTime
sourcestruct TacoRenderUpdateReportWritten after a successful batch apply.
commands_appliedcounts the batch’s commands;node_countandresource_countdescribe the scene after the batch. generation goes up by one for every batch with at least one command, andscene_digestis a hash of the retained scene contents.TypeScript:
PORTABLE_WASM_ABI_LAYOUT,RenderUpdateReportuint32_t struct_sizesourceuint32_t commands_appliedsourceuint32_t node_countsourceuint32_t resource_countsourceuint64_t generationsourceuint64_t scene_digestsource
wasmsourceTacoRenderStatus taco_render_session_apply_with_static_image_frames( TacoRenderSession *session, const uint8_t *batch, size_t batch_size, const TacoRenderStaticImageFrameSet *frame_set, TacoRenderUpdateReport *report ) noexceptAtomically 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_generationmust be the generation before the batch (elseSTALE_GENERATION). The frames must name every image asset the new scene uses, once each, in ascending asset-ID order; missing or extra IDs returnINVALID_SCENE. A scene with video returnsCAPABILITY_UNAVAILABLE; usetaco_render_session_apply_with_media_framesfor it.Called from TS:
InteractiveRetainedBackend.exportCompiledScene,RenderCoreSession.applyWithStaticImageFramesImplemented by:
RetainedSession.state,SessionCache.applyWithStaticImageFrames,SessionCache.recordSceneUpdateTime,isValidUtf8
wasmsourceTacoRenderStatus taco_render_session_apply_with_media_frames( TacoRenderSession *session, const uint8_t *batch, size_t batch_size, const TacoRenderMediaFrameSet *frame_set, TacoRenderUpdateReport *report ) noexceptAtomically 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 returnCAPABILITY_UNAVAILABLE.Called from TS:
RenderCoreSession.applyWithMediaFramesImplemented by:
RetainedSession.state,SessionCache.applyWithMediaFrames,SessionCache.recordSceneUpdateTime
sourcestruct TacoRenderStaticImageFrameSetexpected_generationis the current generation before applying the batch.TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsourceconst TacoRenderStaticImageFrame *framessourcesize_t frame_countsource
sourcestruct TacoRenderStaticImageFrameOne member of a complete, logical-asset-ID-deduplicated static image set.
content_revisionis a caller-owned, stable, nonzero content identity.REUSE_ACTIVEsupplies 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 exactbyte_length; the surrounding array has no independent record stride.TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t dispositionsourceuint32_t kindsourceuint32_t reservedsourceconst char *asset_idsourcesize_t asset_id_sizesourceuint64_t content_revisionsourceuint32_t widthsourceuint32_t heightsourceuint32_t row_bytessourceuint32_t reserved_v1sourceconst uint8_t *pixelssourcesize_t byte_lengthsource
wasmsourceTacoRenderStatus taco_render_session_update_video_frames( TacoRenderSession *session, const TacoRenderMediaFrameSet *frame_set, TacoRenderDirtyBounds *dirty_bounds ) noexceptReplaces the exact video-frame set without changing retained scene identity. generation, digest, history, and editing authority remain unchanged. On success
dirty_boundscontains the union of every visible user of a changed video asset. Portable/no-Skia builds returnCAPABILITY_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_framesfor this generation; without one it returnsINVALID_SCENE.Called from TS:
RenderCoreSession.updateVideoFramesImplemented 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.
wasmsourceTacoRenderStatus taco_render_session_get_scene_info( const TacoRenderSession *session, TacoRenderSceneInfo *info ) noexceptWrites the scene’s size, counts, generation and digest.
Called from TS:
InteractiveRetainedBackend.getSceneInfo,RenderCoreSession.getSceneInfoCalled from TS, in helpers of:
RenderCoreSession.updateVideoFrames,RenderCoreSession.getTextCarets,RenderCoreSession.getTextRangeGeometry,RenderCoreSession.hitTestTextand 11 more
RenderCoreSession.navigateText,RenderCoreSession.getTextLayoutMetrics,RenderCoreSession.getFontMetadata,RenderCoreSession.setTextEditingOverlay,RenderCoreSession.clearTextEditingOverlay,RenderCoreSession.getVectorGeometry,RenderCoreSession.hitTestVector,RenderCoreSession.setVectorEditingOverlay,RenderCoreSession.clearVectorEditingOverlay,PortableWasmRenderCoreSession.outlineStroke,RenderCoreSession.booleanVectorPathsCalled from TS, through
InteractiveRetainedBackend.getSceneInfo:InteractiveRenderSession.getSceneGeometrySnapshot,InteractiveRenderSession.getVectorGeometry,InteractiveRenderSession.hitTestVector,InteractiveRenderSession.loadDocument,InteractiveRenderSessionCoordinator.loadDocument,InteractiveRenderSession.applyDocumentUpdate,InteractiveRenderSessionCoordinator.applyDocumentUpdate,InteractiveRenderSession.setActivePageand 18 more
InteractiveRenderSessionCoordinator.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.resolveAccessibilityActionRouteV1Called from TS, through
RenderCoreSession.getSceneInfo:outlineStrokesInThrowawaySession,measureExportFromLoadedSessionCalled from TS, through
PortableWasmRenderCoreSession.outlineStroke:outlineStrokesInThrowawaySessionCalled from TS, through
measureExportFromLoadedSession:exportFromLoadedSessionImplemented by:
RetainedSession.state,RetainedSession.digest
sourcestruct TacoRenderSceneInfoThe 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,RenderSceneInfouint32_t struct_sizesourceuint32_t node_countsourceuint32_t resource_countsourceuint32_t reservedsourcedouble widthsourcedouble heightsourceuint64_t generationsourceuint64_t scene_digestsource
sourceTacoRenderStatus taco_render_session_get_node_info( const TacoRenderSession *session, const char *node_id, size_t node_id_size, TacoRenderNodeInfo *info ) noexceptWrites one node’s authored kind, z order, visibility, transform, opacity and widest stroke.
node_idisnode_id_sizebytes of UTF-8, not NUL-terminated, and must not be empty. ReturnsNOT_FOUNDfor an unknown node.Implemented by:
RetainedSession.findNode,Node.kind,NodeStyle.maximumStrokeWidth
sourcestruct TacoRenderNodeInfoA node’s authored values, written by
taco_render_session_get_node_info. kind is aTacoRenderNodeKind. 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_widthis the widest of the node’s strokes, or 0 without strokes.uint32_t struct_sizesourceuint32_t kindsourceint32_t z_ordersourceuint8_t visiblesourceuint8_t[3] reservedsourcedouble[6] transformsourcedouble opacitysourcedouble stroke_widthsource
wasmsourceTacoRenderStatus taco_render_session_get_node_geometry( const TacoRenderSession *session, const char *node_id, size_t node_id_size, TacoRenderNodeGeometry *geometry ) noexceptWrites one node’s computed geometry: world transform, local, world and visible bounds, painter order and effective opacity. Returns
NOT_FOUNDfor an unknown node.Called from TS:
InteractiveRetainedBackend.getNodeGeometry,RenderCoreSession.getNodeGeometryCalled from TS, through
InteractiveRetainedBackend.getNodeGeometry:InteractiveRenderSession.getNodeGeometry,InteractiveRenderSessionCoordinator.getNodeGeometryCalled from TS, through
RenderCoreSession.getNodeGeometry:measureExportFromLoadedSessionCalled from TS, through
measureExportFromLoadedSession:exportFromLoadedSessionImplemented by:
RetainedSession.findNode,RetainedSession.geometrySnapshot
sourcestruct TacoRenderNodeGeometryA node’s computed geometry, written by
taco_render_session_get_node_geometry. Each has_* byte says whether the matching bounds are present.world_boundsare in scene coordinates;visible_world_boundsare also clipped by clipping ancestors and are absent when the node is not effectively visible.painter_orderis the node’s back-to-front paint position, andeffective_opacityandeffectively_visibleinclude every ancestor.subtree_visible_boundsjoins 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,RenderNodeGeometryuint32_t struct_sizesourceuint32_t kindsourceuint8_t has_local_boundssourceuint8_t has_world_boundssourceuint8_t has_visible_world_boundssourceuint8_t effectively_visiblesourceuint64_t painter_ordersourcedouble effective_opacitysourcedouble[6] world_transformsourceTacoRenderRect local_boundssourceTacoRenderRect world_boundssourceTacoRenderRect visible_world_boundssourceuint8_t has_subtree_visible_boundssourceuint8_t[7] reservedsourceTacoRenderRect subtree_visible_boundssource
wasmsourceTacoRenderStatus 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 ) noexceptGeometry 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_SMALLwhen either buffer is short; pass capacities of 0 to ask for the sizes first. Node identifiers are packed back to back innode_id_bytes, and each record points into them.Called from TS:
InteractiveRetainedBackend.getGeometrySnapshot,RenderCoreSession.getGeometrySnapshotCalled from TS, through
InteractiveRetainedBackend.getGeometrySnapshot:InteractiveRenderSession.getSceneGeometrySnapshot,InteractiveRenderSessionCoordinator.getSceneGeometrySnapshotImplemented by:
RetainedSession.geometrySnapshot,RetainedSession.state
sourcestruct TacoRenderGeometryRecordOne node in
taco_render_session_get_geometry_snapshot. It holds the same values asTacoRenderNodeGeometry, with the has_* bytes packed into flags (TacoRenderGeometryFlags). The node identifier is thenode_id_sizebytes atnode_id_offsetin the call’s identifier buffer.TypeScript:
decodeWasmGeometryRecords,PORTABLE_WASM_ABI_LAYOUT,RenderNodeGeometryTypeScript, through
decodeWasmGeometryRecords:RenderCoreSession.getGeometrySnapshotuint32_t struct_sizesourceuint32_t node_id_offsetsourceuint32_t node_id_sizesourceuint32_t kindsourceuint32_t flagssourceuint32_t reservedsourceuint64_t painter_ordersourcedouble effective_opacitysourcedouble[6] world_transformsourceTacoRenderRect local_boundssourceTacoRenderRect world_boundssourceTacoRenderRect visible_world_boundssource
wasmsourceTacoRenderStatus 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 ) noexceptFinds the visible nodes under the scene point (
world_x,world_y); paths also count withinworld_tolerancescene units. Ancestor clips apply. Hits are returned topmost first.hit_countandnode_id_byte_countalways receive the required sizes; when either buffer is too small nothing else is written and the call returnsBUFFER_TOO_SMALL. The inputs must be finite and the tolerance non-negative.Called from TS:
InteractiveRetainedBackend.hitTest,RenderCoreSession.hitTestCalled from TS, through
InteractiveRetainedBackend.hitTest:InteractiveRenderSession.hitTest,InteractiveRenderSessionCoordinator.hitTest,InteractiveRenderSessionCoordinator.hitTestIncludingLockedImplemented by:
RetainedSession.state,RetainedSession.hitTest,toRect
sourcestruct TacoRenderHitOne node under a point, written by
taco_render_session_hit_test. The node identifier is thenode_id_sizebytes atnode_id_offsetin the call’s identifier buffer. kind is aTacoRenderNodeKind.TypeScript:
decodeWasmHitRecords,PORTABLE_WASM_ABI_LAYOUT,RenderHitTypeScript, through
decodeWasmHitRecords:RenderCoreSession.hitTestuint32_t struct_sizesourceuint32_t node_id_offsetsourceuint32_t node_id_sizesourceuint32_t kindsourceuint32_t reservedsourceuint64_t painter_ordersourceTacoRenderRect visible_world_boundssource
wasmsourceTacoRenderStatus taco_render_session_get_last_dirty_bounds( const TacoRenderSession *session, TacoRenderDirtyBounds *dirty_bounds ) noexceptWrites 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.applyWithMediaFramesCalled from TS, through
RenderCoreSession.apply:outlineStrokesInThrowawaySessionImplemented by:
RetainedSession.lastDirtyBounds,toRect
sourcestruct TacoRenderDirtyBoundsThe scene-space area an update changed:
old_boundscovers what the changed nodes painted before,new_boundswhat they paint after, andcombined_boundsboth. Each has_* byte says whether that rectangle is set.TypeScript:
PORTABLE_WASM_ABI_LAYOUT,RenderDirtyBoundsuint32_t struct_sizesourceuint8_t has_old_boundssourceuint8_t has_new_boundssourceuint8_t has_combined_boundssourceuint8_t reservedsourceuint64_t generationsourceTacoRenderRect old_boundssourceTacoRenderRect new_boundssourceTacoRenderRect combined_boundssource
wasmsourceTacoRenderStatus taco_render_session_map_viewport( const TacoRenderSession *session, const TacoRenderViewportOptions *options, TacoRenderViewportMapping *mapping ) noexceptComputes 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_ARGUMENTfor bad options andINVALID_SCENEwhen the scene has no positive size or the result is not finite.Called from TS:
RenderCoreSession.mapViewportImplemented by:
RetainedSession.state
sourcestruct TacoRenderViewportOptionsInput to
taco_render_session_map_viewport. mode is aTacoRenderViewportMode. The viewport size must be finite and positive.align_xandalign_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,ViewportMappingOptionsuint32_t struct_sizesourceuint32_t modesourcedouble viewport_widthsourcedouble viewport_heightsourcedouble align_xsourcedouble align_ysource
sourcestruct TacoRenderViewportMappingOutput 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,ViewportMappinguint32_t struct_sizesourceuint32_t reservedsourcedouble[6] scene_to_viewportsourcedouble[6] viewport_to_scenesourceTacoRenderRect 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.
wasmsourceTacoRenderStatus taco_render_session_render_rgba( TacoRenderSession *session, const TacoRenderRgbaTarget *target ) noexceptPaints 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_UNAVAILABLEin portable/no-Skia builds, and also when the scene has media without a current installed frame set.Called from TS:
RenderCoreSession.renderImplemented by:
renderRgba
sourcestruct TacoRenderRgbaTargetCaller-owned premultiplied RGBA8888 pixels for the render calls.
row_bytesmust be at least width * 4, andbyte_lengthat leastrow_bytes* height.TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t widthsourceuint32_t heightsourceuint32_t row_bytessourceuint8_t *pixelssourcesize_t byte_lengthsource
wasmsourceTacoRenderStatus taco_render_session_render_rgba_incremental( TacoRenderSession *session, const TacoRenderRgbaTarget *target, const TacoRenderIncrementalRenderOptions *options, TacoRenderIncrementalFrameReport *report ) noexceptLike
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 withTACO_RENDER_INCREMENTAL_RENDER_FORCE_FULL, it repaints everything. ReturnsSTALE_GENERATIONwhen options->expected_generationis not current. On success, including a frame with nothing to repaint, it writes the report. Portable/no-Skia builds returnCAPABILITY_UNAVAILABLE.Implemented by:
renderRgbaIncremental
sourcestruct TacoRenderIncrementalRenderOptionsInput to
taco_render_session_render_rgba_incremental. flags is a bitwise OR ofTacoRenderIncrementalRenderFlags;expected_generationmust be the scene’s current generation.uint32_t struct_sizesourceuint32_t flagssourceuint64_t expected_generationsource
sourcestruct TacoRenderIncrementalFrameReportTimings are finite milliseconds measured by the native core. A CPU render reports
submit_msas 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_sizesourceuint32_t flagssourceuint32_t nodes_paintedsourceuint32_t nodes_consideredsourceuint64_t generationsourceuint64_t scene_digestsourceTacoRenderDeviceRect device_damagesourcedouble scene_update_mssourcedouble layout_mssourcedouble paint_mssourcedouble submit_mssourceuint64_t retained_payload_bytessourceuint32_t decoded_font_entriessourceuint32_t text_layout_entriessource
wasmsourceTacoRenderStatus taco_render_session_export( TacoRenderSession *session, const TacoRenderExportRequest *request, TacoRenderExportResult *result ) noexceptExports 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_lengthis the size to copy. Portable/no-Skia builds returnCAPABILITY_UNAVAILABLE.Called from TS:
InteractiveRetainedBackend.exportCompiledScene,PortableWasmRenderCoreSession.exportSceneCalled from TS, through
PortableWasmRenderCoreSession.exportScene:exportFromLoadedSessionImplemented by:
exportScene
sourcestruct TacoRenderExportRequestInput to
taco_render_session_export; format is aTacoRenderExportFormat.TypeScript:
RenderExportRequestuint32_t struct_sizesourceuint32_t formatsourcedouble xsourceThe exported region in scene coordinates.
double ysourcedouble widthsourcedouble heightsourcedouble scalesourceOutput units per scene unit: raster pixels, SVG user units, PDF points.
const char *root_node_idsourceOptional. When nonempty, only this node and its descendants paint, so overlapping siblings and ancestors stay out of the export.
size_t root_node_id_sizesourceuint32_t image_samplingsourceHow image fills and media frames are resampled; a
TacoRenderExportImageSamplingvalue. Zero keeps the screen’s bilinear sampling.uint32_t flagssourceA bitwise OR of
TacoRenderExportFlagsvalues.double pdf_raster_dpisourcePDF only: the resolution, in dots per inch, at which effects PDF cannot express (blurs, shadows) are rasterized. Zero means 72.
sourcestruct TacoRenderExportResultOutput of a successful
taco_render_session_export.TypeScript:
RenderExportResultuint32_t struct_sizesourceuint32_t pixel_widthsourceOutput size: ceil(region * scale). Raster formats use it as pixels.
uint32_t pixel_heightsourceuint32_t row_bytessourceBytes per row for
TACO_RENDER_EXPORT_RGBA; zero for every other format.size_t byte_lengthsourceuint32_t request_struct_sizesourceThe request size this build read, so a host can tell that the image sampling, flags and PDF resolution fields were honored.
sourceenum TacoRenderExportFormatScene 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:
RenderExportFormatTACO_RENDER_EXPORT_RGBA = 1sourceTightly packed, unpremultiplied RGBA8888 rows of
pixel_width * 4bytes.TACO_RENDER_EXPORT_PNG = 2sourcePNG, when the build carries a PNG encoder; otherwise unavailable.
TACO_RENDER_EXPORT_SVG = 3sourceSVG with text converted to outlines.
TACO_RENDER_EXPORT_PDF = 4sourceSingle-page PDF whose page is the scaled region.
wasmsourcesize_t taco_render_session_copy_export_bytes( const TacoRenderSession *session, uint8_t *destination, size_t destination_size ) noexceptCopies up to
destination_sizebytes of the last successful export and returns the count copied. Returns 0 when session or destination isNULL.Called from TS:
PortableWasmRenderCoreSession.exportSceneCalled 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.
wasmsourceTacoRenderStatus 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 ) noexceptText 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, orNOT_FOUNDwhen missing), the query’sexpected_generationmust be current (elseSTALE_GENERATION), and portable/no-Skia builds returnCAPABILITY_UNAVAILABLE.This one writes every caret position of the text. With too small a
caret_capacity(pass 0 to ask) it returnsBUFFER_TOO_SMALLand writes only result->required_count. More thanTACO_RENDER_TEXT_QUERY_MAX_RECORDScarets returnLIMIT_EXCEEDED.Called from TS:
InteractiveRetainedBackend.getTextGeometry,RenderCoreSession.getTextCaretsImplemented by:
SessionCache.getTextCaretCount,RetainedSession.state,SessionCache.getTextCarets
wasmsourceTacoRenderStatus 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 ) noexceptWrites the selection rectangles between query->anchor and query->focus. Sizing works as in
taco_render_session_get_text_carets. A position outside the text returnsINVALID_ARGUMENT.Called from TS:
InteractiveRetainedBackend.getTextGeometry,RenderCoreSession.getTextRangeGeometryImplemented by:
RetainedSession.state,SessionCache.getTextRangeRects
wasmsourceTacoRenderStatus taco_render_session_hit_test_text( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextHitQuery *query, TacoRenderTextHitResult *result ) noexceptFinds 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.hitTestTextImplemented by:
SessionCache.hitTestTextMoves a caret position by one
TacoRenderTextNavigationOperation. When the caret cannot move, result->has_resultis 0. For line up and down, pass back the returnedpreferred_local_xto keep the caret’s column.Called from TS:
InteractiveRetainedBackend.navigateText,RenderCoreSession.navigateTextImplemented by:
SessionCache.navigateText
wasmsourceTacoRenderStatus taco_render_session_get_text_layout_metrics( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextQuery *query, TacoRenderTextLayoutMetrics *metrics ) noexceptWrites 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.getTextLayoutMetricsImplemented by:
SessionCache.getTextLayoutMetrics
sourcestruct TacoRenderTextBufferResultFor bounded queries, result is written on OK and
BUFFER_TOO_SMALL. OnBUFFER_TOO_SMALLrequired_countis exact and the record buffer is untouched. Every other failure leaves both the result and record buffers untouched.TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t required_countsourceuint64_t generationsource
sourcestruct TacoRenderTextPositionParagraph-relative offsets count authored UTF-16 code units.
TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t paragraph_indexsourceuint32_t utf16_offsetsourceuint32_t affinitysourceuint32_t reservedsource
wasmsourceTacoRenderStatus taco_render_session_set_text_editing_overlay( TacoRenderSession *session, const char *node_id, size_t node_id_size, const TacoRenderTextEditingOverlay *overlay ) noexceptPaints a text editing overlay on one text node: the selection from
selection_anchortoselection_focus, an optional IME composition range (flagHAS_COMPOSITION) and the caret (flagCARET_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 returnINVALID_ARGUMENT.Called from TS:
InteractiveRetainedBackend.setOverlays,RenderCoreSession.setTextEditingOverlayCalled from TS, through
InteractiveRetainedBackend.setOverlays:InteractiveRenderSession.setOverlays,InteractiveRenderSessionCoordinator.setOverlaysImplemented by:
SessionCache.setTextEditingOverlay
wasmsourceTacoRenderStatus taco_render_session_clear_text_editing_overlay( TacoRenderSession *session, const TacoRenderTextQuery *query ) noexceptRemoves the text editing overlay. Returns
STALE_GENERATIONwhen query->expected_generationis not current, andCAPABILITY_UNAVAILABLEin portable/no-Skia builds.Called from TS:
InteractiveRetainedBackend.setOverlays,RenderCoreSession.clearTextEditingOverlayCalled from TS, through
InteractiveRetainedBackend.setOverlays:InteractiveRenderSession.setOverlays,InteractiveRenderSessionCoordinator.setOverlaysImplemented by:
RetainedSession.state,SessionCache.clearTextEditingOverlay
sourcestruct TacoRenderTextEditingOverlayInput to
taco_render_session_set_text_editing_overlay. flags is a bitwise OR ofTacoRenderTextEditingOverlayFlags; the composition range is read only withHAS_COMPOSITION.TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t flagssourceuint64_t expected_generationsourceTacoRenderTextPosition selection_anchorsourceTacoRenderTextPosition selection_focussourceTacoRenderTextPosition composition_anchorsourceTacoRenderTextPosition 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.
wasmsourceTacoRenderStatus 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 ) noexceptReads 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 intoidentifier_bytesand each record points into them. result holds every count on OK andBUFFER_TOO_SMALL(pass capacities of 0 to ask). ReturnsNOT_FOUNDfor a missing node,INVALID_ARGUMENTfor a node that is not a path,STALE_GENERATION, andLIMIT_EXCEEDEDpastTACO_RENDER_VECTOR_QUERY_MAX_RECORDS. Works in every build.Called from TS:
InteractiveRetainedBackend.getVectorGeometry,RenderCoreSession.getVectorGeometryImplemented by:
RetainedSession.state,RetainedSession.findNode,RetainedSession.geometrySnapshot,queryVectorGeometry
sourcestruct TacoRenderVectorQueryVector 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_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsourcedouble[6] document_to_screensource
sourcestruct TacoRenderVectorBufferResultOn OK and
BUFFER_TOO_SMALLthis 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_LAYOUTuint32_t struct_sizesourceuint32_t vertex_countsourceuint32_t handle_countsourceuint32_t segment_countsourceuint32_t region_countsourceuint32_t identifier_byte_countsourceuint32_t[2] reservedsourceuint64_t generationsource
wasmsourceTacoRenderStatus 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 ) noexceptFinds the vector components (vertices, handles, segments, regions) of a path node near a screen point, within
tolerance_css_pxCSS pixels. Status and sizing rules matchtaco_render_session_get_vector_geometry.Called from TS:
InteractiveRetainedBackend.hitTestVector,RenderCoreSession.hitTestVectorImplemented by:
RetainedSession.state,RetainedSession.findNode,RetainedSession.geometrySnapshot,hitTestVectorComponents
wasmsourceTacoRenderStatus 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 ) noexceptInstalls 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_idnames the path node being edited.network_bytesis the edited network; components lists the selected parts, whose identifiers are packed intoidentifier_bytes.component_countandidentifier_byte_countmust equal the overlay’s fields.overlay_versionis read first, from the 8 bytes every version of the record starts with: a different version returnsUNSUPPORTED_VERSIONwhatever the record’s size, so a 104-byte version-1 record does. Astruct_sizeunder those 8 bytes, or under sizeof(TacoRenderVectorEditingOverlay) in a record of the current version, returnsINVALID_ARGUMENT. A differentnetwork_wire_versionreturnsUNSUPPORTED_VERSIONonce the rest of the record is valid; an oldexpected_generationreturnsSTALE_GENERATION. The overlay is never exported and is cleared by the next apply that changes the generation.Called from TS:
InteractiveRetainedBackend.setOverlays,RenderCoreSession.setVectorEditingOverlayCalled from TS, through
InteractiveRetainedBackend.setOverlays:InteractiveRenderSession.setOverlays,InteractiveRenderSessionCoordinator.setOverlaysImplemented by:
RetainedSession.state,SessionCache.setVectorEditingOverlay
sourcestruct TacoRenderVectorEditingOverlayThe network byte payload uses
TACO_RENDER_VECTOR_NETWORK_WIRE_VERSIONand is bounded byTACO_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_LAYOUTuint32_t struct_sizesourceuint32_t overlay_versionsourceuint64_t expected_generationsourcedouble[6] document_to_screensourcedouble screen_width_css_pxsourcedouble screen_height_css_pxsourcedouble screen_pixel_densitysourceuint32_t network_wire_versionsourceuint32_t component_countsourceuint32_t identifier_byte_countsourceuint32_t pending_handle_flagssourceTacoRenderVectorEditingPendingHandleFlags; zero when no handle is pending.
uint32_t pending_handle_vertex_indexsourcePosition of the handle’s vertex in the preview network’s vertex table.
uint32_t reservedsourcedouble pending_handle_dxsourceThe handle’s offset from its vertex, in the owner’s local units.
double pending_handle_dysource
wasmsourceTacoRenderStatus taco_render_session_clear_vector_editing_overlay( TacoRenderSession *session, const TacoRenderVectorEditingOverlayQuery *query ) noexceptRemoves the vector editing overlay. Returns
STALE_GENERATIONwhen query->expected_generationis not current, andCAPABILITY_UNAVAILABLEin portable/no-Skia builds.Called from TS:
InteractiveRetainedBackend.setOverlays,RenderCoreSession.clearVectorEditingOverlayCalled from TS, through
InteractiveRetainedBackend.setOverlays:InteractiveRenderSession.setOverlays,InteractiveRenderSessionCoordinator.setOverlaysImplemented by:
RetainedSession.state,SessionCache.clearVectorEditingOverlay
wasmsourceTacoRenderStatus 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 ) noexceptCombines 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.booleanVectorPathsImplemented by:
RetainedSession.state,RetainedSession.findNode,RetainedSession.geometry,booleanVectorPaths,encodeVectorNetwork
sourcestruct TacoRenderVectorBooleanQueryOne renderer-authoritative Boolean operation over two retained paths. The output uses
TACO_RENDER_VECTOR_NETWORK_WIRE_VERSIONin the primary path’s local coordinate space. The query never mutates retained state.TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t operationsourceuint64_t expected_generationsourceuint32_t[2] reservedsource
sourcestruct TacoRenderVectorBooleanResultOn OK and
BUFFER_TOO_SMALLthis 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_LAYOUTuint32_t struct_sizesourceuint32_t network_byte_countsourceuint64_t generationsource
wasmsourceTacoRenderStatus 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 ) noexceptWrites 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.outlineStrokeCalled from TS, through
PortableWasmRenderCoreSession.outlineStroke:outlineStrokesInThrowawaySessionImplemented by:
RetainedSession.state,RetainedSession.findNode,outlineNodeStroke,encodeVectorNetwork
sourcestruct TacoRenderStrokeOutlineQueryAsks for one stroke of a retained path or container as the filled shape it paints.
stroke_indexcounts 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:
RenderStrokeOutlineRequestuint32_t struct_sizesourceuint32_t stroke_indexsourceuint64_t expected_generationsourceuint32_t[2] reservedsource
sourcestruct TacoRenderStrokeOutlineResultWritten as
TacoRenderVectorBooleanResultis: on OK andBUFFER_TOO_SMALLwith the exact standalone network byte requirement; other failures leave both outputs untouched.TypeScript:
RenderStrokeOutlineSnapshotuint32_t struct_sizesourceuint32_t network_byte_countsourceuint64_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.
wasmsourceTacoRenderStatus taco_render_session_set_selection_chrome( TacoRenderSession *session, const uint8_t *bytes, size_t size ) noexceptReplaces 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_ARGUMENTand keep the old chrome. Portable/no-Skia builds returnCAPABILITY_UNAVAILABLE.Called from TS:
encodeSelectionChrome,InteractiveRetainedBackend.setOverlays,RenderSelectionChrome,RenderCoreSession.setSelectionChromeCalled from TS, through
encodeSelectionChrome:RenderCoreSession.setSelectionChromeCalled from TS, through
InteractiveRetainedBackend.setOverlays:InteractiveRenderSession.setOverlays,InteractiveRenderSessionCoordinator.setOverlaysImplemented by:
SessionCache.setSelectionChrome
wasmsourceTacoRenderStatus taco_render_session_set_canvas_overlay( TacoRenderSession *session, const uint8_t *bytes, size_t size ) noexceptReplaces 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
gapCSS 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 withtaco_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_ARGUMENTand keep the old overlay. Portable/no-Skia builds returnCAPABILITY_UNAVAILABLE.Called from TS:
encodeCanvasOverlay,InteractiveRetainedBackend.setOverlays,RenderCoreSession.setCanvasOverlayCalled from TS, through
encodeCanvasOverlay:RenderCoreSession.setCanvasOverlayCalled from TS, through
InteractiveRetainedBackend.setOverlays:InteractiveRenderSession.setOverlays,InteractiveRenderSessionCoordinator.setOverlaysImplemented by:
SessionCache.setCanvasOverlay
wasmsourceTacoRenderStatus taco_render_session_set_canvas_overlay_font( TacoRenderSession *session, const uint8_t *bytes, size_t size ) noexceptSets 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_ARGUMENTand keep the old font. Portable/no-Skia builds returnCAPABILITY_UNAVAILABLE.Called from TS:
RenderCoreSession.setCanvasOverlayFontImplemented 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.
wasmsourceTacoRenderStatus 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 ) noexceptLists the OpenType features (from GSUB and GPOS) and variation axes of a font resource in the scene. Returns
NOT_FOUNDwhen no font resource has that ID andSTALE_GENERATIONwhen query->expected_generationis not current. Sizing works as in the text queries: result holds both counts on OK andBUFFER_TOO_SMALL. Portable/no-Skia builds returnCAPABILITY_UNAVAILABLE.Called from TS:
InteractiveRetainedBackend.getFontMetadata,RenderCoreSession.getFontMetadataImplemented by:
SessionCache.getFontMetadata
sourcestruct TacoRenderFontMetadataResultOn OK and
BUFFER_TOO_SMALLthis 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_LAYOUTuint32_t struct_sizesourceuint32_t feature_countsourceuint32_t axis_countsourceuint32_t reservedsourceuint64_t generationsource
wasmsourceTacoRenderStatus 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 ) noexceptCopies 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_sizereceives the table’s byte count; whentable_capacityis smaller (pass 0 to ask for the size) nothing is copied and the call returnsBUFFER_TOO_SMALL. ReturnsNOT_FOUNDwhen the font has no such table,INVALID_SCENEwhen the bytes are not a decodable font, andCAPABILITY_UNAVAILABLEin 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.
wasmsourceuint32_t taco_render_wasm_abi_version() noexceptReturns
TACO_RENDER_ABI_VERSION.Called from TS, in helpers of:
PortableWasmRenderCoreModule.constructor,createOwnedWasmRenderCoreSession,attachPortableWasmRenderCoreSession
wasmsourceuint32_t taco_render_wasm_wire_version() noexceptReturns
TACO_RENDER_WIRE_VERSION.Called from TS, in helpers of:
PortableWasmRenderCoreModule.constructor,createOwnedWasmRenderCoreSession,attachPortableWasmRenderCoreSession
wasmsourceuint32_t taco_render_wasm_vector_network_wire_version() noexceptReturns TACO_RENDER_VECTOR_NETWORK_WIRE_VERSION.
Called from TS, in helpers of:
PortableWasmRenderCoreModule.constructor,createOwnedWasmRenderCoreSession,attachPortableWasmRenderCoreSession
wasmsourceuint32_t taco_render_wasm_portable_capabilities() noexceptReturns 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
wasmsourcePortableWasmSession *taco_render_wasm_session_create() noexceptCreates an empty portable session, or returns null when allocation fails.
wasmsourcevoid taco_render_wasm_session_destroy(PortableWasmSession *session) noexceptFrees a portable session. Null is allowed.
wasmsourceuint32_t taco_render_wasm_session_apply( PortableWasmSession *session, const uint8_t *batch, size_t batch_size ) noexceptApplies one batch, all or nothing, and returns a
TacoRenderStatus.
wasmsourceconst char *taco_render_wasm_session_snapshot( PortableWasmSession *session ) noexceptReturns a JSON summary of the scene, its geometry and painter order, or
nullon failure. The string lives until the next snapshot or apply.
wasmsourceconst char *taco_render_wasm_session_last_error( const PortableWasmSession *session ) noexceptReturns 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.
sourceclass taco::render::RetainedSessionThe 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()sourceApplyResult apply(std::span<const uint8_t> batch, ApplyTransaction *transaction)sourceApplies 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:
RenderChangeBatchEncoderC ABI:
taco_render_session_applyconst SceneState &state() constsourceC 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_viewportand 9 more
taco_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_strokeconst Node *findNode(std::string_view id) constsourceThe 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_strokeuint64_t digest() constsourceA hash of the scene contents, cached until the next apply.
size_t retainedPayloadBytes() constsourceconst SessionApplyStats &applyStats() constsourceconst ApplyChangeSummary &lastApplyChanges() constsourcebool sceneContainsMedia() constsourcebool hasUnboundedVisibleText() constsourcevoid resetApplyStats()sourceconst SceneGeometry &geometry() constsourcestd::shared_ptr<const SceneGeometry> geometrySnapshot() constsourceC ABI:
taco_render_session_get_node_geometry,taco_render_session_get_geometry_snapshot,taco_render_session_get_vector_geometry,taco_render_session_hit_test_vectorconst PaintedStrokeCache &paintedStrokes() constsourceWhat hit tests have kept for path strokes, for tests and measurements.
void setMaximumPaintedStrokeBytes(size_t maximum_bytes)sourceReplaces 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 ) constsourceVisible nodes under a scene point, topmost first (see
pointHitsNode).C ABI:
taco_render_session_hit_testconst DirtyBounds &lastDirtyBounds() constsourceThe area changed by the most recent successful apply.
sourceclass taco::render::ApplyTransactionHolds a batch apply open so a caller can check more state before keeping it.
SessionCache.applyWithMediaFramesuses one to install frames and scene together, or neither.ApplyTransaction()ApplyTransaction(const ApplyTransaction &) = deletesourceApplyTransaction(ApplyTransaction &&) noexcept~ApplyTransaction()sourcevoid commit() noexceptsourceKeep the applied session state. A transaction that is still active is rolled back automatically when it leaves scope.
void rollback() noexceptsource
sourcestruct taco::render::ApplyResultThe outcome of
RetainedSession.apply: a status with an error message on failure, the command count and the dirty bounds on success.TacoRenderStatus statussourceuint32_t commands_appliedsourcestd::string errorsourceDirtyBounds dirty_boundssource
sourcestruct taco::render::SceneStateThe retained scene itself: size, generation, nodes and resources by ID.
double widthsourcedouble heightsourceuint64_t generationsourcestd::map<std::string_view, std::shared_ptr<const Node>, std::less<>> nodessourceKeys 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
sourcestruct taco::render::NodeOne retained node: its identity, parent, stacking, style and the payload for its kind (path, text, container or media).
std::string idsourcestd::string parent_idsourceint32_t z_ordersourcebool visiblesourceBlendMode blend_modesourceWire 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 masksourcebool vertex_cornerssourceWire version 12 node flag bit 2: a path appends its vertex corner table.
bool stroke_capssourceWire version 13 node flag bit 4: a path appends its two stroke caps.
Transform transformsourceTransform-only wire updates intentionally mutate this field while the immutable node identity and payload remain shared with paint caches.
NodeStyle stylesourceuint16_t source_wire_versionsourcestd::variant<PathData, TextData, ContainerData, MediaData> datasourcevoid setTransform(const Transform &value) constsourceTacoRenderNodeKind kind() constsource
sourcestruct taco::render::SceneGeometryThe 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 generationsourcesize_t painter_order_id_bytessourcestd::vector<std::shared_ptr<const Node>> retained_nodessourceGeometry 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_transformfor the transform captured by this geometry result.std::map<std::string_view, ComputedNodeGeometry, std::less<>> nodessourcestd::vector<std::string_view> painter_ordersourceSceneHitIndex hit_indexsourcestd::map<std::string_view, std::string_view, std::less<>> mask_ownerssourceMasked node ID to the visible sibling mask that clips it. Views into retained nodes, recomputed in full on every geometry build.
sourcestruct taco::render::ComputedNodeGeometryGeometry 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_boundssourceBounds world_boundssourceBounds visible_world_boundssourceBounds subtree_visible_boundssourceEverything 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_paintedsourceWhether 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_transformsourceuint64_t painter_ordersourcebool effectively_visiblesourcedouble effective_opacitysource
sourcestruct taco::render::DirtyBoundsThe scene-space area one update changed, before and after. The C ABI copies it into
TacoRenderDirtyBounds.Bounds old_boundssourceBounds new_boundssourceBounds combined_boundssourceuint64_t generationsource
sourcestruct taco::render::HitRecordOne node hit by
RetainedSession.hitTest, with its geometry.std::shared_ptr<const Node> nodesourceComputedNodeGeometry geometrysource
Stroke settings the session reads from the wire: end caps, corner joins and dash ends, in Figma’s terms.
sourceenum class taco::render::StrokeCap : uint8_tWire version 13 stroke end caps, in Figma’s terms. A path without explicit caps keeps the historical round caps at both ends.
TypeScript:
RetainedStrokeCapkNone = 0sourcekRound = 1sourcekSquare = 2sourcekLineArrow = 3sourcekTriangleArrow = 4sourcekReversedTriangle = 5sourcekCircleArrow = 6sourcekDiamondArrow = 7source
sourceenum class taco::render::StrokeJoin : uint8_tWire version 23 stroke joins and dash ends, in Figma’s terms.
TypeScript:
RetainedStrokeJoinkMiter = 0sourcekBevel = 1sourcekRound = 2source
sourceenum class taco::render::DashCap : uint8_tTypeScript:
RetainedDashCapkNone = 0sourcekRound = 1sourcekSquare = 2source
Skia adapter
SessionCache keeps the Skia state of one session, and the functions after it paint the retained scene through it.
sourceclass taco::render::skia_adapter::SessionCacheOwns 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 &) = deletesourceSessionCache(SessionCache &&) = delete~SessionCache()sourceTacoRenderStatus applyWithStaticImageFrames( RetainedSession *session, std::span<const uint8_t> batch, uint64_t expected_generation, std::span<const StaticImageFrame> frames, ApplyResult *result, std::string *error )sourceApplies 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.
TacoRenderStatus applyWithMediaFrames( RetainedSession *session, std::span<const uint8_t> batch, uint64_t expected_generation, std::span<const MediaFrame> frames, ApplyResult *result, std::string *error )sourceLike
applyWithStaticImageFrames, for a mixed set of image and video frames that must match every media asset of the new scene.TacoRenderStatus updateVideoFrames( const RetainedSession &session, uint64_t expected_generation, std::span<const MediaFrame> frames, DirtyBounds *dirty_bounds, std::string *error )sourceReplaces 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
applyWithMediaFramesfor this generation.uint32_t lastVideoFrameUpdateIndexedAssetCountForTesting() const noexceptsourceNative 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 noexceptsourceuint32_t lastRgbaRollbackAllocationsForTesting() const noexceptsourceTacoRenderStatus getTextCarets( const RetainedSession &session, std::string_view node_id, const TacoRenderTextQuery &query, std::vector<TacoRenderTextCaret> *carets, struct RenderStatistics *statistics, std::string *error )sourceText 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.
TacoRenderStatus getTextCaretCount( const RetainedSession &session, std::string_view node_id, const TacoRenderTextQuery &query, uint32_t *caret_count, struct RenderStatistics *statistics, std::string *error )sourceCounts the carets without building them, so the C function can report
BUFFER_TOO_SMALLfirst.TacoRenderStatus getTextRangeRects( const RetainedSession &session, std::string_view node_id, const TacoRenderTextRangeQuery &query, std::vector<TacoRenderTextRect> *rects, struct RenderStatistics *statistics, std::string *error )sourceBuilds the selection rectangles between two text positions.
TacoRenderStatus hitTestText( const RetainedSession &session, std::string_view node_id, const TacoRenderTextHitQuery &query, TacoRenderTextHitResult *result, struct RenderStatistics *statistics, std::string *error )sourceFinds the caret position nearest to a node-local point.
Moves a text position by one navigation operation.
TacoRenderStatus getTextLayoutMetrics( const RetainedSession &session, std::string_view node_id, const TacoRenderTextQuery &query, TacoRenderTextLayoutMetrics *metrics, struct RenderStatistics *statistics, std::string *error )sourceReports content size, suggested frame size and line counts.
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 )sourceLists a font resource’s OpenType features and variation axes. The result is cached with the decoded font.
TacoRenderStatus setTextEditingOverlay( const RetainedSession &session, std::string_view node_id, const TacoRenderTextEditingOverlay &overlay, struct RenderStatistics *statistics, std::string *error )sourceInstalls the selection, composition and caret painted on one text node, replacing any earlier text overlay.
void clearTextEditingOverlay(const RetainedSession &session) noexceptsourceRemoves the text editing overlay and marks its area for repaint.
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 )sourceInstalls the preview of an edited vector network on one path node, with its selected components, replacing any earlier vector overlay.
void clearVectorEditingOverlay(const RetainedSession &session) noexceptsourceRemoves the vector editing overlay and marks its area for repaint.
TacoRenderStatus setSelectionChrome( std::span<const uint8_t> bytes, std::string *error )sourceReplaces 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,RenderSelectionChromeCalled from TS, through
encodeSelectionChrome:RenderCoreSession.setSelectionChromeTacoRenderStatus setCanvasOverlay( std::span<const uint8_t> bytes, std::string *error )sourceReplaces 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:
encodeCanvasOverlayCalled from TS, through
encodeCanvasOverlay:RenderCoreSession.setCanvasOverlayTacoRenderStatus setCanvasOverlayFont( std::span<const uint8_t> bytes, std::string *error )sourceSets the font frame titles in the canvas overlay are drawn with. Empty bytes remove it, and titles are then not drawn.
void invalidateStaticImageFrames() noexceptsourceSame as
invalidateMediaFrames.void invalidateMediaFrames() noexceptsourceDrops every installed image and video frame. Called after a plain batch apply, which carries no frames.
C ABI:
taco_render_session_applyvoid recordSuccessfulApply( const RetainedSession &session, const DirtyBounds &dirty_bounds, bool generation_changed ) noexceptsourceUpdates 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_applyvoid recordSceneUpdateTime(double scene_update_ms) noexceptsourceAdds apply time to the
scene_update_msof 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
sourcestruct taco::render::skia_adapter::RenderStatisticsuint32_t nodes_consideredsourceuint32_t nodes_paintedsourceuint32_t paths_paintedsourceuint32_t vector_paths_preparedsourceuint32_t vector_path_cache_hitssourceuint32_t vector_region_tight_bounds_computessourceuint32_t vector_regions_visitedsourceuint32_t vector_regions_paintedsourceuint32_t vector_region_fills_paintedsourceuint32_t inherited_vector_region_fills_paintedsourceuint32_t explicit_vector_region_fills_paintedsourceuint32_t vector_region_gradient_fills_paintedsourceuint32_t vector_network_strokes_paintedsourceuint32_t vector_segments_strokedsourceuint32_t vector_stroke_contourssourceCount 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_paintedsourceCount of shaped SkParagraph fragments painted. List markers and split first-line indents make this larger than
authored_paragraphs_painted.uint32_t unresolved_glyphssourceuint32_t font_collection_cache_hitssourceuint32_t font_collections_builtsourceuint32_t fonts_decodedsourceuint32_t decoded_font_cache_hitssourceuint32_t unicode_cache_hitssourceuint32_t unicode_services_builtsourceuint32_t text_layout_cache_hitssourceuint32_t text_layout_font_set_rebuildssourceuint32_t rgba_rollback_allocationssourceuint32_t opacity_layer_unbounded_savessourceuint32_t ancestor_walk_vector_allocationssourceuint32_t text_paragraph_copiessourceuint32_t text_mapping_validationssourceuint32_t text_layouts_builtsourceuint32_t text_layout_cache_invalidationssourceuint32_t paragraphs_builtsourcedouble layout_mssourcedouble paint_mssource
sourceTacoRenderStatus taco::render::skia_adapter::renderRgba( const RetainedSession &session, SessionCache *cache, const TacoRenderRgbaTarget &target, RenderStatistics *statistics, std::string *error )Paints the retained scene into a caller-owned, premultiplied RGBA8888 CPU buffer. No platform font manager or GPU context is used.
sourceTacoRenderStatus taco::render::skia_adapter::renderRgbaIncremental( const RetainedSession &session, SessionCache *cache, const TacoRenderRgbaTarget &target, const TacoRenderIncrementalRenderOptions &options, TacoRenderIncrementalFrameReport *report, std::string *error )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.
sourceTacoRenderStatus taco::render::skia_adapter::renderCanvas( const RetainedSession &session, SessionCache *cache, SkCanvas *canvas, uint32_t width, uint32_t height, RenderStatistics *statistics, std::string *error )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.
sourceTacoRenderStatus 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 )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.
sourcevoid taco::render::skia_adapter::acknowledgeIncrementalRenderTarget( const RetainedSession &session, SessionCache *cache, uintptr_t target_identity, uint32_t width, uint32_t height ) noexceptCommits a prepared incremental canvas render after backend submission.
sourcevoid taco::render::skia_adapter::invalidateIncrementalRenderTarget( SessionCache *cache ) noexceptMakes the next incremental canvas render repaint the complete target.
sourceTacoRenderStatus taco::render::skia_adapter::exportScene( const RetainedSession &session, SessionCache *cache, const TacoRenderExportRequest &request, std::vector<uint8_t> *bytes, TacoRenderExportResult *result, std::string *error )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
sourceTacoRenderStatus 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 )Decodes font bytes (TrueType, OpenType/CFF, WOFF or WOFF2) and copies one OpenType table into
out. Seetaco_render_copy_font_tablefor the statuses.C ABI:
taco_render_copy_font_table
sourceTacoRenderStatus taco::render::skia_adapter::preflightInstalledMediaFrames( const RetainedSession &session, const SessionCache *cache ) noexceptVerifies 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.
sourceTacoRenderStatus taco::render::skia_adapter::buildVectorRegionPaths( const PathData &network, std::vector<SkPath> *region_paths, std::string *error )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.
sourceVectorBooleanResult 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 )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.
sourcestruct taco::render::skia_path_ops::VectorBooleanResultTacoRenderStatus statussourcePathData networksourcestd::string errorsource
sourceTacoRenderStatus taco::render::skia_adapter::buildNodeStrokeOutline( const Node &node, size_t stroke_index, SkPath *outline, std::string *error )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_indexcounts 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) leavesoutlineempty. 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.
sourceVectorNetworkResult taco::render::skia_path_ops::vectorNetworkFromPath( const SkPath &path, std::string_view id_namespace )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).
pathmust hold the non-overlapping closed contours Skia PathOps (Op,Simplify) returns, filled even-odd; runSimplifyfirst 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
kMaximumOutlineContourscontours is refused with TACO_RENDER_STATUS_LIMIT_EXCEEDED before any comparison, and so is one whose nesting would walk more thankMaximumOutlineContainmentSegmentssegments. A path that would become more segments thanmaximumOutlineNetworkSegmentsallows for the length ofid_namespaceis refused the same way, counted from its verbs before any arc is cut into cubics and before anything of the network is built.
sourceVectorNetworkResult taco::render::skia_path_ops::outlineNodeStroke( const Node &node, size_t stroke_index, std::string_view result_id_namespace )One stroke of a retained path or container as a vector network of the area it paints, in the node’s local coordinates:
buildNodeStrokeOutlinefollowed byvectorNetworkFromPath. A stroke that paints nothing gives an empty network.
sourcestruct taco::render::skia_path_ops::VectorNetworkResultTacoRenderStatus statussourcePathData networksourcestd::string errorsource
Wire reader and geometry helpers
Bounded reading of wire bytes, and the geometry functions the session and queries share.
sourceclass taco::render::WireReaderReads little-endian values from a byte span with bounds checks. A read past the end returns
falseand recordsMALFORMED_BATCHwith a message;status()anderror()keep the first failure. The batch decoder and the overlay parsers use it.TypeScript:
RenderChangeBatchEncoderWireReader(std::span<const uint8_t> bytes)sourcesize_t remaining() constsourcesize_t offset() constsourcebool atEnd() constsourceTacoRenderStatus status() constsourceconst std::string &error() constsourcebool readU8(uint8_t *value)sourcebool readU16(uint16_t *value)sourcebool readU32(uint32_t *value)sourcebool readI32(int32_t *value)sourcebool readU64(uint64_t *value)sourcebool readF32(float *value)sourcebool readF64(double *value)sourcebool readBytes(size_t count, std::span<const uint8_t> *value)sourcebool readString(size_t maximum_bytes, bool allow_empty, std::string *value)sourcebool readReservedZero(size_t count)sourcebool readSubreader(size_t count, WireReader *value)sourcebool requireCountFits( uint64_t count, size_t minimum_item_bytes, const char *context )sourcebool requireEnd(const char *context)sourcebool fail(TacoRenderStatus status, std::string message)source
sourcebool taco::render::isValidUtf8(std::span<const uint8_t> bytes)
sourceSceneGeometry taco::render::computeSceneGeometry( const SceneState &state, GeometryBuildStats *stats )SceneGeometry taco::render::computeSceneGeometry( const SceneState &state, const ChildMap &children, GeometryBuildStats *stats )Computes the geometry of every node from scratch: world transforms, bounds, clips, painter order, masks and the hit index.
sourceSceneGeometry 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 )Like
computeSceneGeometry, but recomputes only the changed nodes and reuses the rest fromprevious.
sourcebool 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 )Whether a scene point hits a visible node, after ancestor clips: a path within
world_toleranceof 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.
sourceTacoRenderStatus taco::render::queryVectorGeometry( const PathData &path, const ComputedNodeGeometry &geometry, const Transform &document_to_screen, VectorGeometryQueryResult *result )Builds the records for
taco_render_session_get_vector_geometry.
sourceTacoRenderStatus 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 )Builds the records for
taco_render_session_hit_test_vector.
sourceEncodeVectorNetworkResult taco::render::encodeVectorNetwork( const PathData &network, size_t maximum_bytes )Encodes one exact standalone wire-v4
VectorNetworkwithin the supplied cap.Called from TS:
decodeVectorNetworkRecordCalled from TS, through
decodeVectorNetworkRecord:PortableWasmRenderCoreSession.outlineStroke,RenderCoreSession.booleanVectorPathsC ABI:
taco_render_session_boolean_vector_paths,taco_render_session_outline_stroke
sourceDecodeVectorNetworkResult taco::render::decodeVectorNetwork( std::span<const uint8_t> bytes, uint16_t wire_version )Decodes one exact standalone wire-v4
VectorNetworkwithout mutating a session.Called from TS:
encodeVectorNetworkRecordCalled from TS, through
encodeVectorNetworkRecord:decodeVectorNetworkRecord,RenderCoreSession.setVectorEditingOverlay
Other exports
Entries not yet placed in a section above, in alphabetical order.
sourcebool taco::render::anyRoundedVertex(const PathData &path)Whether any vertex of
pathhas a corner radius.
sourcestruct taco::render::ApplyChangeSummarybool all_nodessourcebool resources_changedsourcestd::set<std::string, std::less<>> node_idssource
sourceenum class taco::render::BlendMode : uint8_tWire version 10 blend modes, in CSS mix-blend-mode order. Nodes carry one since wire version 10 and paints since wire version 19.
TypeScript:
RetainedBlendModekNormal = 0sourcekDarken = 1sourcekMultiply = 2sourcekColorBurn = 3sourcekLighten = 4sourcekScreen = 5sourcekColorDodge = 6sourcekOverlay = 7sourcekSoftLight = 8sourcekHardLight = 9sourcekDifference = 10sourcekExclusion = 11sourcekHue = 12sourcekSaturation = 13sourcekColor = 14sourcekLuminosity = 15source
sourcestruct taco::render::Boundsbool validsourcedouble min_xsourcedouble min_ysourcedouble max_xsourcedouble max_ysource
sourcebool taco::render::branchJoinPair( const std::vector<double> &angles, size_t *first, size_t *second )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.
anglesholds the direction each end leaves the vertex in, in radians asstd::atan2gives them;firstandsecondreceive the two ends’ places in it. False when no two ends are joined: fewer than three ends, or no side that open.buildStrokeContoursjoins 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).
sourceChildMap taco::render::buildChildMap( const SceneState &state, bool sort_siblings )
sourcebool taco::render::buildStrokeContours( const PathData &path, bool joins_open_runs, StrokeContourOrder order, std::vector<StrokeContour> *contours, std::string *error )Fills
contourswith the contours a stroke ofpathis painted along, in the order the renderer adds them to its path.pathis 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
ordersays;joins_open_runsisstrokesJoinOpenRunsfor 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
errorset, 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.
sourcetypedef std::map<std::string_view, std::vector<const Node *>, std::less<>> taco::render::ChildMap
sourcestruct taco::render::Colorfloat redsourcefloat greensourcefloat bluesourcefloat alphasource
sourcestruct taco::render::ContainerDatadouble widthsourcedouble heightsourcebool clipsourcestd::array<double, 4> corner_radiisourceWire 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_smoothingsourcebool rounded() constsourcebool uniformCorners() constsourcevoid setUniformCornerRadius(double radius)sourcedouble effectiveCornerRadius(size_t corner) constsourceThe radius actually used at one corner: a radius beyond half the shorter side clamps.
sourcestruct taco::render::DecodeVectorNetworkResultTacoRenderStatus statussourcePathData networksourcestd::string errorsource
sourcestruct taco::render::DrawnPointOne point of a path as the renderer draws it.
float xsourcefloat ysource
sourcestruct taco::render::DrawnSegmentOne 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;
prepareDrawnSegmentsays how far apart the two are.Shape shapesourcestd::array<DrawnPoint, 9> pointssourceFrom 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_countsourcefloat arc_piece_weightsourceEach 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.
sourceenum class taco::render::DrawnSegment::Shape : uint8_tkLinesourcekCubicsourcekArcPiecessource
sourceenum class taco::render::DrawnSegmentProblem : uint8_tWhy a segment cannot be drawn.
kNonesourcekVertexOutOfRangesourceA number is not finite or is too large for single precision:
kControlPointOutOfRangesourceA number is not finite or is too large for single precision:
kArcOutOfRangesourceA number is not finite or is too large for single precision:
kUnsupportedKindsourceA number is not finite or is too large for single precision:
sourcestruct taco::render::EncodeVectorNetworkResultTacoRenderStatus statussourcestd::vector<uint8_t> bytessourcestd::string errorsource
sourcestruct taco::render::FontVariationstd::array<char, 4> tagsourcedouble valuesource
sourcevoid taco::render::forEachImageAssetReference( const Node &node, const std::function<void (std::string_view)> &visit )Visits every image paint asset ID the node references, media nodes included;
visitreceives each ID once per reference.
sourcestruct taco::render::GeometryBuildStatsuint64_t nodes_computedsourceuint64_t path_bounds_index_buildssourcestd::vector<std::string_view> computed_node_idssource
sourcestruct taco::render::GradientStopdouble offsetsourceColor colorsource
sourcestruct taco::render::HitTestStatsuint64_t path_index_buildssourceuint64_t segment_flatten_callssourceuint64_t clip_parent_string_copiessourceuint64_t rounded_path_buildssourceCalls of
roundPathVerticesand ofbuildStrokeContours.uint64_t stroke_contour_buildssource
sourcebool taco::render::isUtf8Boundary(std::string_view text, size_t offset)
sourceuint64_t taco::render::maximumOutlineNetworkSegments(size_t namespace_bytes)The most segments a stroke outline’s network may have when its identifiers start with a namespace of
namespace_bytesbytes.
sourcedouble taco::render::maximumTransformScale(const Transform &transform)
sourcestruct taco::render::MediaCropdouble xsourcedouble ysourcedouble widthsourcedouble heightsource
sourcestruct taco::render::MediaDataMediaKind kindsourceMediaFit fitsourcebool has_cropsourcedouble widthsourcedouble heightsourcestd::string asset_idsourceMediaCrop cropsource
sourceenum class taco::render::MediaFit : uint8_tTypeScript:
RetainedImagePaintFitkContain = 0sourcekCover = 1sourcekFill = 2source
sourcetypedef StaticImageFrame taco::render::skia_adapter::MediaFrame
sourcetypedef StaticImageFrameDisposition taco::render::skia_adapter::MediaFrameDispositionThe mixed-media ABI intentionally keeps the static-image physical record.
sourceenum class taco::render::MediaKind : uint8_tkImage = 1sourcekVideo = 2source
sourcebool taco::render::nodeReferencesImageAsset(const Node &node)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.
sourcestruct taco::render::NodeStyledouble opacitysourcebool has_fillsourceColor fillsourcestd::vector<RetainedPaint> fillssourcestd::vector<RetainedStroke> strokessourceOrdered 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> shadowssourcedouble layer_blursourceWire 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_blursourcebool hasStroke() constsourcebool hasDropShadow() constsourcebool hasInnerShadow() constsourcedouble maximumStrokeWidth() constsourceThe widest authored stroke width; zero without strokes.
sourcestruct taco::render::OpenTypeFeaturestd::array<char, 4> tagsourceuint32_t valuesource
sourceclass taco::render::PaintedStrokeCacheWhat 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
Nodenever changes either (an update that changes them puts a newNodein the scene; an update of the transform alone keeps the same one), so what is kept belongs to oneNodeobject 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)sourcePaintedStrokeCache(const PaintedStrokeCache &) = delete~PaintedStrokeCache()sourceEntry &entryFor(const std::shared_ptr<const Node> &node)sourceThe 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 untilsettleis called for it or for another entry.void settle(Entry *entry)sourceCall once a hit test has built something into
entryand no longer reads it. Measures the entry, then drops the entries used longest ago until all of them fit the limit, orentryitself when it alone is larger than the limit.void dropUnless(std::string_view id, const Node *current)sourceDrops the entry for the node with this ID unless it was built from
current, the object now in the scene (nullwhen the node is gone).void clear()sourcevoid setMaximumBytes(size_t maximum_bytes)sourceChanges the limit and drops the entries used longest ago until the rest fit it.
size_t maximumBytes() constsourcesize_t size() constsourcebool holds(std::string_view id) constsourcesize_t bytes() constsourceThe size of all entries, as added up when each was last settled.
size_t measuredBytes() constsourceThe same size, found by measuring every entry again. Tests compare the two.
sourceenum class taco::render::PaintKind : uint8_tTypeScript:
RetainedPaintRecordkSolid = 1sourcekLinearGradient = 2sourcekRadialGradient = 3sourceWire version 8 adds the three kinds below.
kAngularGradient = 4sourceWire version 8 adds the three kinds below.
kImage = 5sourceWire version 8 adds the three kinds below.
sourcestruct taco::render::PathDatastd::vector<VectorVertex> verticessourcestd::vector<VectorSegment> segmentssourcestd::vector<VectorTangentConstraint> constraintssourcestd::vector<VectorRegion> regionssourcebool explicit_stroke_capssourceWire 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_capsourceStrokeCap end_capsourcestd::unique_ptr<std::pmr::monotonic_buffer_resource> identifier_arenasourceIdentifier 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_viewsmust remain valid whenPathDatais moved into a retained Node.std::unique_ptr<std::pmr::unsynchronized_pool_resource> identifier_index_poolsourceThe 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_indexsourceuint32_t identifier_readssourcebool identifier_index_finalizedsourcestd::string_view internIdentifier(std::string_view value)sourcevoid finalizeIdentifierInterning()sourcePathData()source
sourceDrawnSegmentProblem taco::render::prepareDrawnSegment( const VectorSegment &segment, double start_x, double start_y, double end_x, double end_y, DrawnSegment *drawn )Fills
drawnwithsegmentas 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_arcandsweepchoose among the four arcs that fit. All of this is worked out in single precision, by the steps Skia’sSkPathBuilder::arcTotakes, 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
mare 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
mis 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.mdsays what closing this would take.
sourcevoid taco::render::skia_path_ops::resetVectorPathOpsStats()
sourcestruct taco::render::Resourcestd::string idsourceResourceKind kindsourcestd::vector<uint8_t> bytessource
sourceenum class taco::render::ResourceKind : uint8_tTypeScript:
ResourceKindkFont = 1sourcekImage = 2sourcekBlob = 3source
sourcestruct taco::render::RetainedPaintTypeScript:
RetainedPaintRecordPaintKind kindsourceBlendMode blend_modesourceWire 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.
double opacitysourceColor colorsourcedouble anglesourceLinear gradients use
anglealone. Radial and angular gradients key their center to the shape bounds as fractions; radial adds elliptical radii as fractions, angular reusesangleas its start angle.double center_xsourcedouble center_ysourcedouble radius_xsourcedouble radius_ysourcestd::vector<GradientStop> stopssourcestd::string asset_idsourceImage paints name a logical asset attached through the media frame set.
MediaFit image_fitsourcebool isGradient() constsource
sourcestruct taco::render::RetainedShadowbool innersourceWire 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 xsourcedouble ysourcedouble blursourcedouble spreadsourceColor colorsource
sourcestruct taco::render::RetainedStrokeColor colorsourcedouble widthsourceStrokePosition positionsourcestd::vector<double> dashsourcebool has_paintsourceWire version 6 can carry a full stroke paint. When absent the compact
colorremains authoritative. Every stroke after the first carries one.RetainedPaint paintsourcebool has_sidessourceWire version 18 can give a container stroke its own weight per side, in the order top, right, bottom, left;
widththen holds the largest one.std::array<double, 4> sidessourcebool has_stylesourceWire 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 joinsourceDashCap dash_capsourcedouble miter_limitsource
sourcestruct taco::render::RichTextRunuint32_t start_bytesourceuint32_t end_bytesourceuint32_t style_indexsource
sourcestruct taco::render::RichTextStylestd::vector<std::string> font_resource_idssourcestd::string languagesourcedouble font_sizesourcedouble line_heightsourcedouble letter_spacingsourceuint16_t weightsourcebool italicsourcebool underlinesourcebool strikethroughsourceColor colorsourcestd::vector<OpenTypeFeature> open_type_featuressourcestd::vector<FontVariation> font_variationssource
sourcestruct taco::render::RoundedPathDataA path with its rounded corners worked into its segments (
roundPathVertices).idsowns the identifiers of the vertices and segments a rounded corner adds.std::deque<std::string> idssourcePathData datasource
sourcebool taco::render::roundPathVertices( const PathData &authored, RoundedPathData *out, std::string *error )Fills
outwithauthoredas 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 ofouthas a corner radius. This is the only place that decides whether a corner is rounded: the renderer paintsoutand 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, witherrorset, when a segment or region names a missing vertex or segment.
sourcestruct taco::render::SceneHitIndexbool validsourceBounds boundssourcestd::array<std::vector<uint32_t>, kCellCount> cellssourcestd::vector<uint32_t> unboundedsource
sourceenum class taco::render::SegmentDirection : uint8_tkForward = 0sourcekReverse = 1source
sourcebool 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 )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.
startandendare the segment’s own start and end vertices;at_startpicks 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 withinkDrawnPointToleranceof 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).
sourceenum class taco::render::SegmentKind : uint8_tkLine = 1sourcekCubic = 2sourcekEllipticalArc = 3source
sourcestruct taco::render::SessionApplyStatsCounters for apply/digest cost. Tests assert complexity bounds; production callers may ignore them.
digest_walksis incremented only whendigest()actually walks the retained scene.uint64_t digest_walkssourceuint64_t payload_walkssourceuint64_t geometry_nodes_computedsourceuint64_t path_bounds_index_buildssourceuint64_t dirty_nodes_visitedsourceuint64_t scene_nodes_validatedsourceuint64_t geometry_nodes_validatedsourceuint64_t hit_path_index_buildssourceuint64_t hit_segment_flatten_callssourceuint64_t hit_clip_parent_string_copiessourceuint64_t hit_rounded_path_buildssourceHow 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_buildssourceuint64_t hit_test_traversalssourceuint64_t hit_nodes_testedsourceuint64_t scene_state_node_entries_copiedsource
sourcebool taco::render::singlePrecision(double value, float *rounded)Rounds
valueto single precision, the precision the renderer builds every path in. False whenvalueis not finite or is too large for single precision to hold. Every number that reaches a drawn path is rounded here and nowhere else.
sourcestruct taco::render::skia_adapter::StaticImageFrameInternal 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_idsourceuint64_t content_revisionsourceuint32_t kindsourceStaticImageFrameDisposition dispositionsourcesk_sp<SkImage> imagesource
sourceenum class taco::render::skia_adapter::StaticImageFrameDisposition : uint8_tkReuseActivesourcekReplacesource
sourcedouble taco::render::strokeCapReach(StrokeCap cap, double width)The farthest distance from the endpoint that a cap of stroke width
widthpaints, including the stroke’s own half width around arrow arms.
sourcestruct taco::render::StrokeContourOne 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> stepssourcebool closedsource
sourceStrokeContourOrder taco::render::strokeContourOrder( const NodeStyle &style, const PathData &path, const RetainedStroke &stroke )Which contours of a path the renderer paints
strokealong.pathis the path as stored, which says whether it has stroke caps.
sourceenum class taco::render::StrokeContourOrder : uint8_tkStoredsourceSegments 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.
kJoinedsourceSegments 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
branchJoinPairchooses 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.
sourcestruct taco::render::StrokeContourStepOne segment of a stroke contour:
forwardwhen the contour runs through it from its start vertex to its end vertex.const VectorSegment *segmentsourcebool forwardsource
sourceStrokeOutlineStats &taco::render::skia_adapter::strokeOutlineStats()
sourcestruct taco::render::skia_adapter::StrokeOutlineStatsCounts 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_bodiescounts the lines of strokes with end shapes that were dashed and stroked, for painting and for outlines alike.dash_estimateis the last count of dashes made before dashing;path_ops_segmentsandpath_ops_workare 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_pathssourceuint64_t path_ops_runssourceuint64_t capped_stroke_bodiessourcedouble dash_estimatesourceuint64_t path_ops_segmentssourceuint64_t path_ops_worksource
sourceenum class taco::render::StrokePosition : uint8_tTypeScript:
RetainedStrokePositionkCenter = 0sourcekInside = 1sourcekOutside = 2source
sourcebool taco::render::strokesJoinOpenRuns(const NodeStyle &style)Whether any stroke of
stylenames 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.
source#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 65535uTypeScript:
RENDER_FONT_METADATA_MAX_AXES
source#define TACO_RENDER_FONT_METADATA_MAX_FEATURES 131070uTypeScript:
RENDER_FONT_METADATA_MAX_FEATURES
source#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_COUNTImage and video frames share one bounded decoded-equivalent media budget.
source#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 65536uText 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_EXCEEDEDwithout writes. This keeps native and 512 MiB Wasm sessions predictably bounded.
source#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 65536uGeometry applies this ceiling to each record array; hit results apply it once.
source#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
sourcestruct TacoRenderDeviceRectuint32_t xsourceuint32_t ysourceuint32_t widthsourceuint32_t heightsource
sourceenum TacoRenderExportFlagsTACO_RENDER_EXPORT_FLAG_SVG_NODE_MARKERS = 1sourceSVG 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">, whereHEXis the node identifier’s UTF-8 bytes in lowercase hexadecimal, so a host can turn them intoidattributes.
sourceenum TacoRenderExportImageSamplingTypeScript:
RenderExportImageSampling
sourcestruct TacoRenderFontAxisOne sorted variable-font axis from the exact retained SkTypeface.
TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourcechar[4] tagsourceuint8_t[7] reservedsourcedouble minimumsourcedouble default_valuesourcedouble maximumsource
sourcestruct TacoRenderFontFeatureOne sorted, unique, printable four-byte feature tag from GSUB or GPOS.
TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourcechar[4] tagsource
sourcestruct TacoRenderFontMetadataQueryTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsource
sourceenum TacoRenderGeometryFlagsTACO_RENDER_GEOMETRY_FLAG_HAS_LOCAL_BOUNDS = 1sourceTACO_RENDER_GEOMETRY_FLAG_HAS_WORLD_BOUNDS = 2sourceTACO_RENDER_GEOMETRY_FLAG_HAS_VISIBLE_WORLD_BOUNDS = 4sourceTACO_RENDER_GEOMETRY_FLAG_EFFECTIVELY_VISIBLE = 8source
sourceenum TacoRenderIncrementalFrameFlagsTACO_RENDER_INCREMENTAL_FRAME_HAS_DAMAGE = 1sourceTACO_RENDER_INCREMENTAL_FRAME_FULL_PAINT = 2sourceTACO_RENDER_INCREMENTAL_FRAME_REUSED_TARGET = 4source
sourceenum TacoRenderIncrementalRenderFlagsTACO_RENDER_INCREMENTAL_RENDER_FORCE_FULL = 1source
sourcetypedef TacoRenderStaticImageFrame TacoRenderMediaFrameVideo-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.
sourcetypedef TacoRenderStaticImageFrameDisposition TacoRenderMediaFrameDisposition
sourceenum TacoRenderMediaFrameKindTACO_RENDER_MEDIA_FRAME_STATIC_IMAGE = 1sourceTACO_RENDER_MEDIA_FRAME_VIDEO = 2source
sourcetypedef TacoRenderStaticImageFrameSet TacoRenderMediaFrameSet
sourceenum TacoRenderNodeKindThe kind of a retained node, as reported by node and geometry queries.
TACO_RENDER_NODE_PATH = 1sourceTACO_RENDER_NODE_TEXT = 2sourceTACO_RENDER_NODE_CONTAINER = 3sourceTACO_RENDER_NODE_MEDIA = 4source
sourcestruct TacoRenderRectAn axis-aligned rectangle: top-left corner plus size.
double xsourcedouble ysourcedouble widthsourcedouble heightsource
sourceenum TacoRenderStaticImageFrameDispositionTACO_RENDER_STATIC_IMAGE_FRAME_REUSE_ACTIVE = 1sourceTACO_RENDER_STATIC_IMAGE_FRAME_RGBA8888 = 2source
sourceenum TacoRenderTextAffinityTypeScript:
RenderTextAffinityTACO_RENDER_TEXT_AFFINITY_UPSTREAM = 0sourceTACO_RENDER_TEXT_AFFINITY_DOWNSTREAM = 1source
sourceenum TacoRenderTextAutoResizeTACO_RENDER_TEXT_AUTO_RESIZE_NONE = 0sourceTACO_RENDER_TEXT_AUTO_RESIZE_HEIGHT = 1sourceTACO_RENDER_TEXT_AUTO_RESIZE_WIDTH_AND_HEIGHT = 2source
sourcestruct TacoRenderTextCaretTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceTacoRenderTextPosition positionsourceTacoRenderRect local_boundssource
sourceenum TacoRenderTextEditingOverlayFlagsTACO_RENDER_TEXT_EDITING_OVERLAY_HAS_COMPOSITION = 1sourceTACO_RENDER_TEXT_EDITING_OVERLAY_CARET_VISIBLE = 2source
sourcestruct TacoRenderTextHitQueryTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsourcedouble local_xsourcedouble local_ysource
sourcestruct TacoRenderTextHitResultTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t insidesourceuint64_t generationsourceTacoRenderTextPosition positionsource
sourcestruct TacoRenderTextLayoutMetricsTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t auto_resizesourceuint64_t generationsourcedouble frame_widthsourcedouble frame_heightsourcedouble content_widthsourcedouble content_heightsourcedouble suggested_widthsourcedouble suggested_heightsourceuint32_t paragraph_countsourceuint32_t line_countsourceuint32_t caret_countsourceuint32_t reservedsource
TypeScript:
RenderTextNavigationOperationTypeScript:
PORTABLE_WASM_ABI_LAYOUTTypeScript:
PORTABLE_WASM_ABI_LAYOUT
sourcestruct TacoRenderTextQueryThe common text query input: the scene generation the caller expects. reserved must be 0.
TypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsource
sourcestruct TacoRenderTextRangeQueryTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsourceTacoRenderTextPosition anchorsourceTacoRenderTextPosition focussource
sourcestruct TacoRenderTextRectTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceTacoRenderRect local_boundssource
sourceenum TacoRenderVectorBooleanOperationTypeScript:
RenderVectorBooleanOperationTACO_RENDER_VECTOR_BOOLEAN_UNION = 1sourceTACO_RENDER_VECTOR_BOOLEAN_INTERSECTION = 2sourceTACO_RENDER_VECTOR_BOOLEAN_DIFFERENCE = 3sourceTACO_RENDER_VECTOR_BOOLEAN_EXCLUSION = 4source
sourceenum TacoRenderVectorComponentKindTACO_RENDER_VECTOR_COMPONENT_VERTEX = 1sourceTACO_RENDER_VECTOR_COMPONENT_CUBIC_HANDLE = 2sourceTACO_RENDER_VECTOR_COMPONENT_SEGMENT = 3sourceTACO_RENDER_VECTOR_COMPONENT_REGION = 4source
sourcestruct TacoRenderVectorCubicHandleTypeScript:
decodeWasmVectorGeometry,PORTABLE_WASM_ABI_LAYOUTTypeScript, through
decodeWasmVectorGeometry:RenderCoreSession.getVectorGeometryuint32_t struct_sizesourceuint32_t endpointsourceuint32_t id_offsetsourceuint32_t id_sizesourceuint32_t segment_id_offsetsourceuint32_t segment_id_sizesourceuint32_t vertex_id_offsetsourceuint32_t vertex_id_sizesourcedouble local_xsourcedouble local_ysourcedouble screen_xsourcedouble screen_ysource
sourcestruct TacoRenderVectorEditingComponentOne 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_LAYOUTuint32_t struct_sizesourceuint32_t kindsourceuint32_t id_offsetsourceuint32_t id_sizesourceuint32_t flagssourceuint32_t[3] reservedsource
sourceenum TacoRenderVectorEditingComponentFlagsTACO_RENDER_VECTOR_EDITING_COMPONENT_PRIMARY = 1source
sourcestruct TacoRenderVectorEditingOverlayQueryTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsource
sourceenum TacoRenderVectorEditingPendingHandleFlagsA 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 = 1sourceTACO_RENDER_VECTOR_EDITING_PENDING_HANDLE_MIRRORED = 2source
sourceenum TacoRenderVectorEndpointTACO_RENDER_VECTOR_ENDPOINT_NONE = 0sourceTACO_RENDER_VECTOR_ENDPOINT_START = 1sourceTACO_RENDER_VECTOR_ENDPOINT_END = 2source
sourcestruct TacoRenderVectorHitTypeScript:
decodeWasmVectorHits,PORTABLE_WASM_ABI_LAYOUTTypeScript, through
decodeWasmVectorHits:RenderCoreSession.hitTestVectoruint32_t struct_sizesourceuint32_t kindsourceuint32_t id_offsetsourceuint32_t id_sizesourceuint32_t prioritysourceuint32_t endpointsourceuint32_t reservedsourcedouble distance_css_pxsource
sourcestruct TacoRenderVectorHitBufferResultTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t hit_countsourceuint32_t identifier_byte_countsourceuint32_t reservedsourceuint64_t generationsource
sourcestruct TacoRenderVectorHitQueryTypeScript:
PORTABLE_WASM_ABI_LAYOUTuint32_t struct_sizesourceuint32_t reservedsourceuint64_t expected_generationsourcedouble screen_xsourcedouble screen_ysourcedouble tolerance_css_pxsourcedouble[6] document_to_screensource
sourcestruct TacoRenderVectorRegionTypeScript:
decodeWasmVectorGeometry,PORTABLE_WASM_ABI_LAYOUTTypeScript, through
decodeWasmVectorGeometry:RenderCoreSession.getVectorGeometryuint32_t struct_sizesourceuint32_t winding_rulesourceuint32_t id_offsetsourceuint32_t id_sizesourceuint32_t[3] reservedsourceTacoRenderRect local_boundssourceTacoRenderRect screen_boundssource
sourcestruct TacoRenderVectorSegmentTypeScript:
decodeWasmVectorGeometry,PORTABLE_WASM_ABI_LAYOUTTypeScript, through
decodeWasmVectorGeometry:RenderCoreSession.getVectorGeometryuint32_t struct_sizesourceuint32_t kindsourceuint32_t id_offsetsourceuint32_t id_sizesourceuint32_t start_vertex_id_offsetsourceuint32_t start_vertex_id_sizesourceuint32_t end_vertex_id_offsetsourceuint32_t end_vertex_id_sizesourceuint32_t start_handle_id_offsetsourceuint32_t start_handle_id_sizesourceuint32_t end_handle_id_offsetsourceuint32_t end_handle_id_sizesourceuint32_t flagssourceTacoRenderRect local_boundssourceTacoRenderRect screen_boundssourcedouble radius_xsourcedouble radius_ysourcedouble x_axis_rotationsource
sourceenum TacoRenderVectorSegmentFlagsTACO_RENDER_VECTOR_SEGMENT_LARGE_ARC = 1sourceTACO_RENDER_VECTOR_SEGMENT_SWEEP = 2source
sourceenum TacoRenderVectorSegmentKindTACO_RENDER_VECTOR_SEGMENT_LINE = 1sourceTACO_RENDER_VECTOR_SEGMENT_CUBIC = 2sourceTACO_RENDER_VECTOR_SEGMENT_ELLIPTICAL_ARC = 3source
sourcestruct TacoRenderVectorVertexTypeScript:
decodeWasmVectorGeometry,PORTABLE_WASM_ABI_LAYOUTTypeScript, through
decodeWasmVectorGeometry:RenderCoreSession.getVectorGeometryuint32_t struct_sizesourceuint32_t id_offsetsourceuint32_t id_sizesourceuint32_t reservedsourcedouble local_xsourcedouble local_ysourcedouble screen_xsourcedouble screen_ysource
sourceenum TacoRenderVectorWindingRuleTACO_RENDER_VECTOR_WINDING_NONZERO = 0sourceTACO_RENDER_VECTOR_WINDING_EVENODD = 1source
sourceenum TacoRenderViewportModeHow
taco_render_session_map_viewportscales the scene into a viewport.TACO_RENDER_VIEWPORT_FIT = 0sourceUniform scale; the whole scene fits inside the viewport.
TACO_RENDER_VIEWPORT_FILL = 1sourceUniform scale; the scene covers the whole viewport.
TACO_RENDER_VIEWPORT_STRETCH = 2sourceSeparate x and y scales; the scene matches the viewport exactly.
sourceenum class taco::render::TangentConstraintMode : uint8_tkAngle = 0sourcekAngleAndLength = 1source
sourceenum class taco::render::TextAlign : uint8_tkLeft = 0sourcekCenter = 1sourcekRight = 2sourcekJustify = 3source
sourceenum class taco::render::TextAutoResize : uint8_tkNone = 0sourcekHeight = 1sourcekWidthAndHeight = 2source
sourcestruct taco::render::TextDatadouble widthsourcedouble heightsourcedouble line_heightsourceTextWrap wrapsourceTextAlign alignsourcestd::string textsourcestd::vector<TextRun> runssourceTextVerticalAlign vertical_alignsourceTextOverflow overflowsourceTextAutoResize auto_resizesourcestd::vector<RichTextStyle> stylessourcestd::vector<TextParagraph> paragraphssource
sourceenum class taco::render::TextDirection : uint8_tkAuto = 0sourcekLtr = 1sourcekRtl = 2source
sourceenum class taco::render::TextListKind : uint8_tkNone = 0sourcekOrdered = 1sourcekUnordered = 2source
sourcestruct taco::render::TextListMarkerbool has_textsourcestd::string textsourcebool has_stylesourceuint32_t style_indexsourcebool has_gapsourcedouble gapsource
sourceenum class taco::render::TextOverflow : uint8_tkClip = 0sourcekVisible = 1source
sourcestruct taco::render::TextParagraphTextDirection directionsourceTextAlign alignsourcedouble spacing_beforesourcedouble spacing_aftersourcebool has_indentsourceTextParagraphIndent indentsourceTextParagraphList listsourcestd::string textsourcestd::vector<RichTextRun> runssource
sourcestruct taco::render::TextParagraphIndentdouble leftsourcedouble rightsourcedouble first_linesource
sourcestruct taco::render::TextParagraphListTextListKind kindsourceuint32_t levelsourcebool has_startsourceuint32_t startsourcebool has_markersourceTextListMarker markersource
sourcestruct taco::render::TextRunuint32_t start_bytesourceuint32_t end_bytesourcestd::string font_resource_idsourcedouble font_sizesourcedouble letter_spacingsourceuint16_t weightsourcebool italicsourcebool underlinesourcebool strikethroughsourceColor colorsourcestd::vector<OpenTypeFeature> open_type_featuressourcestd::vector<FontVariation> font_variationssource
sourceenum class taco::render::TextVerticalAlign : uint8_tkTop = 0sourcekMiddle = 1sourcekBottom = 2source
sourceenum class taco::render::TextWrap : uint8_tkNone = 0sourcekWord = 1sourcekCharacter = 2source
sourceTacoRenderRect taco::render::toRect(const Bounds &bounds)C ABI:
taco_render_session_hit_test,taco_render_session_get_last_dirty_bounds
sourcestruct taco::render::TransformAn affine matrix stored as
values = [a, b, c, d, e, f], the same layout as the TypeScriptTransform. A point maps as .std::array<double, 6> valuessource
sourceBounds taco::render::unionBounds(const Bounds &first, const Bounds &second)
sourcestruct taco::render::VectorGeometryQueryResultstd::vector<TacoRenderVectorVertex> verticessourcestd::vector<TacoRenderVectorCubicHandle> handlessourcestd::vector<TacoRenderVectorSegment> segmentssourcestd::vector<TacoRenderVectorRegion> regionssourcestd::string identifierssource
sourcestruct taco::render::VectorHandlestd::string_view idsourcedouble dxsourcedouble dysource
sourcestruct taco::render::VectorHitQueryResultstd::vector<TacoRenderVectorHit> hitssourcestd::string identifierssource
sourceVectorPathOpsStats &taco::render::skia_path_ops::vectorPathOpsStats()
sourcestruct taco::render::skia_path_ops::VectorPathOpsStatsint id_concat_temporariessourceuint64_t contour_pair_checkssourcePairs of contours
vectorNetworkFromPathcompared to group contours into regions.uint64_t projected_segmentssourceSegments
vectorNetworkFromPathlast 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
sourcestruct taco::render::VectorRegionstd::string_view idsourceWindingRule winding_rulesourcebool has_explicit_fillssourcestd::vector<RetainedPaint> fillssourcestd::vector<VectorRegionLoop> loopssource
sourcestruct taco::render::VectorRegionLoopbool implicit_closesourcestd::vector<VectorSegmentReference> segmentssource
sourcestruct taco::render::VectorSegmentstd::string_view idsourcestd::string_view start_vertex_idsourcestd::string_view end_vertex_idsourceSegmentKind kindsourceVectorHandle start_handlesourceVectorHandle end_handlesourcedouble radius_xsourcedouble radius_ysourcedouble x_axis_rotationsourcebool large_arcsourcebool sweepsource
sourcestruct taco::render::VectorSegmentReferencestd::string_view segment_idsourceSegmentDirection directionsource
sourcestruct taco::render::VectorTangentConstraintstd::string_view vertex_idsourcestd::array<std::string_view, 2> handle_idssourceTangentConstraintMode modesource
sourcestruct taco::render::VectorVertexstd::string_view idsourcedouble xsourcedouble ysourcedouble corner_radiussourceWire 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.
sourceenum class taco::render::WindingRule : uint8_tkNonzero = 0sourcekEvenodd = 1source