mirror of
https://github.com/ZSeven-W/openpencil.git
synced 2026-06-01 03:14:29 +07:00
The `op export` command was removed in 0.7.x but the README still advertised it (#116). The pen-mcp README also documented an `npx @zseven-w/pen-mcp` quick-start that never worked because the package ships TypeScript source against workspace-only deps with no `bin` entry (#117). - Strip `op export` references from all 15 root and 15 cli READMEs - Sync AGENTS.md, CLAUDE.md, apps/cli/CLAUDE.md to match the codegen- pipeline reality (no standalone export command anymore) - Rewrite pen-mcp README's quick-start: explain the package ships as part of the OpenPencil app and external clients connect over HTTP Closes #116 Closes #117 |
||
|---|---|---|
| .. | ||
| src | ||
| CLAUDE.md | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
@zseven-w/pen-figma
Figma .fig file parser and converter for OpenPencil. Import Figma designs directly into the OpenPencil document model — binary file parsing, multi-page support, clipboard paste, and full style conversion.
Install
npm install @zseven-w/pen-figma
# or
bun add @zseven-w/pen-figma
Overview
This package handles the complete pipeline from Figma's proprietary binary format to OpenPencil's PenDocument:
.fig binary → Kiwi schema decode → FigmaNodeChange[] → tree building → PenNode[] → PenDocument
It supports:
- Binary
.figfiles (Kiwi schema + zstd/zip compression) - Figma clipboard HTML (copy from Figma → paste in OpenPencil)
- All node types: frames, groups, components, instances, shapes, text, vectors, images
- Full style conversion: fills, strokes, effects, gradients, auto-layout, typography
Usage
Parse a .fig file
import { parseFigFile, figmaAllPagesToPenDocument } from '@zseven-w/pen-figma';
const buffer = await fs.readFile('design.fig');
const figFile = parseFigFile(buffer);
const document = figmaAllPagesToPenDocument(figFile);
console.log(`Imported ${document.pages?.length} pages`);
Single page import
import { parseFigFile, getFigmaPages, figmaToPenDocument } from '@zseven-w/pen-figma';
const figFile = parseFigFile(buffer);
const pages = getFigmaPages(figFile);
// Import just the first page
const document = figmaToPenDocument(figFile, pages[0]);
Clipboard paste
Detect and convert Figma clipboard data (when users copy from Figma and paste into OpenPencil):
import {
isFigmaClipboardHtml,
extractFigmaClipboardData,
figmaClipboardToNodes,
} from '@zseven-w/pen-figma';
document.addEventListener('paste', (e) => {
const html = e.clipboardData?.getData('text/html');
if (html && isFigmaClipboardHtml(html)) {
const data = extractFigmaClipboardData(html);
const nodes = figmaClipboardToNodes(data);
// Insert nodes into document...
}
});
Convert individual nodes
For lower-level access (e.g., incremental sync):
import { figmaNodeChangesToPenNodes } from '@zseven-w/pen-figma';
const penNodes = figmaNodeChangesToPenNodes(figmaNodeChanges, options);
Icon resolution
Register an icon lookup function for converting Figma component instances to icon nodes:
import { setIconLookup } from '@zseven-w/pen-figma';
setIconLookup((name) => {
// Return SVG path data for the icon name, or null
return iconRegistry[name]?.path ?? null;
});
Image resolution
Resolve embedded image blob references to data URLs:
import { resolveImageBlobs } from '@zseven-w/pen-figma';
const images = resolveImageBlobs(figFile);
// Map<blobHash, dataURL>
Conversion Coverage
Node Types
| Figma Type | PenNode Type | Notes |
|---|---|---|
| FRAME | frame |
With auto-layout, constraints, clip content |
| GROUP | group |
Preserves child transforms |
| COMPONENT | frame |
Marked as reusable |
| INSTANCE | frame |
Resolved with overrides |
| RECTANGLE | rectangle |
Corner radius (uniform + per-corner) |
| ELLIPSE | ellipse |
Arc support |
| LINE | line |
Stroke properties |
| TEXT | text |
Rich text with styled segments |
| VECTOR | path |
Complex paths, boolean ops, stars, polygons |
| IMAGE | image |
Embedded or referenced |
Styles
| Figma Style | Conversion |
|---|---|
| Solid fills | PenFill with solid type |
| Linear gradients | PenFill with gradient stops + angle |
| Radial gradients | PenFill with center + radius |
| Image fills | PenFill with image source |
| Strokes | PenStroke with cap, join, dash |
| Drop shadows | PenEffect shadow |
| Inner shadows | PenEffect inner shadow |
| Blur | PenEffect blur |
| Auto-layout | layout, gap, padding, justifyContent, alignItems |
| Text styles | fontSize, fontWeight, fontFamily, lineHeight, letterSpacing |
| Rich text | StyledTextSegment[] with per-run styles |
API Reference
| Function | Description |
|---|---|
parseFigFile(buffer) |
Parse binary .fig file → FigmaDecodedFile |
figmaAllPagesToPenDocument(file) |
Convert all pages → PenDocument |
figmaToPenDocument(file, page) |
Convert single page → PenDocument |
getFigmaPages(file) |
List available pages |
figmaNodeChangesToPenNodes(changes) |
Convert raw node changes → PenNode[] |
isFigmaClipboardHtml(html) |
Detect Figma clipboard data |
extractFigmaClipboardData(html) |
Extract base64 .fig from clipboard HTML |
figmaClipboardToNodes(data) |
Convert clipboard data → PenNode[] |
resolveImageBlobs(file) |
Resolve image references → data URLs |
setIconLookup(fn) |
Register icon name → SVG path resolver |