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:
parent
11f0fa73df
commit
a4b810620f
|
|
@ -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),
|
||||
{
|
||||
|
|
|
|||
47
packages/docs/programmable/ai-chat.md
Normal file
47
packages/docs/programmable/ai-chat.md
Normal 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.
|
||||
65
packages/docs/programmable/cli/analyzing.md
Normal file
65
packages/docs/programmable/cli/analyzing.md
Normal 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.
|
||||
59
packages/docs/programmable/cli/exporting.md
Normal file
59
packages/docs/programmable/cli/exporting.md
Normal 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
|
||||
```
|
||||
102
packages/docs/programmable/cli/inspecting.md
Normal file
102
packages/docs/programmable/cli/inspecting.md
Normal 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'
|
||||
```
|
||||
70
packages/docs/programmable/cli/scripting.md
Normal file
70
packages/docs/programmable/cli/scripting.md
Normal 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
|
||||
```
|
||||
38
packages/docs/programmable/collaboration.md
Normal file
38
packages/docs/programmable/collaboration.md
Normal 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
|
||||
51
packages/docs/programmable/index.md
Normal file
51
packages/docs/programmable/index.md
Normal 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.
|
||||
120
packages/docs/programmable/jsx-renderer.md
Normal file
120
packages/docs/programmable/jsx-renderer.md
Normal 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 (0–1) |
|
||||
| `opacity` | 0–1 |
|
||||
| `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.
|
||||
251
packages/docs/programmable/mcp-server.md
Normal file
251
packages/docs/programmable/mcp-server.md
Normal 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 (0–1) |
|
||||
| `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.
|
||||
Loading…
Reference in a new issue