openpencil/packages/pen-core/CLAUDE.md
Fini 1be9ec4a19 fix(canvas): require explicit role:'overlay' for layout-flow escape hatch
isBadgeOverlayNode matched role:'badge'|'pill'|'tag' and pulled those
children out of their parent's auto-layout, rendering them at (0,0) of
the parent and stacking them on top of siblings. But in this repo
badge/pill/tag are inline-component roles (see role-resolver NAME_EXACT_MAP
and strip-redundant-section-fills PROTECTED_ROLES) — they're meant to
flow in layout like any other child.

Rename to isOverlayNode and narrow to role:'overlay'. Add matching
"Layout-escape roles" guidance in role-definitions.md so generation
prompts can reach the new opt-in. Inline roles now flow correctly;
true floating decorations (notification dots, corner ribbons) still
have a dedicated marker.
2026-04-16 21:07:40 +08:00

41 lines
3.8 KiB
Markdown

# pen-core
Pure document tree operations, layout engine, variables, normalization, boolean ops, and merge utilities.
## Structure
- `src/tree-utils.ts` — Tree CRUD: `findNodeInTree`, `findParentInTree`, `removeNodeFromTree`, `updateNodeInTree`, `flattenNodes`, `insertNodeInTree`, `isDescendantOf`, `getNodeBounds`, `findClearX`, `scaleChildrenInPlace`, `rotateChildrenInPlace`, `nodeTreeToSummary`; page helpers: `createEmptyDocument`, `getActivePage`, `getActivePageChildren`, `setActivePageChildren`, `getAllChildren`, `migrateToPages`, `ensureDocumentNodeIds`; clone utilities: `deepCloneNode`, `cloneNodeWithNewIds`, `cloneNodesWithNewIds`; constants: `DEFAULT_FRAME_ID`, `DEFAULT_PAGE_ID`
- `src/normalize.ts` — `normalizePenDocument`: format-only normalization (fill type "color" to "solid", gradient stop position to offset, sizing strings, padding arrays). Preserves `$variable` refs
- `src/boolean-ops.ts` — `canBooleanOp`, `executeBooleanOp` (union/subtract/intersect via Paper.js headless)
- `src/sync-lock.ts` — `isFabricSyncLocked`, `setFabricSyncLock`: prevents circular document-store to canvas sync
- `src/arc-path.ts` — `buildEllipseArcPath`, `isArcEllipse`: SVG arc path generation for partial ellipses
- `src/path-anchors.ts` — `anchorsToPathData`, `pathDataToAnchors`, `getPathBoundsFromAnchors`, `inferPathAnchorPointType`
- `src/font-utils.ts` — `cssFontFamily`: CSS font-family string builder
- `src/node-helpers.ts` — `isOverlayNode` (detects `role: 'overlay'`), `sanitizeName` (PascalCase conversion)
- `src/design-md-parser.ts` — `parseDesignMd`, `generateDesignMd`, `designMdColorsToVariables`, `extractDesignMdFromDocument`
- `src/constants.ts` — Canvas rendering constants (zoom limits, colors, snap thresholds, pen tool sizes, guide styling)
- `src/id.ts` — `generateId` (nanoid wrapper)
- `src/layout/engine.ts` — Auto-layout computation: `resolvePadding`, `getNodeWidth`, `getNodeHeight`, `computeLayoutPositions`, `inferLayout`, `fitContentWidth`, `fitContentHeight`, `isNodeVisible`, `setRootChildrenProvider`, `getRootFillWidthFallback`
- `src/layout/text-measure.ts` — Text measurement: `estimateTextWidth`, `estimateTextWidthPrecise`, `estimateTextHeight`, `resolveTextContent`, `parseSizing`, `defaultLineHeight`, `hasCjkText`, `isCjkCodePoint`, `countWrappedLinesFallback`, `setWrappedLineCounter`
- `src/layout/normalize-tree.ts` — `normalizeTreeLayout`: infers missing layout mode on frames, strips child x/y in layout containers
- `src/layout/unwrap-fake-phone-mockup.ts` — `unwrapFakePhoneMockups`: repairs AI-generated fake phone mockup frames
- `src/layout/strip-redundant-section-fills.ts` — `stripRedundantSectionFills`: removes redundant dark fills from section containers
- `src/normalize/normalize-stroke-fill-schema.ts` — `normalizeStrokeFillSchema`: repairs AI-generated stroke/fill schema violations
- `src/variables/resolve.ts` — `isVariableRef`, `getDefaultTheme`, `resolveVariableRef`, `resolveColorRef`, `resolveNumericRef`, `resolveNodeForCanvas`
- `src/variables/replace-refs.ts` — `replaceVariableRefsInTree`: recursively rename/delete `$variable` refs in node trees
- `src/merge/node-diff.ts` — `diffDocuments`: one-direction diff producing `NodePatch[]` (add/remove/modify/move)
- `src/merge/node-merge.ts` — `mergeDocuments`: pure 3-way merge of PenDocument trees, returns `MergeResult` with conflicts
- `src/merge/merge-helpers.ts` — Shared helpers for diff/merge (node indexing, field comparison)
## Key patterns
- All tree operations are pure functions returning new references (structural sharing)
- `$variable` refs are preserved in the document; resolution happens at render time via `resolveNodeForCanvas()`
- Layout engine resolves `fill_container`/`fit_content` sizing and computes absolute positions
## Testing
```bash
bun --bun vitest run packages/pen-core/src/__tests__/
```