From 3ea310d092ea8db2141942c312df7fd76bc4b904 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Tue, 26 May 2026 16:00:50 +0300 Subject: [PATCH] fix(io): preserve imported canvas background paints --- packages/core/src/io/formats/fig/export.ts | 5 +++++ packages/docs/development/roadmap.md | 2 +- tests/engine/io/fig/roundtrip/source-metadata.test.ts | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/core/src/io/formats/fig/export.ts b/packages/core/src/io/formats/fig/export.ts index 65fdb0a32..e3d038e36 100644 --- a/packages/core/src/io/formats/fig/export.ts +++ b/packages/core/src/io/formats/fig/export.ts @@ -211,6 +211,11 @@ function applyImportedCanvasFields(page: FigExportPage, canvasNc: KiwiNodeChange if ('backgroundColor' in page.source.fig.rawNodeFields) { canvasNc.backgroundColor = structuredClone(page.source.fig.rawNodeFields.backgroundColor) } + if ('backgroundPaints' in page.source.fig.rawNodeFields) { + canvasNc.backgroundPaints = structuredClone( + page.source.fig.rawNodeFields.backgroundPaints + ) as NodeChange['backgroundPaints'] + } const strokeJoin = page.source.fig.rawNodeFields.strokeJoin if (typeof strokeJoin === 'string') canvasNc.strokeJoin = strokeJoin const strokeWeight = page.source.fig.rawNodeFields.strokeWeight diff --git a/packages/docs/development/roadmap.md b/packages/docs/development/roadmap.md index 5f71ee414..eb0e191ee 100644 --- a/packages/docs/development/roadmap.md +++ b/packages/docs/development/roadmap.md @@ -194,7 +194,7 @@ OpenPencil deliberately preserves many Figma/Kiwi fields even when they are not | State-group metadata | ↩ | — | — | Preserved only. | | Version/sort/publish/library metadata | ↩ | — | ◐ | Assets UI shows a subset; publish/update workflow is missing. | | Variable and parameter consumption maps | ✅ | ◐ | ◐ | Filtered/preserved for safe round-trip; normalized bindings cover common cases. | -| Page fields: background, page type, guides | ↩ | ◐ | — | Background/page type/guides mostly round-trip. Guides are not rendered/editable. | +| Page fields: background, page type, guides | ↩ | ◐ | — | Background color and background paints round-trip for imported pages; page type/guides mostly round-trip. Guides are not rendered/editable. | | Text internals: `textData`, layout versions, font version, derived data | ✅ | ✅ | — | Important for text fidelity; most internals are not editable. Imported derived text data, leading trim, decoration style, underline decoration paint/offset/thickness, semantic font metadata, and raw OpenType feature toggles are preserved for round-trip when safe; decoration style/thickness/color now render through CanvasKit. | | `fontVariations` | ✅ | ✅ | — | Variable font axes are imported, rendered, and exported for text nodes and style runs. | | Raw paint/effect/vector/geometry payloads | ✅ | ✅ | ◐ | Converted fields render; raw payloads preserve Figma import/export details, including mask, background paint, layout grid, export setting, and prototype interaction metadata where safe. | diff --git a/tests/engine/io/fig/roundtrip/source-metadata.test.ts b/tests/engine/io/fig/roundtrip/source-metadata.test.ts index 61f59181d..516b44ba9 100644 --- a/tests/engine/io/fig/roundtrip/source-metadata.test.ts +++ b/tests/engine/io/fig/roundtrip/source-metadata.test.ts @@ -34,6 +34,15 @@ describe('fig roundtrip source metadata', () => { b: 0.9750000238418579, a: 1 } + page.source.fig.rawNodeFields.backgroundPaints = [ + { + type: 'SOLID', + color: { r: 0.5, g: 0.75, b: 1, a: 1 }, + opacity: 1, + visible: true, + blendMode: 'NORMAL' + } + ] page.source.fig.rawNodeFields.strokeJoin = 'BEVEL' page.source.fig.rawNodeFields.strokeWeight = 0 @@ -62,6 +71,7 @@ describe('fig roundtrip source metadata', () => { expect(canvas?.strokeJoin).toBe('BEVEL') expect(canvas?.strokeWeight).toBe(0) expect(canvas?.backgroundColor).toEqual(page.source.fig.rawNodeFields.backgroundColor) + expect(canvas?.backgroundPaints).toEqual(page.source.fig.rawNodeFields.backgroundPaints) expect(changes.get('4:4812')?.parentIndex?.position).toBe('~~~~~~~~~~1') expect(changes.get('4:4813')?.parentIndex?.position).toBe('~~~~~~~~~~3')