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-core
Core document operations for OpenPencil — tree manipulation, layout engine, design variables, boolean path operations, 3-way merge, and more.
Install
npm install @zseven-w/pen-core
# or
bun add @zseven-w/pen-core
Overview
pen-core is the foundation layer of the OpenPencil stack. It provides pure functions for every document operation — tree CRUD, auto-layout computation, variable resolution, SVG path booleans, and document normalization. All operations are immutable (structural sharing) and framework-free.
Features
Document Tree Operations
Create, query, and mutate the document tree:
import {
createEmptyDocument,
findNodeInTree,
findParentInTree,
insertNodeInTree,
removeNodeFromTree,
updateNodeInTree,
flattenNodes,
isDescendantOf,
getNodeBounds,
} from '@zseven-w/pen-core';
const doc = createEmptyDocument();
const node = findNodeInTree(doc.children, 'node-id');
const parent = findParentInTree(doc.children, 'node-id');
const flat = flattenNodes(doc.children); // all nodes in flat array
Node Cloning
Deep clone nodes with optional ID regeneration:
import { deepCloneNode, cloneNodeWithNewIds, cloneNodesWithNewIds } from '@zseven-w/pen-core';
const clone = deepCloneNode(node); // preserve IDs
const fresh = cloneNodeWithNewIds(node); // new IDs for all descendants
const batch = cloneNodesWithNewIds([a, b, c]); // batch clone
Multi-Page Support
import {
getActivePage,
getActivePageChildren,
setActivePageChildren,
migrateToPages,
ensureDocumentNodeIds,
} from '@zseven-w/pen-core';
// Migrate a single-page document to multi-page format
const multiPageDoc = migrateToPages(doc);
Layout Engine
Flexbox-like auto-layout computation supporting fill_container, fit_content, gap, padding, and alignment:
import {
computeLayoutPositions,
inferLayout,
fitContentWidth,
fitContentHeight,
resolvePadding,
getNodeWidth,
getNodeHeight,
isNodeVisible,
} from '@zseven-w/pen-core';
// Compute absolute positions for all children in a layout container
const positions = computeLayoutPositions(frame, frame.children);
// Infer layout direction from child arrangement
const layout = inferLayout(children); // 'horizontal' | 'vertical' | 'none'
Text Measurement
Estimate text dimensions for layout without a browser DOM:
import {
estimateTextWidth,
estimateTextWidthPrecise,
estimateTextHeight,
resolveTextContent,
hasCjkText,
defaultLineHeight,
} from '@zseven-w/pen-core';
const width = estimateTextWidth('Hello World', 16, 400); // font size, weight
const height = estimateTextHeight(textNode, containerWidth);
const isCjk = hasCjkText('こんにちは'); // true
Design Variables
Resolve $variable references against document variables and theme axes:
import {
isVariableRef,
resolveVariableRef,
resolveNodeForCanvas,
replaceVariableRefsInTree,
getDefaultTheme,
} from '@zseven-w/pen-core';
isVariableRef('$primary'); // true
// Resolve all $refs in a node tree for rendering
const resolved = resolveNodeForCanvas(node, doc.variables, doc.themes);
// Rename $old-name → $new-name across entire tree
replaceVariableRefsInTree(children, 'old-name', 'new-name');
Boolean Path Operations
Union, subtract, intersect, and exclude paths via Paper.js:
import { executeBooleanOp, canBooleanOp, BooleanOpType } from '@zseven-w/pen-core';
if (canBooleanOp(selectedNodes)) {
const result = executeBooleanOp(selectedNodes, BooleanOpType.Union);
}
Document Normalization
Sanitize and fix documents from external sources (Figma imports, AI generation):
import { normalizePenDocument } from '@zseven-w/pen-core';
const cleaned = normalizePenDocument(rawDoc);
// Fixes: fill type "color" → "solid", gradient stop position → offset,
// sizing strings, padding arrays. Preserves $variable refs.
Layout Normalization
Repair AI-generated layout issues:
import {
normalizeTreeLayout,
unwrapFakePhoneMockups,
stripRedundantSectionFills,
normalizeStrokeFillSchema,
} from '@zseven-w/pen-core';
// Infer missing layout modes, strip child x/y in layout containers
normalizeTreeLayout(rootNode);
3-Way Document Merge
Diff and merge document trees for collaborative editing and git integration:
import { diffDocuments, mergeDocuments } from '@zseven-w/pen-core';
// One-direction diff: base → current
const patches = diffDocuments(base, current);
// patches: NodePatch[] — add, remove, modify, move
// 3-way merge: base + ours + theirs
const result = mergeDocuments(base, ours, theirs);
// result: { document, conflicts }
Design.md Parser
Parse and generate design specification documents:
import {
parseDesignMd,
generateDesignMd,
designMdColorsToVariables,
extractDesignMdFromDocument,
} from '@zseven-w/pen-core';
Path Anchors
Convert between anchor point representation and SVG path data:
import {
anchorsToPathData,
pathDataToAnchors,
getPathBoundsFromAnchors,
inferPathAnchorPointType,
} from '@zseven-w/pen-core';
API Reference
| Category | Key Functions |
|---|---|
| Tree CRUD | findNodeInTree, insertNodeInTree, removeNodeFromTree, updateNodeInTree, flattenNodes |
| Cloning | deepCloneNode, cloneNodeWithNewIds, cloneNodesWithNewIds |
| Pages | getActivePage, migrateToPages, ensureDocumentNodeIds |
| Layout | computeLayoutPositions, inferLayout, fitContentWidth, fitContentHeight |
| Text | estimateTextWidth, estimateTextHeight, hasCjkText |
| Variables | resolveVariableRef, resolveNodeForCanvas, replaceVariableRefsInTree |
| Boolean | executeBooleanOp, canBooleanOp |
| Normalize | normalizePenDocument, normalizeTreeLayout |
| Merge | diffDocuments, mergeDocuments |
| IDs | generateId |