From a4b810620fc8514c5a495dad5462c1a9f5badd45 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Sun, 8 Mar 2026 13:22:36 +0300 Subject: [PATCH] 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 --- packages/docs/.vitepress/config.ts | 88 +++++-- packages/docs/programmable/ai-chat.md | 47 ++++ packages/docs/programmable/cli/analyzing.md | 65 +++++ packages/docs/programmable/cli/exporting.md | 59 +++++ packages/docs/programmable/cli/inspecting.md | 102 ++++++++ packages/docs/programmable/cli/scripting.md | 70 ++++++ packages/docs/programmable/collaboration.md | 38 +++ packages/docs/programmable/index.md | 51 ++++ packages/docs/programmable/jsx-renderer.md | 120 +++++++++ packages/docs/programmable/mcp-server.md | 251 +++++++++++++++++++ 10 files changed, 867 insertions(+), 24 deletions(-) create mode 100644 packages/docs/programmable/ai-chat.md create mode 100644 packages/docs/programmable/cli/analyzing.md create mode 100644 packages/docs/programmable/cli/exporting.md create mode 100644 packages/docs/programmable/cli/inspecting.md create mode 100644 packages/docs/programmable/cli/scripting.md create mode 100644 packages/docs/programmable/collaboration.md create mode 100644 packages/docs/programmable/index.md create mode 100644 packages/docs/programmable/jsx-renderer.md create mode 100644 packages/docs/programmable/mcp-server.md diff --git a/packages/docs/.vitepress/config.ts b/packages/docs/.vitepress/config.ts index 1bb9fd202..42c6573d9 100644 --- a/packages/docs/.vitepress/config.ts +++ b/packages/docs/.vitepress/config.ts @@ -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), { diff --git a/packages/docs/programmable/ai-chat.md b/packages/docs/programmable/ai-chat.md new file mode 100644 index 000000000..864480015 --- /dev/null +++ b/packages/docs/programmable/ai-chat.md @@ -0,0 +1,47 @@ +--- +title: AI Chat +description: Built-in AI assistant with 87 tools for creating and modifying designs. +--- + +# AI Chat + +Press ⌘J (Ctrl + J) 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 (⌘J) +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. diff --git a/packages/docs/programmable/cli/analyzing.md b/packages/docs/programmable/cli/analyzing.md new file mode 100644 index 000000000..da8ab307b --- /dev/null +++ b/packages/docs/programmable/cli/analyzing.md @@ -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. diff --git a/packages/docs/programmable/cli/exporting.md b/packages/docs/programmable/cli/exporting.md new file mode 100644 index 000000000..058bb6988 --- /dev/null +++ b/packages/docs/programmable/cli/exporting.md @@ -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 +
+

Card Title

+

Description text

+
+``` + +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 +``` diff --git a/packages/docs/programmable/cli/inspecting.md b/packages/docs/programmable/cli/inspecting.md new file mode 100644 index 000000000..af4f17aae --- /dev/null +++ b/packages/docs/programmable/cli/inspecting.md @@ -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' +``` diff --git a/packages/docs/programmable/cli/scripting.md b/packages/docs/programmable/cli/scripting.md new file mode 100644 index 000000000..8c668bc5f --- /dev/null +++ b/packages/docs/programmable/cli/scripting.md @@ -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 +``` diff --git a/packages/docs/programmable/collaboration.md b/packages/docs/programmable/collaboration.md new file mode 100644 index 000000000..b7e2b1de1 --- /dev/null +++ b/packages/docs/programmable/collaboration.md @@ -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/`) +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 diff --git a/packages/docs/programmable/index.md b/packages/docs/programmable/index.md new file mode 100644 index 000000000..a013cc51a --- /dev/null +++ b/packages/docs/programmable/index.md @@ -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. diff --git a/packages/docs/programmable/jsx-renderer.md b/packages/docs/programmable/jsx-renderer.md new file mode 100644 index 000000000..66a1e8cb4 --- /dev/null +++ b/packages/docs/programmable/jsx-renderer.md @@ -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 `` 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 + + Card Title + Description text + +``` + +In the CLI: + +```sh +open-pencil eval design.fig -c 'figma.render(``)' -w +``` + +## Elements + +All node types are available as JSX elements: + +| Element | Creates | Aliases | +|---------|---------|---------| +| `` | Frame (container, supports auto-layout) | `` | +| `` | Rectangle | `` | +| `` | Ellipse / circle | | +| `` | Text node (children become text content) | | +| `` | Line | | +| `` | Star | | +| `` | Polygon | | +| `` | Vector path | | +| `` | Group | | +| `
` | 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 + +- Old Title ++ New Title + Description + +``` + +This makes design changes reviewable in pull requests, trackable in version control, and auditable in CI. diff --git a/packages/docs/programmable/mcp-server.md b/packages/docs/programmable/mcp-server.md new file mode 100644 index 000000000..634725bf1 --- /dev/null +++ b/packages/docs/programmable/mcp-server.md @@ -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 ` 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.