Commit graph

93 commits

Author SHA1 Message Date
Danila Poyarkov 8983c11f45 feat(design-jsx): support structured effects 2026-06-30 10:45:15 +03:00
Danila Poyarkov cc1167eed5 feat(design-jsx): support structured paints 2026-06-30 10:44:51 +03:00
Danila Poyarkov 4b65e64c18 feat(design-jsx): support variable refs 2026-06-30 10:44:28 +03:00
Danila Poyarkov 568cdf91ef Merge remote-tracking branch 'joey/feature-overlapping-element-analysis-tool' into pr352-local-merge
# Conflicts:
#	tests/engine/clipboard/fixtures/gold-preview-roundtrip.test.ts
#	tests/engine/io/fig/import/group-reclassify.test.ts
#	tests/engine/io/fig/roundtrip/glyph-blob.test.ts
#	tests/engine/layout/auto-layout/text/measurement.test.ts
2026-06-30 09:47:07 +03:00
Joseph Cumines dc9638ac3e
fix(export): prevent GUID collisions and file corruption on .fig round-trip (#333)
* fix(export): prevent GUID collisions and file corruption on .fig round-trip

Nodes sharing the same imported source.id (component instance children,
cloned subtrees) silently overwrote each other on export because the
GUID assignment reused imported GUID values without checking for
duplicates.  This caused data loss on reimport — only the last node
with a given GUID survived.

- Track all assigned GUID values in a Set for O(1) collision detection.
- Scan imported source.ids for both sessionID 0 and 1 before assigning
  any new GUIDs, so the counter starts past every imported value.
- Fall back to counter-based GUIDs when source.id collides with an
  already-assigned value.

Additional fixes in the same change set:

- cloneTree now deep-copies source.fig via structuredClone, preventing
  mutations on a clone from corrupting the original node's kiwi payload.
- Removed decompressFigKiwiData sync wrapper (zero callers) and the
  silent try/catch fallback in parseFigKiwiContainer that masked
  corrupt data as raw bytes.
- buildFigKiwi uses Bun.zstdCompressSync when available, matching the
  zstd decompression path already used on import.
- Fixed setSavedVersion ordering in read.ts — must run after
  requestRender to capture the post-bump version, preventing spurious
  dirty-state immediately after file reload.

Tests: GUID collision (2 and 3 node), clone isolation, parse failure,
text export zstd compatibility, gold-preview round-trip.

* fix(export): handle EXCLUDE boolean operation and BOOLEAN_OPERATION node type

The internal representation uses EXCLUDE for exclude boolean operations,
but Figma's kiwi schema uses XOR. Map EXCLUDE back to XOR on export so
round-trips through .fig files don't fail. Also add BOOLEAN_OPERATION to
VALID_NODE_TYPES and increase timeout for heavy material3 fixture test.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* style: format export-node.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test: add type guard after null assertion in guid-collision test

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clipboard): detect zstd-compressed data before zlib inflate

fflate's inflateSync silently accepts zstd-compressed data and returns
garbage instead of throwing. Check for the zstd magic bytes (28 b5 2f
fd) before attempting zlib decompression. Also revert the EXCLUDE enum
addition to the kiwi schema since the export-node.ts mapping is
sufficient.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(clipboard): add length guard before zstd magic byte check

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(scene-graph): group clone regression coverage

* ci: retrigger CI checks

* test: increase timeouts for heavy .fig fixture tests on slow CI runners

Gold-preview.fig and material3.fig parsing/export tests consistently
exceed the 5s bun:test default timeout on GitHub Actions runners.
Increase to 30s for: beforeAll codec init, clipboard roundtrip,
glyph blob roundtrip, group reclassification, and text measurement.

* test: add individual test timeouts for heavy .fig fixture tests

The beforeAll timeout helped but individual test() calls also need
explicit 30s timeouts since bun:test applies the 5s default per-test.
Fixes remaining CI flakes in glyph-blob roundtrip and clipboard
roundtrip tests.

* test: increase beforeAll timeout for render cache test

The canvas/render cache test loads gold-preview.fig AND initializes
CanvasKit (Skia WASM), which is much slower than the other fixture
tests. Use 60s timeout to account for slow CI runners.

