openpencil/packages/fig
Danila Poyarkov 9c28c13fdf
perf(scene-graph): give imported nodes the shape every node shares (#903)
* perf(scene-graph): give imported nodes the shape every node shares

createDefaultNode had no default for booleanOperation, and the .fig
importer sets that key on every node. A key outside an object's initial
shape turns it into a JavaScriptCore dictionary, so every imported node
had its own structure and dictionary property storage. Fully loading
material3.fig took 3.7 GB RSS in Bun; with the default it takes 2.8 GB,
with 0.5 GB less JS heap and 0.46 GB less memory outside it. V8 keeps
these objects fast either way.

The defaults now must name every SceneNode field, which the compiler
enforces, and a test checks that imported nodes carry exactly the
default fields. booleanOperation states the explicit undefined the
importer already stores.

* fix(scene-graph): require booleanOperation and compare node keys in order

Every SceneNode field is required except booleanOperation, so a typed
producer could still omit it and build a node with a different shape.
It is now required, with undefined still meaning no operation. The
node-shape test compares keys in insertion order, since JavaScriptCore
lays out the same keys added in another order as a different shape.

The changelog entry no longer quotes the Bun measurement as figures for
the macOS app and Safari, and records the type change as breaking.
2026-10-05 17:14:12 +00:00
..
docs fix(fig): read, render, and write Figma slots (#850) 2026-10-04 00:45:10 +04:00
scripts feat(fig): occurrence-scoped instance interpretation as the single .fig reader 2026-10-01 11:20:27 +04:00
src test: typecheck the test suites and fix what that found (#896) 2026-10-05 12:42:38 +00:00
tests perf(scene-graph): give imported nodes the shape every node shares (#903) 2026-10-05 17:14:12 +00:00
AGENTS.md feat(fig): occurrence-scoped instance interpretation as the single .fig reader 2026-10-01 11:20:27 +04:00
package.json build: update dependencies (#873) 2026-10-04 12:48:24 +00:00
README.md feat(fig): occurrence-scoped instance interpretation as the single .fig reader 2026-10-01 11:20:27 +04:00
tsconfig.json fix: explain unsupported browsers instead of a blank window (#745) 2026-09-22 14:40:59 +04:00
tsconfig.test.json feat(fig): scaffold package shell 2026-06-30 10:51:36 +03:00
tsdown.config.ts refactor(fig): own Figma clipboard encoding and conversion 2026-09-11 00:36:03 +03:00

@open-pencil/fig

.fig file-format package for OpenPencil.

The package owns the outer .fig archive boundary and is the staged home for Figma-specific SceneGraph conversion policy. Production SceneGraph read/write remains available through @open-pencil/core/io while conversion modules move behind this package's public API.

Current ownership:

  • Complete .fig archive parsing through parseFigBuffer()
  • .fig archive assembly through writeFigArchive()
  • Canvas payload and image resource handling
  • readFigContainer() / writeFigContainer() helpers for raw fig-kiwi payloads
  • .fig source and archive result types
  • NodeChange-to-SceneGraph property conversion, including styles, plugin metadata, text, paint, vector, and font policy, through @open-pencil/fig/node-change
  • Component-property, symbol-override, derived-symbol-data, and instance synchronization policy through @open-pencil/fig/instance-overrides
  • Effective raw-metadata precedence and invalidation over SceneGraph's format-neutral edited-field tracking
  • SceneGraph-to-NodeChange export conversion with an explicit glyph-outline runtime service
  • Package-local archive, conversion, instance, export, and dist smoke tests

Architecture documentation

Start with the package docs for the source model, instance evaluation, materialization, document sessions, export, and validation contracts.

Planned ownership:

  • Oracle-backed .fig fixtures

Non-goals:

  • Generic Kiwi schema/runtime internals — use @open-pencil/kiwi
  • Format-neutral IO registration, export targeting, CanvasKit thumbnails, or browser workers — use @open-pencil/core/io
  • Editor actions, renderer behavior, Vue/app UI, CLI formatting, or MCP transport

This follows the existing @open-pencil/pen pattern: a format package owns its source model/parser and SceneGraph policy, while core registers it in the shared IO system.

Checks

cd packages/fig
bun run check