* feat(core): add visual diff and patch apply tools diff_visual renders two nodes at one scale through the existing raster export, compares them with pixelmatch, and returns the diff PNG with the changed ratio and region in source-node coordinates. It takes export_image's scale and maxEdge inputs. FigmaAPI gains a CanvasKit-backed raster codec and a pageId export option, so the app and headless CLI decode pixels and render nodes off the current page. diff_apply applies diff_create and diff_show patches through the Figma API, validates every node before changing any, and supports dryRun and force. diff_show now simulates changes on a detached copy with the same property code. One serializer and parser back all three. diffDocuments compares two documents page by page by name path. Image tool results now reach models as media with their metadata as text, for any tool rather than export_image alone. diff_create, diff_jsx, and diff_visual join the default AI tool set, and the diff tools are no longer hidden from WebMCP. * feat(cli): add diff commands and agent diff guidance openpencil diff create, jsx, show, apply, and visual run the Core diff tools on a file or the running app; apply writes back with --write or --output like eval. diff files compares two documents page by page and exits 1 when they differ. The chat prompt asks the agent to edit in place and to verify risky edits against a reference copy with diff_jsx, diff_create, and diff_visual. The skill, CLI reference, MCP tool table, and a new Comparing Designs page document the commands and tools. * feat(core): diff and patch node trees as JSX attributes diff_create, diff_show, diff_apply, and diffDocuments used a hand-rolled `key: value` property format that covered about fifteen properties, matched children by name path, and could not see moves. Nodes are now projected to the attributes the JSX export prints, and jsondiffpatch matches children (by ID or by name path) and detects moves. Patches list `-`/`+` attribute lines per node plus moved, added, and removed children. diff_apply checks every hunk first, applies attribute changes through the renderer's prop handling, and changes only the fields an attribute moves, so IDs, instance links, and other state survive. diff_show takes JSX attributes instead of a JSON props object. design-jsx gains sceneNodeAttributes, parseJSXAttributes, and jsxNodeFields for this, and the export round-trip property table is shared so every case is also diffed and applied. `diff files` loads its documents in order so node IDs, and so its patches, are deterministic. * fix(core): keep diff_apply atomic and diff files honest about differences - Added nodes render before anything else changes; if one fails, for example on a missing component, the rendered ones are deleted and nothing else is committed. - A hunk with an attribute the renderer ignores fails instead of reporting "unchanged". - diffDocuments reports `changed` from page statuses, and a page only one document has gets its status but no patch, since patches do not add or remove pages. diff files uses it, so an added empty page no longer reads as a match. - diff files rejects a --page neither document has and a --depth that is not a non-negative integer, exiting 2; diff_create's depth is validated the same way.
8.6 KiB
| title | description |
|---|---|
| CLI Reference | Complete reference for all openpencil commands, options, and flags. |
CLI Reference
All commands accept a .fig file as a positional argument. When omitted, the CLI connects to the running desktop app via RPC.
info
Show document info — pages, node counts, fonts, file size.
openpencil info [file] [--json]
| Option | Description |
|---|---|
--json |
Output as JSON |
tree
Print the node hierarchy.
openpencil tree [file] [options]
| Option | Description |
|---|---|
--page |
Page name (default: first page) |
--depth |
Max depth (default: unlimited) |
--json |
Output as JSON |
find
Search nodes by name or type.
openpencil find [file] [options]
| Option | Description |
|---|---|
--name |
Node name (partial match, case-insensitive) |
--type |
Node type: FRAME, TEXT, RECTANGLE, INSTANCE, etc. |
--page |
Page name (default: all pages) |
--limit |
Max results (default: 100) |
--json |
Output as JSON |
node
Show detailed properties of a node.
openpencil node [file] --id <id> [--json]
| Option | Description |
|---|---|
--id |
Required. Node ID (e.g. 1:23) |
--json |
Output as JSON |
pages
List all pages in the document.
openpencil pages [file] [--json]
| Option | Description |
|---|---|
--json |
Output as JSON |
variables
List design variables and collections.
openpencil variables [file] [options]
| Option | Description |
|---|---|
--collection |
Filter by collection name |
--type |
Filter by type: COLOR, FLOAT, STRING, BOOLEAN |
--json |
Output as JSON |
export
Export to PNG, JPG, WEBP, SVG, JSX, HTML, .fig, or Storybook stories.
openpencil export [file] [options]
| Option | Alias | Description |
|---|---|---|
--format |
-f |
png (default), jpg, webp, svg, pdf, pptx, jsx, tailwind-jsx, html, fig, storybook |
--output |
-o |
Output file path (default: <name>.<format>); a directory for storybook (default: <name>-stories) |
--scale |
-s |
Export scale (default: 1) |
--quality |
-q |
Quality 0–100, JPG/WEBP only (default: 90) |
--page |
Page name (default: first page; fig, pptx, and storybook default to every page) |
|
--node |
Node ID to export (default: all top-level nodes) | |
--style |
JSX style: openpencil (default), tailwind (same as -f tailwind-jsx) |
|
--html |
HTML mode: fragment (default), standalone |
|
--css |
HTML CSS output: inline (default), tailwind |
|
--assets |
Standalone HTML assets: inline (default), external |
|
--fonts |
Standalone HTML font output: assets, none (default) |
|
--framework |
Storybook framework: react (default), vue, html |
|
--design-images |
Storybook: render a PNG per variant for the Design panel (default: on; --no-design-images to skip) |
|
--watch |
Storybook: re-export whenever the document is saved | |
--beside |
Storybook: write each document's stories into the document's own folder; the file argument can then be several files or a quoted glob | |
--thumbnail |
Export page thumbnail instead of full render | |
--width |
Thumbnail width (default: 1920) | |
--height |
Thumbnail height (default: 1080) |
import
Import HTML/CSS/Tailwind into an editable OpenPencil document.
openpencil import page.html [options]
| Option | Alias | Description |
|---|---|---|
--format |
-f |
Output format: fig (default), json |
--output |
-o |
Output file path (default: <name>.<format>) |
--css |
CSS file to apply before conversion | |
--css-text |
Inline CSS text to apply before conversion | |
--tailwind |
Tailwind utility candidates to compile and apply | |
--tailwind-file |
File containing Tailwind utility candidates | |
--page-name |
Scene graph page name (default: DOM/CSS) |
|
--json |
Print a machine-readable summary |
Examples:
openpencil import card.html --css card.css -o card.fig
openpencil import card.html --tailwind "flex flex-col gap-3 w-80 p-6 rounded-xl bg-white" -o card.fig
eval
Execute JavaScript with the Figma Plugin API.
openpencil eval [file] [options]
| Option | Alias | Description |
|---|---|---|
--code |
-c |
JavaScript code to execute |
--stdin |
Read code from stdin | |
--write |
-w |
Write changes back to the input file |
--output |
-o |
Write to a different file |
--json |
Output as JSON | |
--quiet |
-q |
Suppress output |
analyze colors
Analyze color palette usage across the document.
openpencil analyze colors [file] [options]
| Option | Description |
|---|---|
--limit |
Max colors to show (default: 30) |
--threshold |
Distance threshold for clustering similar colors, 0–50 (default: 15) |
--similar |
Show similar color clusters |
--json |
Output as JSON |
analyze typography
Analyze font family, size, and weight distribution.
openpencil analyze typography [file] [options]
| Option | Description |
|---|---|
--group-by |
Group by: family, size, weight (default: show all styles) |
--limit |
Max styles to show (default: 30) |
--json |
Output as JSON |
analyze spacing
Analyze gap and padding values across auto-layout frames.
openpencil analyze spacing [file] [options]
| Option | Description |
|---|---|
--grid |
Base grid size to check against (default: 8) |
--json |
Output as JSON |
analyze clusters
Find repeated node patterns — potential components.
openpencil analyze clusters [file] [options]
| Option | Description |
|---|---|
--limit |
Max clusters to show (default: 20) |
--min-size |
Min node size in px (default: 30) |
--min-count |
Min instances to form a cluster (default: 2) |
--json |
Output as JSON |
diff create
Patch that turns one node tree into another, as JSX attribute changes plus moved, added, and removed children. Children match by name; see Comparing designs for the format.
openpencil diff create [file] --from <id> --to <id> [options]
| Option | Description |
|---|---|
--from |
Source node ID |
--to |
Target node ID |
--depth |
Max tree depth (default: 10) |
--json |
Output as JSON |
diff jsx
Structural diff between two nodes as design JSX.
openpencil diff jsx [file] --from <id> --to <id> [--json]
diff show
Preview the patch that setting JSX attributes on a node would produce, without changing it.
openpencil diff show <id> [file] --attributes '<jsx attributes>' [--json]
--attributes takes attributes as the JSX export writes them, such as 'w={200} bg="#FF0000"'.
diff apply
Apply a patch from diff create, diff show, or diff files. Every node must still match the patch's old values unless --force is set, and nothing changes unless every hunk applies.
openpencil diff apply <patch> [file] [options]
| Option | Alias | Description |
|---|---|---|
--dry-run |
Validate and list changes without applying | |
--force |
Apply even when current values differ from the patch | |
--write |
-w |
Write changes back to the input file |
--output |
-o |
Write to a different file |
--json |
Output as JSON |
Pass - as the patch path to read it from stdin.
diff visual
Pixel diff between two rendered nodes, written as a PNG with changed pixels in red.
openpencil diff visual [file] --from <id> --to <id> --output <png> [options]
| Option | Alias | Description |
|---|---|---|
--output |
-o |
Diff PNG path |
--scale |
Render scale before the max-edge limit (default: 1) | |
--max-edge |
Maximum image width or height (default: 1280) | |
--threshold |
Color tolerance 0–1; smaller is stricter (default: 0.1) | |
--json |
Output as JSON |
diff files
Structural diff of two documents, page by page. Pages match by name and nodes by name path, so two versions of a file compare even though their node IDs differ. Exits with status 1 when the documents differ and 2 when the options are invalid, such as a --page neither document has.
openpencil diff files <before> <after> [options]
| Option | Description |
|---|---|
--page |
Compare only the page with this name |
--depth |
Max tree depth below each page (default: unlimited) |
--json |
Output as JSON |