* fix: select top-level auto layout frames by their empty area Checked against Figma desktop 126 with real pointer input. The gaps and padding of a top-level frame with auto layout (any direction or grid, filled or not, on the page or in a section) select, highlight, and drag the frame, while clicks on its layers still select those layers. A plain top-level frame's empty area stays background, as #900 recorded; its oracle scenarios only used frames without auto layout. Auto layout frames are hit by their bounds even without a fill, and a press on such a frame's empty area no longer starts a marquee. * feat(canvas): outline auto layout children with dotted lines, as in Figma Hovering a horizontal or vertical auto layout frame outlines its visible direct children, and a single selected layer dots the border of its auto layout parent, in the container's outline color. Grid and plain frames, grandchildren, and multi-selections show none. Line weight and dash were measured from Figma 126 screenshots: 1 px with 1.75 px dashes and 1.25 px gaps in screen space. The editor gains a transforming flag that canvas input sets while layers move, resize, or rotate; the outlines and the padding and gap markers of a selected auto layout frame hide while it is set, as Figma's do. The children outline a selected auto layout frame drew under the pointer now uses the same stroke, and the dash effect is freed after each draw. * fix(canvas): select unfilled frames by their empty area only on a click Live Figma 126, with real pointer input on a top-level card holding an unfilled auto layout row and an unfilled plain frame, with and without auto layout on the card: a click on either frame's empty area selects that frame, while a ⌘-click looks through it to the card. Only top-level auto layout frames now own their empty area in hit testing, so a deep hit no longer stops at a nested unfilled auto layout frame. A click that ends on an open container selects the frame child under the point by its bounds, filled or not, which plain frames did not get before; boards that open by themselves keep their empty area as background. * fix(canvas): select an empty unfilled frame on the page by its bounds Live Figma 126: a click inside an empty frame without a fill selects it, on the page and in a section. The section case already did after the previous commit; on the page the click found nothing, since an unfilled frame without children is not hit. A click that hits nothing now looks for a frame under the point among the scope's children, which skips boards that hold layers, as their empty area stays background. * refactor(scene-graph): read the containers the selection opens in their own helper * fix(canvas): open a selected container, so repeated clicks reach deeper layers In Figma, clicking again at the same point inside a selected frame, component, or instance selects the layer under the pointer, so repeated clicks walk down to a text in a nested component such as shadcn's calendar. The click hit test opened only the ancestors of the selection, so a selected nested container stopped the walk at itself and only a double-click went deeper. A selected container with layers now opens like its ancestors; groups and booleans stay closed, as in Figma. Pressing inside the selection still drags it, and its empty area keeps it selected. |
||
|---|---|---|
| .claude | ||
| .devcontainer | ||
| .github | ||
| .storybook | ||
| .vscode | ||
| assets/brand | ||
| desktop | ||
| lint | ||
| packages | ||
| public | ||
| skills/open-pencil | ||
| src | ||
| tests | ||
| tools | ||
| vite | ||
| .coderabbit.yaml | ||
| .gitattributes | ||
| .gitignore | ||
| .gitleaks.toml | ||
| .lfsconfig | ||
| .oxfmtrc.json | ||
| AGENTS.md | ||
| bun.lock | ||
| bunfig.toml | ||
| CHANGELOG.md | ||
| commitlint.config.ts | ||
| CONTRIBUTING.md | ||
| index.html | ||
| knip.json | ||
| LICENSE | ||
| oxlint.json | ||
| package.json | ||
| playwright.config.ts | ||
| portless.json | ||
| README.md | ||
| SECURITY.md | ||
| steiger.config.ts | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| tsconfig.tests.json | ||
| updater.html | ||
| vite.config.ts | ||
| wdio.conf.ts | ||
OpenPencil
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
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
.figand.penfiles — 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 plus an
openpencilAPI viaeval, 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
- Working controls — give a component a Reka UI behaviour (switch, slider, tabs, text field, …) and preview it live over the canvas, with real inputs, focus, and keyboard
- 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
Find nodes by type, attributes, and structure:
openpencil query design.fig "//FRAME[@width < 300]" # Frames under 300px
openpencil query design.fig "//SECTION//TEXT" # Text inside sections
Export, convert, and import
Render to PNG, JPG, WEBP, SVG, PDF, PPTX, HTML, JSX, Storybook stories, or .fig, convert between document formats, and turn HTML/CSS into editable layers:
openpencil export design.fig # PNG
openpencil export design.fig -f jpg -s 2 # JPG at 2x
openpencil export design.fig -f tailwind-jsx # Tailwind JSX
openpencil export design.fig -f storybook # Storybook stories per component
openpencil convert design.pen design.fig # Between document formats
openpencil import card.html --css card.css # HTML/CSS → editable .fig
Lint and analyze
Catch naming, layout, and accessibility issues, and audit a design system's real palette, type scale, spacing, and repeated components:
openpencil lint design.fig --preset strict # Naming, layout, accessibility
openpencil analyze colors design.fig # Also typography, spacing, clusters
openpencil variables design.fig # Variables and collections
#1d1b20 ██████████████████████████████ 17155×
#49454f ██████████████████████████████ 9814×
#ffffff ██████████████████████████████ 8620×
#6750a4 ██████████████████████████████ 3967×
Script with the Figma Plugin API
eval runs JavaScript against the document with Figma's Plugin API; -w writes the result back:
openpencil eval design.fig -c "figma.currentPage.selection.forEach(n => n.opacity = 0.5)" -w
Control the running app
Omit the file argument and the CLI works on the document open in the editor:
openpencil tree # Inspect the live document
openpencil documents list # Also open, new, save, close, activate
openpencil tool call get_selection # Run any MCP tool
openpencil undo # Undo the newest automation change
openpencil settings set appearance.theme light # Change editor settings
Every command supports --json. See the CLI reference for all commands and options.
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 works too: choose it in guided setup (Settings → AI & agents → Run guided setup), which installs the @open-pencil/harness companion with one click and uses the providers you signed in to in Pi. The companion needs Node.js 22.15 or later; see Coding agents.
Setup (Claude Code):
- Install the ACP adapter:
npm install -g @agentclientprotocol/claude-agent-acp - Add MCP permission to
~/.claude/settings.json:{ "permissions": { "allow": ["mcp__open-pencil__*"] } } - 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
Set OPENPENCIL_MCP_ROOT to limit file access to one directory; it 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.
- Click the share button in the top-right panel
- Share the generated link (
app.openpencil.dev/share/<room-id>) - Collaborators see your cursor, selection, and edits in real time
- 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
- Discord — chat, quick questions, and showing a problem live
- GitHub Discussions — Q&A for help, Ideas for feature proposals, Show and tell for what you built; maintainers post Announcements there
- Issues — reproducible bugs; report security problems through a private advisory
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.
