180 lines
4.9 KiB
Markdown
180 lines
4.9 KiB
Markdown
---
|
|
title: Scripting
|
|
description: Execute JavaScript with a Figma-compatible Plugin API to query, batch-modify, and generate designs.
|
|
---
|
|
|
|
# Scripting
|
|
|
|
`openpencil eval` runs JavaScript against an OpenPencil document with a Figma-compatible `figma` global. Use it for headless batch edits, inspection, fixture setup, and automation without opening the editor UI.
|
|
|
|
## Basic usage
|
|
|
|
```sh
|
|
openpencil eval design.fig -c "return figma.currentPage.children.length"
|
|
```
|
|
|
|
The `-c` flag accepts JavaScript. If the code does not start with `return`, OpenPencil wraps it in an async function and returns the value from that function when present.
|
|
|
|
```sh
|
|
openpencil eval design.fig -c "
|
|
const frame = figma.createFrame()
|
|
frame.name = 'Card'
|
|
frame.resize(300, 200)
|
|
frame.layoutMode = 'VERTICAL'
|
|
frame.itemSpacing = 12
|
|
return { id: frame.id, name: frame.name }
|
|
"
|
|
```
|
|
|
|
## Query nodes
|
|
|
|
```sh
|
|
openpencil eval design.fig -c "
|
|
return figma.currentPage
|
|
.findAll((node) => node.type === 'FRAME' && node.name.includes('Button'))
|
|
.map((button) => ({
|
|
id: button.id,
|
|
name: button.name,
|
|
width: button.width,
|
|
height: button.height
|
|
}))
|
|
"
|
|
```
|
|
|
|
## Modify and save
|
|
|
|
Use `--write` / `-w` to write changes back to the input file:
|
|
|
|
```sh
|
|
openpencil eval design.fig -c "
|
|
figma.currentPage.children.forEach((node) => {
|
|
node.opacity = 0.5
|
|
})
|
|
" --write
|
|
```
|
|
|
|
Use `--output` / `-o` to write to a new file:
|
|
|
|
```sh
|
|
openpencil eval design.fig -c "figma.currentPage.name = 'Updated'" -o updated.fig
|
|
```
|
|
|
|
## Read scripts from stdin
|
|
|
|
```sh
|
|
cat transform.js | openpencil eval design.fig --stdin --write
|
|
```
|
|
|
|
## Live app mode
|
|
|
|
Omit the file path to run against the currently open document in the desktop app:
|
|
|
|
```sh
|
|
openpencil eval -c "return figma.currentPage.name"
|
|
```
|
|
|
|
The desktop app must be running with a document open.
|
|
|
|
## Output
|
|
|
|
By default, non-TTY output is JSON. Use `--json` to force JSON output:
|
|
|
|
```sh
|
|
openpencil eval design.fig -c "return figma.currentPage.children.map((n) => n.name)" --json
|
|
```
|
|
|
|
Use `--quiet` / `-q` to suppress output when only writing a file.
|
|
|
|
## Supported API surface
|
|
|
|
The API is intentionally close to Figma's Plugin API, but it maps to OpenPencil's scene graph and file format.
|
|
|
|
### Document and pages
|
|
|
|
- `figma.root`
|
|
- `figma.currentPage`
|
|
- `figma.currentPage.selection`
|
|
- `figma.getNodeById(id)`
|
|
- `figma.createPage()`
|
|
|
|
### Node creation
|
|
|
|
- `figma.createFrame()`
|
|
- `figma.createRectangle()`
|
|
- `figma.createEllipse()`
|
|
- `figma.createText()`
|
|
- `figma.createLine()`
|
|
- `figma.createPolygon()`
|
|
- `figma.createStar()`
|
|
- `figma.createVector()`
|
|
- `figma.createComponent()`
|
|
- `figma.createSection()`
|
|
|
|
### Tree operations
|
|
|
|
- `node.children`
|
|
- `node.parent`
|
|
- `node.appendChild(child)`
|
|
- `node.insertChild(index, child)`
|
|
- `node.clone()`
|
|
- `node.remove()`
|
|
- `node.findAll(callback?)`
|
|
- `node.findOne(callback)`
|
|
- `node.findChild(callback)`
|
|
- `node.findChildren(callback?)`
|
|
- `figma.group(nodes, parent)`
|
|
- `figma.ungroup(node)`
|
|
|
|
### Components
|
|
|
|
- `figma.createComponentFromNode(node)`
|
|
- `component.createInstance()`
|
|
- `instance.mainComponent`
|
|
|
|
### Variables
|
|
|
|
- `figma.getLocalVariables(type?)`
|
|
- `figma.getVariableById(id)`
|
|
- `figma.getLocalVariableCollections()`
|
|
- `figma.getVariableCollectionById(id)`
|
|
- `figma.createVariable(name, type, collectionId, value?)`
|
|
- `figma.setVariableValue(variableId, modeId, value)`
|
|
- `figma.deleteVariable(id)`
|
|
- `figma.createVariableCollection(name)`
|
|
- `figma.deleteVariableCollection(id)`
|
|
- `figma.bindVariable(nodeId, field, variableId)`
|
|
- `figma.unbindVariable(nodeId, field)`
|
|
|
|
### Properties
|
|
|
|
Common node properties are readable/writable through the proxy, including:
|
|
|
|
- Geometry: `x`, `y`, `width`, `height`, `rotation`, `resize(width, height)`
|
|
- Appearance: `fills`, `strokes`, `effects`, `opacity`, `visible`, `locked`, `blendMode`, `clipsContent`
|
|
- Radius: `cornerRadius`, `topLeftRadius`, `topRightRadius`, `bottomRightRadius`, `bottomLeftRadius`
|
|
- Text: `characters`, `fontSize`, `fontName`, `fontWeight`, alignment, line height, letter spacing, style-run helpers
|
|
- Auto-layout: `layoutMode`, `primaryAxisAlignItems`, `counterAxisAlignItems`, `itemSpacing`, padding, sizing, and layout positioning fields
|
|
- Stroke helpers: `strokeWeight`, `strokeAlign`, `dashPattern`
|
|
|
|
### Utilities
|
|
|
|
- `figma.mixed`
|
|
- `figma.createImage(data)`
|
|
- `figma.loadFontAsync(fontName)` no-ops because OpenPencil does not gate text edits on plugin font loading
|
|
- `figma.listAvailableFontsAsync()` returns host-provided fonts when available
|
|
- `figma.notify(message)` logs a warning in headless mode
|
|
- `figma.viewport`
|
|
|
|
## Not yet Figma-compatible
|
|
|
|
These Figma APIs are not exposed as compatible helpers yet:
|
|
|
|
- `node.exportAsync()`
|
|
- `node.setBoundVariable(field, variable)`
|
|
- `node.detachInstance()`
|
|
- `figma.combineAsVariants(components, parent)`
|
|
- Figma style APIs such as `figma.createPaintStyle()` / `figma.createTextStyle()`
|
|
- Full vector boolean operation parity
|
|
|
|
Use OpenPencil CLI export commands, core tools, or direct scene-graph helpers where available.
|