Add Programmable docs section with CLI, JSX, MCP, AI, Collab

New top-level nav section documenting OpenPencil's programmability:
- CLI: inspecting, exporting, analyzing, scripting (eval)
- JSX Renderer: elements, style props, visual diffing
- MCP Server: moved from Reference (setup + tool list)
- AI Chat: setup, 87 tools, example prompts
- Collaboration: room sharing, cursors, follow mode

Restructured sidebar:
- Programmable added to nav bar (7 locales)
- Context Menu moved from User Guide to Reference
- MCP and eval-command removed from Reference
This commit is contained in:
Danila Poyarkov 2026-03-08 13:22:36 +03:00
parent 11f0fa73df
commit a4b810620f
10 changed files with 867 additions and 24 deletions

View file

@ -25,8 +25,21 @@ interface SidebarLabels {
figmaMatrix: string
}
interface ProgrammableLabels {
cli: string
inspecting: string
exporting: string
analyzing: string
scripting: string
jsxRenderer: string
mcpServer: string
aiChat: string
collaboration: string
}
interface NavLabels {
userGuide: string
programmable: string
reference: string
development: string
openApp: string
@ -52,7 +65,6 @@ const userGuideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.Sideba
text: l.organizing,
items: [
{ text: l.layers, link: `${prefix}/user-guide/layers-and-pages` },
{ text: l.contextMenu, link: `${prefix}/user-guide/context-menu` },
{ text: l.exporting, link: `${prefix}/user-guide/exporting` },
],
},
@ -66,6 +78,34 @@ const userGuideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.Sideba
},
]
const programmableSidebar = (prefix: string, p: ProgrammableLabels): DefaultTheme.SidebarItem[] => [
{
text: p.cli,
items: [
{ text: p.inspecting, link: `${prefix}/programmable/cli/inspecting` },
{ text: p.exporting, link: `${prefix}/programmable/cli/exporting` },
{ text: p.analyzing, link: `${prefix}/programmable/cli/analyzing` },
{ text: p.scripting, link: `${prefix}/programmable/cli/scripting` },
],
},
{
text: p.jsxRenderer,
link: `${prefix}/programmable/jsx-renderer`,
},
{
text: p.mcpServer,
link: `${prefix}/programmable/mcp-server`,
},
{
text: p.aiChat,
link: `${prefix}/programmable/ai-chat`,
},
{
text: p.collaboration,
link: `${prefix}/programmable/collaboration`,
},
]
const guideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.SidebarItem[] => [
{
text: l.guide,
@ -80,16 +120,15 @@ const guideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.SidebarIte
},
]
const referenceSidebar = (prefix: string, label: string): DefaultTheme.SidebarItem[] => [
const referenceSidebar = (prefix: string, label: string, l: SidebarLabels): DefaultTheme.SidebarItem[] => [
{
text: label,
items: [
{ text: 'Keyboard Shortcuts', link: `${prefix}/reference/keyboard-shortcuts` },
{ text: l.contextMenu, link: `${prefix}/user-guide/context-menu` },
{ text: 'Node Types', link: `${prefix}/reference/node-types` },
{ text: 'MCP Tools', link: `${prefix}/reference/mcp-tools` },
{ text: 'Scene Graph', link: `${prefix}/reference/scene-graph` },
{ text: 'File Format', link: `${prefix}/reference/file-format` },
{ text: 'Eval Command', link: `${prefix}/eval-command` },
],
},
]
@ -106,20 +145,31 @@ const developmentSidebar = (prefix: string, label: string): DefaultTheme.Sidebar
},
]
const EN_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspecting Files', exporting: 'Exporting', analyzing: 'Analyzing Designs', scripting: 'Scripting', jsxRenderer: 'JSX Renderer', mcpServer: 'MCP Server', aiChat: 'AI Chat', collaboration: 'Collaboration' }
const DE_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Dateien inspizieren', exporting: 'Exportieren', analyzing: 'Designs analysieren', scripting: 'Skripte', jsxRenderer: 'JSX-Renderer', mcpServer: 'MCP-Server', aiChat: 'KI-Chat', collaboration: 'Zusammenarbeit' }
const IT_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Ispezione file', exporting: 'Esportazione', analyzing: 'Analisi design', scripting: 'Scripting', jsxRenderer: 'Renderer JSX', mcpServer: 'Server MCP', aiChat: 'Chat IA', collaboration: 'Collaborazione' }
const FR_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspecter les fichiers', exporting: 'Exporter', analyzing: 'Analyser les designs', scripting: 'Scripts', jsxRenderer: 'Moteur JSX', mcpServer: 'Serveur MCP', aiChat: 'Chat IA', collaboration: 'Collaboration' }
const ES_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspeccionar archivos', exporting: 'Exportar', analyzing: 'Analizar diseños', scripting: 'Scripts', jsxRenderer: 'Renderizador JSX', mcpServer: 'Servidor MCP', aiChat: 'Chat IA', collaboration: 'Colaboración' }
const PL_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspekcja plików', exporting: 'Eksportowanie', analyzing: 'Analiza projektów', scripting: 'Skrypty', jsxRenderer: 'Renderer JSX', mcpServer: 'Serwer MCP', aiChat: 'Czat AI', collaboration: 'Współpraca' }
const RU_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Инспекция файлов', exporting: 'Экспорт', analyzing: 'Анализ дизайна', scripting: 'Скрипты', jsxRenderer: 'JSX-рендерер', mcpServer: 'MCP-сервер', aiChat: 'ИИ-чат', collaboration: 'Совместная работа' }
const localeThemeConfig = (
prefix: string,
nav: NavLabels,
sidebar: SidebarLabels,
prog: ProgrammableLabels,
): DefaultTheme.Config => ({
nav: [
{ text: nav.userGuide, link: `${prefix}/user-guide/` },
{ text: nav.programmable, link: `${prefix}/programmable/` },
{ text: nav.reference, link: `${prefix}/reference/keyboard-shortcuts` },
{ text: nav.development, link: `${prefix}/development/contributing` },
{ text: nav.openApp, link: 'https://app.openpencil.dev' },
],
sidebar: {
[`${prefix}/user-guide/`]: userGuideSidebar(prefix, sidebar),
[`${prefix}/reference/`]: referenceSidebar(prefix, nav.reference),
[`${prefix}/programmable/`]: programmableSidebar(prefix, prog),
[`${prefix}/reference/`]: referenceSidebar(prefix, nav.reference, sidebar),
[`${prefix}/`]: [
...guideSidebar(prefix, sidebar),
...developmentSidebar(prefix, nav.development),
@ -251,37 +301,37 @@ export default defineConfig({
label: 'Deutsch',
lang: 'de',
description: 'Open-Source, KI-nativer Design-Editor. Figma-Alternative.',
themeConfig: localeThemeConfig('/de', { userGuide: 'Benutzerhandbuch', reference: 'Referenz', development: 'Entwicklung', openApp: 'App öffnen' }, DE),
themeConfig: localeThemeConfig('/de', { userGuide: 'Benutzerhandbuch', programmable: 'Programmierbar', reference: 'Referenz', development: 'Entwicklung', openApp: 'App öffnen' }, DE, DE_PROG),
},
it: {
label: 'Italiano',
lang: 'it',
description: 'Editor di design open-source, IA-nativo. Alternativa a Figma.',
themeConfig: localeThemeConfig('/it', { userGuide: 'Guida utente', reference: 'Riferimento', development: 'Sviluppo', openApp: 'Apri app' }, IT),
themeConfig: localeThemeConfig('/it', { userGuide: 'Guida utente', programmable: 'Programmabile', reference: 'Riferimento', development: 'Sviluppo', openApp: 'Apri app' }, IT, IT_PROG),
},
fr: {
label: 'Français',
lang: 'fr',
description: 'Éditeur de design open-source, IA-natif. Alternative à Figma.',
themeConfig: localeThemeConfig('/fr', { userGuide: 'Guide utilisateur', reference: 'Référence', development: 'Développement', openApp: "Ouvrir l'app" }, FR),
themeConfig: localeThemeConfig('/fr', { userGuide: 'Guide utilisateur', programmable: 'Programmable', reference: 'Référence', development: 'Développement', openApp: "Ouvrir l'app" }, FR, FR_PROG),
},
es: {
label: 'Español',
lang: 'es',
description: 'Editor de diseño open-source, IA-nativo. Alternativa a Figma.',
themeConfig: localeThemeConfig('/es', { userGuide: 'Guía del usuario', reference: 'Referencia', development: 'Desarrollo', openApp: 'Abrir app' }, ES),
themeConfig: localeThemeConfig('/es', { userGuide: 'Guía del usuario', programmable: 'Programable', reference: 'Referencia', development: 'Desarrollo', openApp: 'Abrir app' }, ES, ES_PROG),
},
pl: {
label: 'Polski',
lang: 'pl',
description: "Open-source'owy edytor graficzny z natywnym AI. Alternatywa dla Figmy.",
themeConfig: localeThemeConfig('/pl', { userGuide: 'Podręcznik', reference: 'Referencja', development: 'Rozwój', openApp: 'Otwórz app' }, PL),
themeConfig: localeThemeConfig('/pl', { userGuide: 'Podręcznik', programmable: 'Programowalny', reference: 'Referencja', development: 'Rozwój', openApp: 'Otwórz app' }, PL, PL_PROG),
},
ru: {
label: 'Русский',
lang: 'ru',
description: 'Дизайн-редактор с открытым исходным кодом. Альтернатива Figma с встроенным ИИ.',
themeConfig: localeThemeConfig('/ru', { userGuide: 'Руководство', reference: 'Справочник', development: 'Разработка', openApp: 'Открыть приложение' }, RU),
themeConfig: localeThemeConfig('/ru', { userGuide: 'Руководство', programmable: 'Программируемость', reference: 'Справочник', development: 'Разработка', openApp: 'Открыть приложение' }, RU, RU_PROG),
},
},
@ -290,6 +340,7 @@ export default defineConfig({
nav: [
{ text: 'User Guide', link: '/user-guide/' },
{ text: 'Programmable', link: '/programmable/' },
{ text: 'Reference', link: '/reference/keyboard-shortcuts' },
{ text: 'Development', link: '/development/contributing' },
{ text: 'Open App', link: 'https://app.openpencil.dev' },
@ -297,19 +348,8 @@ export default defineConfig({
sidebar: {
'/user-guide/': userGuideSidebar('', EN),
'/reference/': [
{
text: 'Reference',
items: [
{ text: 'Keyboard Shortcuts', link: '/reference/keyboard-shortcuts' },
{ text: 'Node Types', link: '/reference/node-types' },
{ text: 'MCP Tools', link: '/reference/mcp-tools' },
{ text: 'Scene Graph', link: '/reference/scene-graph' },
{ text: 'File Format', link: '/reference/file-format' },
{ text: 'Eval Command', link: '/eval-command' },
],
},
],
'/programmable/': programmableSidebar('', EN_PROG),
'/reference/': referenceSidebar('', 'Reference', EN),
'/': [
...guideSidebar('', EN),
{

View file

@ -0,0 +1,47 @@
---
title: AI Chat
description: Built-in AI assistant with 87 tools for creating and modifying designs.
---
# AI Chat
Press <kbd></kbd><kbd>J</kbd> (<kbd>Ctrl</kbd> + <kbd>J</kbd>) to open the AI assistant. Describe what you want — it creates shapes, sets styles, manages layout, works with components, and analyzes your design.
## Setup
1. Open the AI chat panel (<kbd></kbd><kbd>J</kbd>)
2. Click the settings icon
3. Enter your OpenRouter API key
4. Choose a model (Claude, GPT-4, Gemini, etc.)
No backend, no subscription — your key talks directly to OpenRouter.
## What It Can Do
The assistant has 87 tools across these categories:
- **Create** — frames, shapes, text, components, pages. Renders JSX for complex layouts.
- **Style** — fills, strokes, effects, opacity, corner radius, blend modes.
- **Layout** — auto-layout, alignment, spacing, sizing.
- **Components** — create components, instances, component sets. Manage overrides.
- **Variables** — create/edit variables, collections, modes. Bind to fills.
- **Query** — find nodes, read properties, list pages, fonts, selection.
- **Analyze** — color palette, typography audit, spacing consistency, cluster detection.
- **Export** — PNG, SVG, JSX with Tailwind classes.
- **Vector** — boolean operations, path manipulation.
## Example Prompts
- "Create a card with a title, description, and a blue button"
- "Make all buttons on this page use the same border radius"
- "What fonts are used in this file?"
- "Change the background of the selected frame to a gradient from blue to purple"
- "Export the selected frame as SVG"
- "Find all text nodes with font size less than 12"
## Tips
- Select nodes before asking — the assistant knows what's selected.
- Be specific about colors, sizes, and positions for precise results.
- The assistant can modify multiple nodes in one message.
- Use "undo" in the editor if you don't like the result.

View file

@ -0,0 +1,65 @@
---
title: Analyzing Designs
description: Audit colors, typography, spacing, and repeated patterns in .fig files.
---
# Analyzing Designs
The `analyze` commands audit an entire design system from the terminal — find inconsistencies, extract the real palette, spot components waiting to be extracted.
## Colors
```sh
open-pencil analyze colors design.fig
```
Finds every color in the file, counts usage, and shows a visual histogram:
```
#1d1b20 ██████████████████████████████ 17155×
#49454f ██████████████████████████████ 9814×
#ffffff ██████████████████████████████ 8620×
#6750a4 ██████████████████████████████ 3967×
```
## Typography
```sh
open-pencil analyze typography design.fig
```
Lists every font family, size, and weight combination with usage counts. Useful for spotting one-off text styles that should be consolidated.
## Spacing
```sh
open-pencil analyze spacing design.fig
```
Audits gap and padding values across auto-layout frames. Helps identify spacing scale inconsistencies — e.g. a stray `13px` gap among otherwise `8/16/24` values.
## Clusters
```sh
open-pencil analyze clusters design.fig
```
Finds repeated node patterns that could be extracted into components:
```
3771× frame "container" (100% match)
size: 40×40, structure: Frame > [Frame]
2982× instance "Checkboxes" (100% match)
size: 48×48, structure: Instance > [Frame]
```
## JSON Output
All analyze commands support `--json` for machine-readable output:
```sh
open-pencil analyze colors design.fig --json
```
Pipe into `jq`, feed into CI checks, or use in scripts that enforce design token budgets.

View file

@ -0,0 +1,59 @@
---
title: Exporting
description: Render .fig files to PNG, JPG, WEBP, SVG, or JSX with Tailwind classes.
---
# Exporting
Export designs from the terminal — raster images, vectors, or JSX code.
## Image Export
```sh
open-pencil export design.fig # PNG (default)
open-pencil export design.fig -f jpg -s 2 -q 90 # JPG at 2×, quality 90
open-pencil export design.fig -f webp -s 3 # WEBP at 3×
open-pencil export design.fig -f svg # SVG vector
```
Options:
- `-f` — format: `png`, `jpg`, `webp`, `svg`, `jsx`
- `-s` — scale: `1``4`
- `-q` — quality: `0``100` (JPG/WEBP only)
- `-o` — output path
- `--page` — page name
- `--node` — specific node ID
## JSX Export
Export as JSX with Tailwind utility classes:
```sh
open-pencil export design.fig -f jsx --style tailwind
```
Output:
```html
<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>
```
Also supports `--style openpencil` for the native JSX format (see [JSX Renderer](../jsx-renderer)).
## Thumbnails
```sh
open-pencil export design.fig --thumbnail --width 1920 --height 1080
```
## Live App Mode
Omit the file to export from the running app:
```sh
open-pencil export -f png # screenshot the current canvas
```

View file

@ -0,0 +1,102 @@
---
title: Inspecting Files
description: Browse node trees, search by name or type, and dig into properties from the terminal.
---
# Inspecting Files
The CLI lets you explore `.fig` files without opening the editor. Every command also works on the live app — just omit the file argument.
## Install
```sh
bun add -g @open-pencil/cli
```
Or with Homebrew:
```sh
brew install open-pencil/tap/open-pencil
```
## Document Info
Get a quick overview — page count, total nodes, fonts used, file size:
```sh
open-pencil info design.fig
```
## Node Tree
Print the full node hierarchy:
```sh
open-pencil tree 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)
```
## Find Nodes
Search by type:
```sh
open-pencil find design.fig --type TEXT
```
Search by name:
```sh
open-pencil find design.fig --name "Button"
```
Both flags can be combined to narrow results further.
## Node Details
Inspect all properties of a specific node by its ID:
```sh
open-pencil node design.fig --id 1:23
```
## Pages
List all pages in the document:
```sh
open-pencil pages design.fig
```
## Variables
List design variables and their collections:
```sh
open-pencil variables design.fig
```
## Live App Mode
When the desktop app is running, omit the file argument — the CLI connects via RPC and operates on the live canvas:
```sh
open-pencil tree # inspect the live document
open-pencil eval -c "..." # query the editor
```
## JSON Output
All commands support `--json` for machine-readable output — pipe into `jq`, feed to CI scripts, or process with other tools:
```sh
open-pencil tree design.fig --json | jq '.[] | .name'
```

View file

@ -0,0 +1,70 @@
---
title: Scripting
description: Execute JavaScript with the Figma Plugin API — query nodes, batch-modify designs, create frames.
---
# Scripting
`open-pencil eval` gives you the full Figma Plugin API in the terminal. Read nodes, modify properties, create shapes — then write changes back to the file.
## Basic Usage
```sh
open-pencil eval design.fig -c "figma.currentPage.children.length"
```
The `-c` flag takes JavaScript. The `figma` global works like the Figma Plugin API.
## Query Nodes
```sh
open-pencil eval design.fig -c "
figma.currentPage.findAll(n => n.type === 'FRAME' && n.name.includes('Button'))
.map(b => ({ id: b.id, name: b.name, w: b.width, h: b.height }))
"
```
## Modify and Save
```sh
open-pencil eval design.fig -c "
figma.currentPage.children.forEach(n => n.opacity = 0.5)
" -w
```
`-w` writes changes back to the input file. Use `-o output.fig` to write to a different file instead.
## Read from Stdin
For longer scripts:
```sh
cat transform.js | open-pencil eval design.fig --stdin -w
```
## Live App Mode
Omit the file to run against the running desktop app:
```sh
open-pencil eval -c "figma.currentPage.name"
```
## Available API
The `figma` object supports:
- `figma.currentPage` — the active page
- `figma.root` — the document root
- `figma.createFrame()`, `figma.createRectangle()`, `figma.createEllipse()`, `figma.createText()`, etc.
- `.findAll()`, `.findOne()` — search descendants
- `.appendChild()`, `.insertChild()` — tree manipulation
- All property setters: `.fills`, `.strokes`, `.effects`, `.opacity`, `.cornerRadius`, `.layoutMode`, `.itemSpacing`, etc.
This is the same API Figma plugins use, so existing knowledge and code snippets transfer directly.
## JSON Output
```sh
open-pencil eval design.fig -c "..." --json
```

View file

@ -0,0 +1,38 @@
---
title: Collaboration
description: Real-time collaborative editing via P2P WebRTC — no server, no account.
---
# Collaboration
Edit designs together in real time. Peers connect directly — no server relays your data, no account required.
## Sharing a Room
1. Click the share button in the top-right corner
2. Copy the generated link (`app.openpencil.dev/share/<room-id>`)
3. Send it to your collaborators
Anyone with the link can join. The room stays active as long as at least one participant has the page open.
## What Syncs
- **Document changes** — every edit (shapes, text, properties, layout) syncs instantly
- **Cursors** — see where each collaborator is pointing, with their name and color
- **Selections** — highlighted selections are visible to everyone
## Follow Mode
Click a collaborator's avatar in the top bar to follow their viewport. Your canvas pans and zooms to match their view. Click again to stop following.
## How It Works
Peers connect directly via WebRTC — your design data goes straight from browser to browser, never through a central server. The document state uses a CRDT (conflict-free replicated data type), so concurrent edits merge automatically without conflicts.
The room persists locally — if you refresh the page, you rejoin with the same state.
## Tips
- Works in the browser and the desktop app
- Room IDs are cryptographically random — only people with the link can join
- Stale cursors are cleaned up automatically when someone disconnects

View file

@ -0,0 +1,51 @@
---
layout: doc
title: Programmable
description: Every operation in OpenPencil is scriptable — CLI, Figma Plugin API, JSX renderer, MCP server, AI chat.
---
# Programmable
OpenPencil treats design files as data. Every operation available in the editor — creating shapes, setting fills, managing auto-layout, exporting assets — is also available from the terminal, from AI agents, and from code. No plugins to install, no API keys, no waiting list.
This is not a bolt-on feature. The editor UI and the programmatic interfaces use the same engine. If you can do it by clicking, you can do it by scripting.
## CLI
Inspect, export, and analyze `.fig` files without opening the editor. List pages, search nodes, extract design tokens, render to PNG — all from the terminal with machine-readable JSON output.
The CLI also connects to the running desktop app via RPC, so you can script the editor while you're using it.
[Inspecting Files](./cli/inspecting) · [Exporting](./cli/exporting) · [Analyzing Designs](./cli/analyzing) · [Scripting](./cli/scripting)
## JSX Renderer
Describe UI as JSX — the same syntax LLMs already know from React. A single `render_jsx` call can create an entire component tree with frames, text, auto-layout, fills, and strokes. Compact, declarative, and diffable.
Going the other direction, export any selection back to JSX with Tailwind classes — useful for handing off to development or feeding designs back into an LLM.
[JSX Renderer →](./jsx-renderer)
## MCP Server
Connect Claude Code, Cursor, Windsurf, or any MCP-compatible client to OpenPencil. The server exposes 90 tools for reading, creating, and modifying designs — the same tools the built-in AI chat uses. Runs over stdio or HTTP with session support.
[MCP Server →](./mcp-server)
## AI Chat
The built-in assistant has access to 87 tools that cover the full surface of the editor. Describe what you want in natural language — "add a 16px drop shadow to all buttons", "create a card component with dark mode variant", "export every frame on this page at 2×".
[AI Chat →](./ai-chat)
## Collaboration
Real-time multiplayer editing over peer-to-peer WebRTC. No server, no account. Share a room link and edit together with live cursors and follow mode. Document state syncs via CRDT, so edits merge automatically even on flaky connections.
[Collaboration →](./collaboration)
## Why Programmable?
Figma is a closed platform. Their MCP server is read-only. CDP browser access was killed in version 126. Design files live in a proprietary format on someone else's servers. Plugin development requires a custom runtime with limited APIs.
OpenPencil is the alternative: open source, MIT licensed, every operation scriptable, data stored locally. Your design files are yours — inspect them, transform them, pipe them into CI, feed them to an LLM. No permission needed.

View file

@ -0,0 +1,120 @@
---
title: JSX Renderer
description: Create designs with JSX — the syntax LLMs already know from millions of React components.
---
# JSX Renderer
OpenPencil uses JSX as its design creation language. LLMs have seen millions of React components — describing a layout as `<Frame><Text>` is natural, no special training needed. Every token matters when an AI agent performs dozens of operations, and JSX is the most compact declarative representation.
JSX is also diffable. When an AI modifies a design, the change is a JSX diff — readable, reviewable, version-controllable.
## Creating Designs
The `render` tool (available in AI chat, MCP, and CLI eval) accepts JSX:
```jsx
<Frame name="Card" w={320} h="hug" flex="col" gap={16} p={24} bg="#FFF" rounded={16}>
<Text size={18} weight="bold">Card Title</Text>
<Text size={14} color="#666">Description text</Text>
</Frame>
```
In the CLI:
```sh
open-pencil eval design.fig -c 'figma.render(`<Frame w={200} h={100} bg="#3B82F6" rounded={8} />`)' -w
```
## Elements
All node types are available as JSX elements:
| Element | Creates | Aliases |
|---------|---------|---------|
| `<Frame>` | Frame (container, supports auto-layout) | `<View>` |
| `<Rectangle>` | Rectangle | `<Rect>` |
| `<Ellipse>` | Ellipse / circle | |
| `<Text>` | Text node (children become text content) | |
| `<Line>` | Line | |
| `<Star>` | Star | |
| `<Polygon>` | Polygon | |
| `<Vector>` | Vector path | |
| `<Group>` | Group | |
| `<Section>` | Section | |
## Style Props
Compact shorthand props inspired by Tailwind's naming.
### Layout
| Prop | Description |
|------|-------------|
| `flex` | `"row"` or `"col"` — enables auto-layout |
| `gap` | Space between children |
| `wrap` | Wrap children to next line |
| `rowGap` | Counter-axis spacing when wrapping |
| `justify` | `"start"`, `"end"`, `"center"`, `"between"` |
| `items` | `"start"`, `"end"`, `"center"`, `"stretch"` |
| `p`, `px`, `py`, `pt`, `pr`, `pb`, `pl` | Padding |
### Size & Position
| Prop | Description |
|------|-------------|
| `w`, `h` | Width/height — number, `"fill"`, or `"hug"` |
| `minW`, `maxW`, `minH`, `maxH` | Size constraints |
| `x`, `y` | Position |
### Appearance
| Prop | Description |
|------|-------------|
| `bg` | Background fill (hex color) |
| `fill` | Alias for `bg` |
| `stroke` | Stroke color |
| `strokeWidth` | Stroke width (default: 1) |
| `rounded` | Corner radius (or `roundedTL`, `roundedTR`, `roundedBL`, `roundedBR`) |
| `cornerSmoothing` | iOS-style smooth corners (01) |
| `opacity` | 01 |
| `shadow` | Drop shadow (e.g. `"0 4 8 #00000040"`) |
| `blur` | Layer blur radius |
| `rotate` | Rotation in degrees |
| `blendMode` | Blend mode |
| `overflow` | `"hidden"` or `"visible"` |
### Typography
| Prop | Description |
|------|-------------|
| `size` / `fontSize` | Font size |
| `font` / `fontFamily` | Font family |
| `weight` / `fontWeight` | `"bold"`, `"medium"`, `"normal"`, or number |
| `color` | Text color |
| `textAlign` | `"left"`, `"center"`, `"right"`, `"justified"` |
## Exporting to JSX
Convert existing designs back to JSX:
```sh
open-pencil export design.fig -f jsx # OpenPencil format
open-pencil export design.fig -f jsx --style tailwind # Tailwind classes
```
The round-trip works: export a design as JSX, modify the code, render it back.
## Visual Diffing
Because designs are representable as JSX, changes become code diffs:
```diff
<Frame name="Card" w={320} flex="col" gap={16} p={24} bg="#FFF">
- <Text size={18} weight="bold">Old Title</Text>
+ <Text size={24} weight="bold" color="#1D1B20">New Title</Text>
<Text size={14} color="#666">Description</Text>
</Frame>
```
This makes design changes reviewable in pull requests, trackable in version control, and auditable in CI.

View file

@ -0,0 +1,251 @@
# MCP Server
OpenPencil includes an MCP (Model Context Protocol) server that lets AI coding tools — Claude Code, Cursor, Windsurf, etc. — read and modify `.fig` files headlessly.
Two transports: **stdio** for MCP clients, **HTTP** for everything else.
## Install
```sh
bun add -g @open-pencil/mcp
```
## Stdio (Claude Code, Cursor, etc.)
Add to your MCP config (e.g. `~/.claude/settings.json` or `.cursor/mcp.json`):
```json
{
"mcpServers": {
"open-pencil": {
"command": "openpencil-mcp"
}
}
}
```
Or run from source without installing:
::: code-group
```json [Bun]
{
"mcpServers": {
"open-pencil": {
"command": "bun",
"args": ["/path/to/open-pencil/packages/mcp/src/index.ts"]
}
}
}
```
```json [Node.js]
{
"mcpServers": {
"open-pencil": {
"command": "npx",
"args": ["tsx", "/path/to/open-pencil/packages/mcp/src/index.ts"]
}
}
}
```
:::
## HTTP
For browser extensions, scripts, CI, or any HTTP client:
```sh
openpencil-mcp-http
```
Or from source: `bun packages/mcp/src/http.ts` / `npx tsx packages/mcp/src/http.ts`
Security defaults (HTTP transport):
- Binds to `127.0.0.1` by default (`HOST` to override)
- `eval` tool is disabled
- File operations are limited to `OPENPENCIL_MCP_ROOT` (defaults to current working directory)
- CORS is disabled by default; set `OPENPENCIL_MCP_CORS_ORIGIN` to allow one origin
- Optional auth token: `OPENPENCIL_MCP_AUTH_TOKEN` (client sends `Authorization: Bearer <token>` or `x-mcp-token`)
Server starts on port 3100 (override with `PORT` env var). Endpoints:
- `GET /health` — server status
- `POST /mcp` — MCP Streamable HTTP (SSE). Sessions via `mcp-session-id` header.
## Workflow
1. **Open**`open_file` to load an existing `.fig`, or `new_document` for a blank canvas
2. **Read**`get_page_tree`, `find_nodes`, `get_node`, `list_pages`
3. **Create**`create_shape`, `render` (JSX)
4. **Modify**`set_fill`, `set_stroke`, `set_layout`, `update_node`, `set_effects`
5. **Structure**`reparent_node`, `group_nodes`, `clone_node`, `delete_node`
6. **Save**`save_file` to write back to `.fig`
## AI Agent Skill
Teach your AI coding agent to use OpenPencil tools:
```sh
npx skills add open-pencil/skills@open-pencil
```
Works with Claude Code, Cursor, Windsurf, Codex, and any agent that supports [skills](https://skills.sh). The skill covers the CLI, MCP tools, JSX rendering, eval, and the running app's automation bridge.
## Tools (90)
### Document
| Tool | Description |
|------|-------------|
| `open_file` | Open a `.fig` file for editing |
| `save_file` | Save the current document to a `.fig` file |
| `new_document` | Create a new empty document |
### Read
| Tool | Description |
|------|-------------|
| `get_selection` | Get currently selected nodes |
| `get_page_tree` | Get the full node tree of the current page |
| `get_current_page` | Get the current page name and ID |
| `get_node` | Get detailed properties of a node by ID |
| `find_nodes` | Find nodes by name pattern and/or type |
| `get_components` | List all components in the document |
| `list_pages` | List all pages |
| `list_variables` | List design variables |
| `list_collections` | List variable collections |
| `list_fonts` | List fonts used in the current page |
| `page_bounds` | Get bounding box of all objects on the current page |
| `node_bounds` | Get bounding box of a node |
| `node_ancestors` | Get ancestor chain of a node |
| `node_children` | Get direct children of a node |
| `node_tree` | Get the subtree rooted at a node |
| `node_bindings` | Get variable bindings on a node |
### Create
| Tool | Description |
|------|-------------|
| `create_shape` | Create a shape (FRAME, RECTANGLE, ELLIPSE, TEXT, LINE, STAR, POLYGON, SECTION) |
| `create_vector` | Create a vector node from a path string |
| `create_slice` | Create an export slice |
| `create_page` | Create a new page |
| `render` | Render JSX to design nodes — create entire component trees in one call |
| `create_component` | Convert a frame/group into a component |
| `create_instance` | Create an instance of a component |
| `node_to_component` | Convert an existing node into a component in-place |
### Modify
| Tool | Description |
|------|-------------|
| `set_fill` | Set fill color (hex) |
| `set_stroke` | Set stroke color, weight, alignment |
| `set_effects` | Add shadow or blur effects |
| `update_node` | Update position, size, opacity, corner radius, text, font |
| `set_layout` | Set auto-layout (flexbox) — direction, spacing, padding, alignment |
| `set_constraints` | Set resize constraints |
| `set_rotation` | Set rotation angle in degrees |
| `set_opacity` | Set opacity (01) |
| `set_radius` | Set corner radius (uniform or per-corner) |
| `set_minmax` | Set min/max width and height constraints |
| `set_text` | Set text content of a TEXT node |
| `set_font` | Set font family and weight |
| `set_font_range` | Set font properties on a character range |
| `set_text_resize` | Set text auto-resize mode (fixed/auto-width/auto-height) |
| `set_visible` | Show or hide a node |
| `set_blend` | Set blend mode |
| `set_locked` | Lock or unlock a node |
| `set_stroke_align` | Set stroke alignment (inside/center/outside) |
| `set_text_properties` | Set text layout: alignment, auto-resize, text case, decoration, truncation |
| `set_layout_child` | Configure auto-layout child: sizing, grow, alignment, absolute positioning |
| `node_move` | Move a node to a new position |
| `node_resize` | Resize a node |
| `node_replace_with` | Replace a node with another node |
| `arrange` | Align or distribute selected nodes |
### Structure
| Tool | Description |
|------|-------------|
| `delete_node` | Delete a node |
| `clone_node` | Duplicate a node |
| `rename_node` | Rename a node |
| `reparent_node` | Move a node into a different parent |
| `select_nodes` | Select nodes by ID |
| `group_nodes` | Group nodes |
| `ungroup_node` | Ungroup a group |
| `flatten_nodes` | Flatten nodes into a single vector |
| `boolean_union` | Boolean union of two or more nodes |
| `boolean_subtract` | Boolean subtraction |
| `boolean_intersect` | Boolean intersection |
| `boolean_exclude` | Boolean exclusion |
### Vector Path
| Tool | Description |
|------|-------------|
| `path_get` | Get the path data of a vector node |
| `path_set` | Set the path data of a vector node |
| `path_scale` | Scale a vector path |
| `path_flip` | Flip a vector path horizontally or vertically |
| `path_move` | Translate a vector path |
### Export
| Tool | Description |
|------|-------------|
| `export_image` | Export nodes as PNG, JPG, or WEBP. Returns base64-encoded image data |
| `export_svg` | Export nodes as SVG markup |
### Viewport
| Tool | Description |
|------|-------------|
| `viewport_get` | Get current viewport position and zoom level |
| `viewport_set` | Set viewport position and zoom |
| `viewport_zoom_to_fit` | Zoom viewport to fit specified nodes |
### Variables
| Tool | Description |
|------|-------------|
| `get_variable` | Get a variable by ID or name |
| `find_variables` | Find variables by name pattern or type |
| `create_variable` | Create a new variable in a collection |
| `set_variable` | Set a variable value in a mode |
| `delete_variable` | Delete a variable |
| `bind_variable` | Bind a variable to a node property |
| `get_collection` | Get a variable collection by ID or name |
| `create_collection` | Create a new variable collection |
| `delete_collection` | Delete a variable collection |
### Analyze
| Tool | Description |
|------|-------------|
| `analyze_colors` | Analyze color palette usage across the document |
| `analyze_typography` | Analyze font/size/weight distribution |
| `analyze_spacing` | Analyze gap and padding values |
| `analyze_clusters` | Detect repeated patterns (potential components) |
### Diff
| Tool | Description |
|------|-------------|
| `diff_create` | Create a snapshot of the current document state |
| `diff_show` | Show differences between the current state and a snapshot |
### Navigation
| Tool | Description |
|------|-------------|
| `switch_page` | Switch to a page by name or ID |
### Escape Hatch
| Tool | Description |
|------|-------------|
| `eval` | Execute JavaScript with full Figma Plugin API access |
Note: `eval` is available over stdio, but disabled in HTTP mode for security.