Find a file
Danila Poyarkov bb69735d27
feat(lint): suggest group-to-frame and hidden-layer fixes; keep canvas edits in code previews (#870)
* feat(lint): suggest converting groups to frames and deleting hidden layers

no-groups and no-hidden-layers now carry suggestions the Lint panel, the
lint_fix tool and editor.applyLintFixes can apply. A group becomes a frame
in place, keeping its id, children, bounds and look; a hidden layer is
deleted with its children. Neither is offered for locked layers or inside
components and instances, and both are checked again when applied.

The editor gains convertGroupToFrame and deleteNodes, which deleteSelected
now uses, and applies lint fixes through a bridge so structure changes and
property updates share one undo step. lint_fix becomes a document mutation
because atomic tools cannot remove layers.

* fix(code): keep canvas edits made while replaced code waits to render

Replacing all the code drops its layer links until the preview links it
again, so a canvas edit in the preview delay could not be patched into the
code and the preview drew over it. Edits made while code waits to render
are now applied again once the preview has linked the code, in the same
undo step, and reach the code like any other canvas change.

* fix(figma-api): default paint opacity and visibility like Figma

Plugin scripts may leave out a paint's opacity and visible; Figma reads
them back as 1 and true. The fills and strokes setters stored the paint
as given, so the Design panel received an undefined opacity.

* build(dev): forward only errors from the browser console

Vite forwards browser logs when an agent starts the dev server. Serializing
a Vue warning's component props walks the editor state and freezes the
tab, so warnings stay in the browser console.

* fix(code): follow values while they are dragged or scrubbed

Live previews change layers without a new scene version, so the code kept
the old value until the gesture ended. The Code tab now follows preview
updates once per frame, as the Design panel does, and goes back when the
gesture is cancelled.

* fix(code): keep canvas edits when the replaced code fails to preview

A failed preview took the canvas edits waiting for it and dropped them,
and stopped recording new ones, so correcting the code drew over them.
The edits now wait for the next preview.
2026-10-04 12:46:06 +00:00
.claude ci: keep AI disclosure out of co-author credits 2026-09-15 23:12:44 +03:00
.devcontainer refactor: rename @open-pencil/codegen to @open-pencil/emit (#822) 2026-10-04 11:22:20 +00:00
.github ci: run CI and the PR title check for the merge queue (#867) 2026-10-04 13:51:51 +04:00
.storybook fix(vue): start CanvasKit once CanvasSurface provides the canvas (#823) 2026-10-02 00:23:49 +04:00
.vscode Initial commit: Tauri + CanvasKit + React editor scaffold 2026-02-27 15:48:29 +03:00
assets/brand build(tools): group tools by role and gate them like the rest of the repo (#791) 2026-09-30 05:00:31 +04:00
desktop feat: check designs live with a Lint panel, canvas markers, and fixes (#804) 2026-10-04 10:08:39 +00:00
lint build(tools): group tools by role and gate them like the rest of the repo (#791) 2026-09-30 05:00:31 +04:00
packages feat(lint): suggest group-to-frame and hidden-layer fixes; keep canvas edits in code previews (#870) 2026-10-04 12:46:06 +00:00
public feat: refresh branding with generated platform icons (#707) 2026-09-16 11:54:07 +03:00
skills/open-pencil feat: check designs live with a Lint panel, canvas markers, and fixes (#804) 2026-10-04 10:08:39 +00:00
src feat(lint): suggest group-to-frame and hidden-layer fixes; keep canvas edits in code previews (#870) 2026-10-04 12:46:06 +00:00
tests feat(lint): suggest group-to-frame and hidden-layer fixes; keep canvas edits in code previews (#870) 2026-10-04 12:46:06 +00:00
tools refactor: rename @open-pencil/codegen to @open-pencil/emit (#822) 2026-10-04 11:22:20 +00:00
vite feat(lint): suggest group-to-frame and hidden-layer fixes; keep canvas edits in code previews (#870) 2026-10-04 12:46:06 +00:00
.coderabbit.yaml chore: hide review status chatter and test generation prompts 2026-09-15 21:43:04 +03:00
.gitattributes Fix CJK text garbled when font unavailable (#89) 2026-03-10 16:58:57 +03:00
.gitignore build(tools): group tools by role and gate them like the rest of the repo (#791) 2026-09-30 05:00:31 +04:00
.gitleaks.toml chore(tools): add secret scanning gate 2026-07-01 15:24:06 +03:00
.lfsconfig ci: move Git LFS to provider-neutral gateway 2026-08-01 17:52:08 +03:00
.oxfmtrc.json style: tighten import grouping 2026-05-06 02:22:08 +03:00
AGENTS.md refactor: rename @open-pencil/codegen to @open-pencil/emit (#822) 2026-10-04 11:22:20 +00:00
bun.lock refactor: rename @open-pencil/codegen to @open-pencil/emit (#822) 2026-10-04 11:22:20 +00:00
bunfig.toml refactor(mcp): align transport domain structure 2026-07-25 22:59:59 +03:00
CHANGELOG.md feat(lint): suggest group-to-frame and hidden-layer fixes; keep canvas edits in code previews (#870) 2026-10-04 12:46:06 +00:00
commitlint.config.ts build(tools): group tools by role and gate them like the rest of the repo (#791) 2026-09-30 05:00:31 +04:00
CONTRIBUTING.md docs: describe rebasing and merging stacked pull requests (#869) 2026-10-04 10:17:30 +00:00
index.html fix: explain unsupported browsers instead of a blank window (#745) 2026-09-22 14:40:59 +04:00
knip.json fix(text): finalize font readiness and label shaping (#593) 2026-08-30 14:33:32 +03:00
LICENSE docs: acknowledge OpenPencil contributors in license 2026-08-31 09:11:10 +03:00
oxlint.json build(tools): group tools by role and gate them like the rest of the repo (#791) 2026-09-30 05:00:31 +04:00
package.json refactor: rename @open-pencil/codegen to @open-pencil/emit (#822) 2026-10-04 11:22:20 +00:00
playwright.config.ts chore: merge master into live-editor-regressions 2026-09-16 00:14:31 +03:00
portless.json chore: add Portless development URLs 2026-08-20 08:15:54 +03:00
README.md feat: export components as Storybook stories (#751) 2026-09-30 03:16:47 +04:00
SECURITY.md docs: document private security reporting 2026-05-17 12:52:10 +03:00
steiger.config.ts build(tools): group tools by role and gate them like the rest of the repo (#791) 2026-09-30 05:00:31 +04:00
tsconfig.json refactor: rename @open-pencil/codegen to @open-pencil/emit (#822) 2026-10-04 11:22:20 +00:00
tsconfig.node.json ci: validate PR commits and streamline package verification 2026-09-15 20:51:33 +03:00
vite.config.ts feat: preview streamed JSX on the canvas (#692) 2026-10-01 10:52:36 +04:00
wdio.conf.ts test(native): cover the desktop MCP server lifecycle (#723) 2026-09-18 11:42:55 +03:00

OpenPencil

MIT license npm Discord GitHub Discussions

Open-source design editor. Opens .fig and .pen design files, includes built-in AI, and ships as a programmable toolkit with a headless Vue SDK for building custom editors.

Status: Active development. Usable today, with some rough edges as features evolve.

Try it online → · Download · Documentation · Roadmap · llms.txt

OpenPencil

Installation

macOS (Homebrew):

brew install --cask openpencil

Or download from the releases page, or use the web app — no install needed.

Requires macOS 13 or later with current Safari updates, Windows 10 or later, or Linux with WebKitGTK 2.40+; the web app needs Chrome 111, Edge 111, Firefox 128, or Safari 16.4 or later. See system requirements.

What it does

  • Opens .fig and .pen files — read and write native Figma files, open supported Pencil documents from the app or OS file browser, copy & paste nodes between apps
  • AI builds designs — describe what you want in chat, 100+ tools create and modify nodes. Connect OpenRouter, Anthropic, OpenAI, Google AI, DeepSeek, Z.ai, MiniMax, or compatible endpoints
  • Fully programmable — headless CLI, XPath queries, Figma Plugin API via eval, MCP server for AI agents, and desktop agent integrations for Claude Code, Codex, and Gemini CLI
  • Lint, convert, and extract tokens — inspect documents, lint naming/layout/accessibility, convert between supported formats, analyze colors/typography/spacing/clusters, and extract design tokens
  • Components and variants — create reusable components, group variants into component sets, insert local assets as instances, and switch variants from the inspector
  • Image vectorization — convert image layers into editable vector layers with Recraft or fal.ai
  • Design-to-code export — export selections as JSX/Tailwind, generate token outputs, and map designs into component-oriented code workflows
  • Vue SDK for custom editors — headless components and composables for embedding OpenPencil into other apps or building workflow-specific editing surfaces. Read the SDK docs →
  • Real-time collaboration — P2P via WebRTC, no server, no account. Cursors, presence, follow mode
  • Auto layout & CSS Grid — flex and grid layout via Yoga WASM, with gap, padding, alignment, track sizing
  • ~15 MB desktop app — Tauri v2 for macOS, Windows, Linux. Also runs in the browser as a PWA

CLI

npm install -g @open-pencil/cli
# or: bun add -g @open-pencil/cli

Inspect design files

Browse node trees, search by name or type, dig into properties — all without opening the editor:

openpencil tree design.fig
openpencil find design.pen --type TEXT
openpencil node design.fig --id 1:23
openpencil info design.fig
[0] [page] "Getting started" (0:46566)
  [0] [section] "" (0:46567)
    [0] [frame] "Body" (0:46568)
      [0] [frame] "Introduction" (0:46569)
        [0] [frame] "Introduction Card" (0:46570)
          [0] [frame] "Guidance" (0:46571)

Query with XPath

Use XPath selectors to find nodes by type, attributes, and structure:

openpencil query design.fig "//FRAME"                              # All frames
openpencil query design.fig "//FRAME[@width < 300]"                # Frames under 300px
openpencil query design.fig "//TEXT[contains(@name, 'Button')]"     # Text with 'Button' in name
openpencil query design.fig "//*[@cornerRadius > 0]"               # Rounded corners
openpencil query design.fig "//SECTION//TEXT"                       # Text inside sections

Export

Render to PNG, JPG, WEBP, SVG, PDF, PPTX, HTML, JSX, Storybook stories, or .fig — or export selections/pages as .fig and convert whole documents between supported formats:

openpencil export design.fig                           # PNG
openpencil export design.fig -f jpg -s 2 -q 90        # JPG at 2x, quality 90
openpencil export design.fig -f fig --page "Page 1"   # Export a page as .fig
openpencil export design.fig -f jsx --style tailwind   # Tailwind JSX
openpencil export design.fig -f html --css tailwind    # Tailwind HTML fragment
openpencil export design.fig -f html --html standalone --assets external # HTML + assets
openpencil export design.fig -f storybook --framework vue # Storybook stories per component
openpencil convert design.pen output.fig               # Convert between document formats
openpencil import page.html --css styles.css -o page.fig # HTML/CSS → editable .fig

DOM/CSS input flows through @open-pencil/dom-css, so HTML, authored CSS, and Tailwind utility CSS can become editable OpenPencil layers:

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
<div className="flex flex-col gap-4 p-6 bg-white rounded-xl">
  <p className="text-2xl font-bold text-[#1D1B20]">Card Title</p>
  <p className="text-sm text-[#49454F]">Description text</p>
</div>

Lint design files

Catch naming, layout, structure, and accessibility issues from the terminal:

openpencil lint design.fig
openpencil lint design.pen --preset strict
openpencil lint design.fig --rule color-contrast
openpencil lint design.fig --list-rules

Analyze and extract design tokens

Audit an entire design system from the terminal — find inconsistencies, extract the real palette, and spot components waiting to be extracted:

openpencil analyze colors design.fig
openpencil analyze typography design.fig
openpencil analyze spacing design.fig
openpencil analyze clusters design.fig
openpencil analyze overlaps design.fig
openpencil variables design.fig
#1d1b20  ██████████████████████████████ 17155×
#49454f  ██████████████████████████████ 9814×
#ffffff  ██████████████████████████████ 8620×
#6750a4  ██████████████████████████████ 3967×

3771× frame "container" (100% match)
     size: 40×40, structure: Frame > [Frame]

2982× instance "Checkboxes" (100% match)
     size: 48×48, structure: Instance > [Frame]

Script with Figma Plugin API

eval gives you the full Figma Plugin API. Modify the file, write it back:

openpencil eval design.fig -c "figma.currentPage.children.length"
openpencil eval design.fig -c "figma.currentPage.selection.forEach(n => n.opacity = 0.5)" -w

Control the running app

When the desktop app is running, omit the file argument — the CLI connects via RPC and operates on the live canvas. Useful for automation scripts, CI pipelines, or AI agents that need to interact with the editor:

openpencil tree                               # Inspect the live document
openpencil export -f png                      # Screenshot the current canvas
openpencil eval -c "figma.currentPage.name"   # Query the editor

All commands support --json for machine-readable output.

AI & MCP

Built-in chat

Press ⌘J (CtrlJ on Windows and Linux) to open the AI assistant. It has 100+ tools that can create shapes, set fills and strokes, manage auto-layout, work with components and variables, run boolean operations, analyze design tokens, and export assets. Bring your own API key for OpenRouter, Anthropic, OpenAI, Google AI, DeepSeek, Z.ai, MiniMax, or compatible endpoints. No backend, no account.

Not every provider works in the browser, and not every model streams tool calls correctly. See BYOK provider & model compatibility for measured results — contributions welcome.

Coding agents (desktop)

Use Claude Code, Codex, or Gemini CLI directly in the chat panel. The agent connects to the editor's MCP server and uses all 100+ design tools. Requires the desktop app and the agent CLI installed locally.

Pi is also available as an optional AI SDK Harness provider. Install its companion CLI with npm install -g @open-pencil/harness, then add a Pi model profile in Settings → AI & agents. The companion is installed separately so OpenPencil does not bundle a JavaScript runtime for users who do not enable Harness providers.

Setup (Claude Code):

  1. Install the ACP adapter: npm install -g @agentclientprotocol/claude-agent-acp
  2. Add MCP permission to ~/.claude/settings.json:
    {
      "permissions": {
        "allow": ["mcp__open-pencil__*"]
      }
    }
    
  3. Open the desktop app → ⌘J → select Claude Code from the provider dropdown

MCP server

Connect Claude Code, Cursor, Windsurf, or any MCP client to inspect, modify, and export design documents headlessly. 100+ tools. Full docs →

Stdio (Claude Code, Cursor, Windsurf):

npm install -g @open-pencil/mcp
claude mcp add --scope user open-pencil -- openpencil-mcp

For other MCP clients:

{
  "mcpServers": {
    "open-pencil": {
      "command": "openpencil-mcp"
    }
  }
}

HTTP (scripts, CI):

openpencil-mcp-http   # Unix socket on macOS/Linux + http://127.0.0.1:7600/mcp

Local clients discover the private Unix socket automatically and fall back to localhost TCP. Set PORT=0 to disable TCP on macOS/Linux.

File access: Set OPENPENCIL_MCP_ROOT to scope file operations (open_file, new_document, export path param) to a directory. Defaults to the current working directory.

AI agent skill

Teach your AI coding agent to use OpenPencil — inspect designs, export assets, analyze tokens, modify .fig files:

npx skills add open-pencil/open-pencil

Works with Claude Code, Cursor, Windsurf, Codex, and any agent that supports skills.

For documentation-aware agents, the docs site publishes llms.txt, llms-full.txt, and per-page Markdown files generated from the VitePress docs.

Collaboration

Share a link to co-edit in real time. No server, no account — peers connect directly via WebRTC.

  1. Click the share button in the top-right panel
  2. Share the generated link (app.openpencil.dev/share/<room-id>)
  3. Collaborators see your cursor, selection, and edits in real time
  4. Click a peer's avatar to follow their viewport

Why

Figma is a closed platform that actively fights programmatic access. Their MCP server is read-only. figma-use added full read/write automation via CDP — then Figma 126 killed CDP. Your design files are in a proprietary binary format that only their software can fully read. Your workflows break when they decide to ship a point release.

OpenPencil is the alternative: open source (MIT), reads .fig files natively, every operation is scriptable, and your data never leaves your machine.

See the roadmap for product direction and current Figma compatibility gaps.

Community

Contributing

bun install
bun run dev:portless  # Web editor at https://open-pencil.localhost
bun run tauri dev     # Desktop app (requires Rust)

CONTRIBUTING.md covers setup, quality gates, pull requests, and commits. AGENTS.md maps the repository and links the guide inside each package. Desktop builds need Rust and the Tauri v2 prerequisites; run bun run tauri build.

Tech stack

Layer Tech
Rendering Skia (CanvasKit WASM)
Layout Yoga WASM (flex + grid via fork)
UI Vue 3, Reka UI, Tailwind CSS 4
File format Kiwi binary + Zstd + ZIP
Collaboration Trystero (WebRTC P2P) + Yjs (CRDT)
Desktop Tauri v2
AI/MCP Vercel AI SDK (multi-provider BYOK), MCP SDK, Hono

Acknowledgments

Thanks to @sld0Ant (Anton Soldatov) for creating and maintaining the documentation site.

License

OpenPencil is licensed under the MIT License.

Copyright (c) 2026 Danila Poyarkov and OpenPencil contributors.