- Update EffectType schema assertion for modern Figma enum values
- Treat unsupported OpenType font buffers as unavailable
- Skip CJK outline fallback coverage when the LFS font fixture is not materialized
- Add a repo-wide TypeScript AST duplicate shape check to the quality gate
- Extract shared named types for existing cross-file duplicates
- Keep same-file oxlint coverage for editor feedback
- Add AST-based duplicate object type shape detection
- Reuse shared variable consumption and visual bounds type shapes
- Keep duplicate type shape checks in the default lint gate
- Add AST-based lint checks for broad unknown object assertions and local JsonObject aliases
- Centralize JsonObject in core and package-local MCP RPC JSON typing
- Replace baseline Record<string, unknown> assertions with named shared/domain types
- Resolve numeric variable aliases in symbol override radius fields
- Preserve imported 999 corner sentinels through G0 to G1 roundtrips
- Remove the resolved corner-radius roundtrip TODO
- Use KiwiNodeChange index signature for raw Figma field assignment
- Type preserved raw NodeChange keys with keyof NodeChange
- Replace scene graph property write casts with a typed setter
- Prefer raw derivedTextData for imported Figma nodes during export
- Keep G1 exports idempotent with preserved text glyph and baseline payloads
- Remove the resolved derivedTextData roundtrip TODO
- Refresh the bundled Figma Kiwi schema from a newer embedded schema
- Preserve additional raw node fields and omitted imported stack sizing
- Tighten idempotent roundtrip checks while documenting known G0 import gaps
Exporting a .fig from OpenPencil and reimporting lost most nodes
(38323 to 57 on gold-preview.fig) and the same files wouldn't open
in Figma. gold-preview.fig now roundtrips into Figma. I haven't
traced each symptom to a specific fix with confidence, but the
changes address the underlying causes:
- fractionalPosition overflow past 94 siblings produced characters
outside Figma's printable ASCII range (33-126); gold-preview.fig
has nodes with 492 children so this hit immediately. New telescoping
~-prefix scheme matches Figma's own encoding.
- re-export used OpenPencil's subset kiwi schema, likely producing
field IDs misaligned with the embedded schema. Preserve and use
the original schema (graph.figSchemaDeflated) instead.
- variable GUIDs assigned before canvas entries could collide with
source.id-derived canvas GUIDs. Reversed the order, scan all
imported GUIDs to advance the counter.
- auto-layout child sort ignored parentIndex.position, using only
transform position which may not reflect original tree order for
imported nodes. Sort by position first, transform as tiebreaker.
- Object.assign of rawNodeFields overwrote explicit serialization.
Materialize with paint variables, apply blocklist, skip keys
already set on nc.
Also fixes: strokeWeight defaulting on strokeless nodes, colorVar
assetRef to guid conversion, per-corner radii emission, textAlignVertical
hardcoding, textAutoResize override for imported nodes, stackReverseZIndex
roundtrip, variable key/version preservation, blendMode on effects,
textCase emission.
Adds exhaustive roundtrip test comparing G0 to G1 to G2 across schema
bytes, node count, tree paths, scene props, and raw node fields.
- Use container bounds for stroked frames with drop shadows
- Keep stroke-geometry shadows for childless stroke-only shapes
- Cover the shadcn dropdown menu case
- Keep explicit color filter deletion checks compatible with cleanup guards
- Verify text inner-shadow restore balancing through the restore-count loop
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- Dispose parent outline paint during renderer teardown
- Dispose section and component label fonts with other renderer-owned fonts
- Cover renderer teardown for label resources
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- Fall back to live rendering when CanvasKit cannot allocate the backing surface
- Match retained backing metadata against the current graph preview version
- Cover null-surface and stale-preview backing regressions
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- Restore canvas save layers and shared paints from shadow effect paths when rendering fails
- Delete temporary paths and color filters through error paths without suppressing failures
- Cover drop-shadow and text inner-shadow cleanup regressions
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- Use polygon, star, and vector paths for shadows when geometry blobs are unavailable
- Apply shadow spread to copied geometry paths without mutating caches
- Clip and cut out inner shadows with the same path geometry
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- Delete gradient text paragraphs through the exception path
- Reset shared effect layer paint after gradient text rendering
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- Delete boolean operation paths even when drawing fails
- Reset fill shaders while preserving the existing render path
Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
- Add current Kiwi component prop fields used by Figma exports
- Preserve empty instance-swap preferred value arrays instead of dropping them during encoding
- Add EditInfo to the export schema so preserved raw Figma edit metadata is encoded
- Emit false symbol publishability for components to match imported Figma payloads
- Continue stripping top-level variable maps that crash Figma import
- Preserve nested variable maps in symbol overrides, component assignments, and derived symbol data for better instance reconstruction
- Preserve raw component property definitions, refs, variant specs, and source library metadata
- Add current Figma schema entries needed to encode variantPropSpecs and modern component prop types
- Drop preserved colorVar and opacityVar payload fields together with variable maps
- Keep concrete paint colors as fallbacks so Figma does not render unresolved variables as missing content
- Keep preserving style IDs and paint variable metadata
- Drop variableConsumptionMap and parameterConsumptionMap on export because Figma imports enter invalid Slides rail state with these raw maps
- Add the current Figma pageType field and EditorType enum values
- Write pageType DESIGN on exported CANVAS records so Figma initializes design-mode page state
- Keep live clipboard-derived numbers only for fields emitted by preserved payloads
- Restore unused glyph/layout fields to the schema shape Figma imports without triggering Slides state
- Move stackWrap and stackCounterSpacing beside adjacent live Figma field IDs
- Preserve clipboard-derived field numbers without appending them after newer high IDs
- Move codec, NodeChange conversion, and instance overrides under kiwi/fig
- Flatten the codec entrypoint to kiwi/fig/codec
- Keep schema-runtime as the generic Kiwi parser/compiler boundary
- Move the Kiwi runtime under schema-runtime to match folder naming
- Load the Figma schema from a .kiwi text asset with static validation
- Assert Figma clipboard-derived field numbers for fragile layout/text fields
- Preserve raw paint variables, style ids, text line metadata, and variable maps
- Keep raw node paint/geometry/effect fields for imported Figma nodes
- Extend the Kiwi schema for current Figma override and variable fields
- Keep raw Figma size and transform values for imported nodes during export
- Add current Kiwi schema fields for component prop varValue and symbol id variable values
- Preserve component source geometry used by Figma to reconstruct instances
- Store imported derivedSymbolData on scene nodes with blob bytes remapped for export
- Re-emit derivedSymbolData and layout versions for Figma instances
- Skip unsupported metadata fields that the current Kiwi encoder cannot write
- Keep imported Figma node guids so override paths and symbol references remain stable
- Serialize imported symbolOverrides and uniform scale factors for instances
- Strip unsupported variable consumption maps from preserved overrides before encoding