* fix(export): reserve document GUID to prevent 0:0 namespace collision

- Add docGuid (0:0) to assignedGuidValues before processing imported
  node source.ids, preventing an imported node with source.id "0:0"
  from reusing the document's GUID slot
- Add regression test using session-0 source.ids to verify nodes
  survive roundtrip without document GUID collision
- Remove unnecessary async keyword from synchronous component
  metadata test

* fix(export): guard canvas GUID reuse with assignedGuidValues check

- Mirror getOrCreateNodeGuid() collision logic in buildCanvasEntries():
  if an imported page's source.id maps to a GUID already in
  assignedGuidValues, generate a fresh counter-based GUID instead
- Prevents canvas-level last-write-wins when multiple pages share
  the same source.id or a page uses 0:0

* fix(test): use explicit little-endian writes and fix misleading test title

- Replace host-endian Uint32Array writes with DataView.setUint32(offset, value, true) in parse-failures.test.ts to ensure platform-independent fig-kiwi container assembly
- Rename test title from "clone clears source.id from the original" to "clone clears source.id from the clone" to accurately reflect what the assertions verify

* test(io): use file-level timeout for heavy fixture

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-06-23 18:05:23 +03:00
Joseph Cumines fa5c072cad test: extend timeout for slow gold-preview fixture tests
Port the timeout fixes from PR #351 that resolved the unit-test CI
timeouts. Add HEAVY_TEST_TIMEOUT_MS (30s) in tests/helpers/test-utils.ts
and apply it to the slow gold-preview.fig fixture tests/hooks:
- clipboard roundtrip
- group reclassification
- glyph blob preservation
- auto-layout text measurement
- render/canvas/cache
2026-06-19 11:05:25 +10:00
Danila Poyarkov 05e4a29a08 feat(design-jsx): support components and instances 2026-05-31 18:33:56 +03:00
Danila Poyarkov 22ff3acdb5 Merge remote-tracking branch 'origin/master' into figma-compat-render-gaps
# Conflicts:
#	CHANGELOG.md
2026-05-31 13:59:19 +03:00
Danila Poyarkov 6ee0e5f9e8 fix(kiwi): preserve imported boolean geometry 2026-05-31 13:28:41 +03:00
Danila Poyarkov f9bd0c7be9 fix(canvas): render complex text fills as outlines 2026-05-30 12:32:55 +03:00
Danila Poyarkov 6ae425ee3b fix(core): address compatibility review findings 2026-05-28 09:57:46 +03:00
Danila Poyarkov 5fcd8e0e9c fix(canvas): support stretched layout grids 2026-05-28 05:39:40 +03:00
Danila Poyarkov 64e8b21949 fix(canvas): render imported page guides 2026-05-27 19:00:23 +03:00
Danila Poyarkov 42fcb37f4d fix(canvas): render imported layout grids 2026-05-27 17:57:41 +03:00
Danila Poyarkov 033326cffb fix(canvas): render raw noise effect fallback 2026-05-27 10:53:12 +03:00
Danila Poyarkov 4224179501 fix(canvas): render derived text decoration styles 2026-05-27 10:11:06 +03:00
Danila Poyarkov 94dd6cf3aa fix(canvas): tune Figma pattern origin 2026-05-26 21:21:49 +03:00
Danila Poyarkov e02782d1b5 test(canvas): record Figma visual oracle diffs 2026-05-26 20:44:18 +03:00
Danila Poyarkov c1532610db fix(canvas): tighten Figma text and pattern fidelity 2026-05-26 18:30:08 +03:00
Danila Poyarkov d208cc326e fix(canvas): render Figma pattern fills 2026-05-26 17:33:45 +03:00
Danila Poyarkov 1e1d78c7f6 fix(canvas): render Figma leading trim 2026-05-26 16:23:59 +03:00
Danila Poyarkov c2a0de5f0c fix(kiwi): support Figma OpenType variant fields 2026-05-26 15:14:23 +03:00
Danila Poyarkov 80daa0d1e9 fix(canvas): render Figma text decoration styles 2026-05-26 13:22:56 +03:00
Danila Poyarkov 84cf18ba80 fix(kiwi): preserve schema paint fields 2026-05-25 09:19:25 +03:00
Danila Poyarkov 5c09ca0299 test(core): stabilize engine unit tests
- 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
2026-05-25 01:41:59 +03:00
Danila Poyarkov afd571341b fix(ai): render common generated JSX aliases 2026-05-24 12:45:42 +03:00
Danila Poyarkov abd795bd9a chore: format codebase 2026-05-24 12:15:29 +03:00
Danila Poyarkov c337905a02 fix(canvas): preserve text OpenType toggles 2026-05-23 01:43:00 +03:00
Danila Poyarkov f36d6b94b7 fix(canvas): apply affine image fill transforms 2026-05-23 01:31:04 +03:00
Danila Poyarkov 762c86e094 Revert "perf(canvas): defer backing rebuilds while zooming"
This reverts commit 21a79d4cd5.
2026-05-23 01:17:48 +03:00
Danila Poyarkov 21a79d4cd5 perf(canvas): defer backing rebuilds while zooming 2026-05-23 01:17:07 +03:00
Danila Poyarkov 0d5db35147 perf(canvas): bound blend and text mask layers 2026-05-23 01:10:24 +03:00
Danila Poyarkov f07d26c50b perf(canvas): bound effect render layers 2026-05-23 01:01:38 +03:00
Danila Poyarkov 7184bc1b06 Revert "perf(canvas): cap retained backing size"
This reverts commit 7b62c8acca.
2026-05-23 00:45:13 +03:00
Danila Poyarkov 7b62c8acca perf(canvas): cap retained backing size 2026-05-23 00:30:30 +03:00
Danila Poyarkov 8c85d2b953 fix(canvas): improve Figma mask and text fidelity 2026-05-23 00:18:33 +03:00
Danila Poyarkov 3dc0904466 fix(canvas): support luminance masks and tile transforms 2026-05-22 23:44:10 +03:00
Danila Poyarkov 98616c8610 fix(canvas): render Figma-style corner smoothing 2026-05-22 23:05:24 +03:00
Danila Poyarkov 76ee122dee fix(canvas): render smoothed corners and effect blend modes 2026-05-22 22:50:58 +03:00
Danila Poyarkov 0ce57ea0fa fix(canvas): render Figma mask stacks 2026-05-22 21:05:35 +03:00
Danila Poyarkov 8e1d5d40e7 fix(canvas): render Figma blend modes 2026-05-22 21:00:34 +03:00
Danila Poyarkov 11c3cfe73e test(canvas): drop brittle shadow source check
- Rely on behavioral effect tests for stroked container shadow coverage
2026-05-22 14:44:16 +03:00
Danila Poyarkov 8f4f7a47e9 fix(canvas): filter retained backing previews
- Keep stale scene backings available during zoom for smooth interaction

- Draw scaled backings with linear filtering to avoid blocky text artifacts
2026-05-22 14:44:16 +03:00
Danila Poyarkov a49f626e3e fix(canvas): render stroked container shadows from bounds
- Use container bounds for stroked frames with drop shadows

- Keep stroke-geometry shadows for childless stroke-only shapes

- Cover the shadcn dropdown menu case
2026-05-22 13:39:29 +03:00
Danila Poyarkov 1c22b3f7a9 test(canvas): align shadow cleanup checks
- 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>
2026-05-22 13:17:04 +03:00
Danila Poyarkov f6e31be707 fix(canvas): delete renderer label resources
- 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>
2026-05-22 13:15:35 +03:00
Danila Poyarkov 420c61e004 fix(canvas): guard retained backing surfaces
- 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>
2026-05-22 12:51:00 +03:00
Danila Poyarkov 9f67a1086d fix(canvas): restore shadow render state on errors
- 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>
2026-05-22 12:47:33 +03:00
Danila Poyarkov 0b4fcc9f77 fix(canvas): render shadows from path shapes
- 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>
2026-05-22 12:37:00 +03:00
Danila Poyarkov a48b23129f fix(fig): stabilize engine test suite 2026-05-21 14:54:17 +03:00