@zseven-w/pen-core
v0.7.6
Published
Core document operations, tree utils, variables, layout engine for OpenPencil
Readme
@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-coreOverview
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 arrayNode 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 cloneMulti-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('こんにちは'); // trueDesign 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 |
