Commit graph

134 commits

Author SHA1 Message Date
Danila Poyarkov c7b944d103 fix(kiwi): harden FIG containers and DOM imports
- Decode zstd FIG data and reject invalid compressed payloads

- Compose caller CSS with Tailwind defaults during DOM import

- Slice pooled fixture buffers to their exact byte range

Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
2026-08-13 20:33:10 +03:00
Danila Poyarkov 6bd319881e perf(fig): reduce export graph cloning
- Clone mutable node shells without duplicating imported binary resources

- Preserve live graph isolation during lazy population
2026-08-13 17:56:37 +03:00
Danila Poyarkov 05a36e9672 fix(fig): preserve imported instance links
- Remap component IDs through SceneGraph updates so the instance index stays consistent
- Cover imported instances resolving through the remapped source ID
- Restore source component synchronization for the reporter's .fig file
2026-08-11 18:54:29 +03:00
Danila Poyarkov b21509cc8d fix(storage): isolate workspace refresh state
- Discard stale provider refreshes and queued previews

- Guard malformed deflate data and listener failures

- Start periodic refresh without an immediate callback
2026-08-10 22:30:54 +03:00
Danila Poyarkov 4fe27dd1e6 fix(storage): harden workspace previews
- Validate ranged thumbnail payloads and S3 bounds

- Invalidate stale previews and expose loading errors

- Document the public document workspace composable
2026-08-10 20:02:58 +03:00
Danila Poyarkov dde7376bd3 feat(storage): show workspace document previews
- Load embedded Figma thumbnails through bounded S3 byte-range requests

- Add a headless Vue workspace composable with lazy previews and refresh lifecycle

- Cache local previews and refresh the workspace after saves and synchronization
2026-08-10 17:21:36 +03:00
Danila Poyarkov 55b368dc4a fix: complete acronym casing follow-ups
- Cover UI identifiers in the acronym guardrail
- Preserve FIG thumbnail and metadata values through archive parsing
- Use Vue-compatible acronym prop attributes
2026-08-10 16:38:17 +03:00
Danila Poyarkov 7f91594d0c refactor: standardize acronym casing
- Rename first-party API, RPC, JSON, CORS, SVG, JSX, and related identifiers to preserve acronym casing
- Keep upstream and serialized boundary names unchanged
- Add a lint guardrail and migration notes for exported APIs
2026-08-10 15:48:16 +03:00
Victor Wads 54abfca334
fix(fig): address instance rendering review 2026-08-10 05:09:03 -03:00
Victor Wads 0208d79375 test(fig): update roundtrip export size 2026-08-10 04:49:28 -03:00
Victor Wads bc79d16ac4
fix(fig): preserve instance text edits on save 2026-08-10 04:30:28 -03:00
Victor Wads e159893ec4
fix(fig): preserve component instance rendering 2026-08-10 04:30:06 -03:00
Danila Poyarkov 854e1642d8 feat(fig): prototype lazy population worker
- Populate subsequent lazy pages in a development-only persistent worker
- Journal created, updated, and deleted graph state as field deltas
- Invalidate stale worker replicas after authoritative graph mutations
- Fall back to synchronous population when the worker is unavailable
2026-08-07 09:27:54 +03:00
Danila Poyarkov f9701ef855 fix(fig): preserve effective clone geometry
- Restore authoritative image-leaf bounds after final component swaps\n- Preserve derived cross-axis positions for thin divider clones\n- Add synthetic and Gold Preview regression coverage
2026-08-07 08:50:39 +03:00
Danila Poyarkov eb55cb1025 fix(text): preserve imported FIG glyph outlines
Keep authoritative derived glyphs through layout, alignment, fill, and decoration changes while still invalidating them for actual shaping edits.

Add scene-graph invalidation coverage and a Gold Preview fixture regression test.

Co-authored-by: Joseph Cumines <joeycumines@gmail.com>
2026-08-07 08:50:39 +03:00
Danila Poyarkov 75dd86d0ad fix(fig): improve imported component layout parity
- Preserve Figma min/max constraints, scalar variable bindings, and direct text bounds
- Reflow only authoritative generated instance geometry and pinned descendants
- Avoid double-applying auto spacing and unsafe lone-child normalization
2026-08-03 18:00:15 +03:00
Danila Poyarkov 617482e323 fix(fig): export stretched auto-layout frames
- Translate parent-level cross-axis stretch to Figma child alignment

- Preserve explicit and absolute child behavior during export

- Cover empty frames, encoded fields, and round-trip semantics
2026-07-28 03:10:47 +03:00
Danila Poyarkov 8dfed26d16 fix(fig): preserve paint variable overrides
- Carry paint bindings through symbol overrides and clone synchronization

- Keep explicit instance strokes and resolved variable opacity intact
2026-07-22 15:51:46 +03:00
Danila Poyarkov 7124d0045f fix(cli): populate lazy FIG pages
- Populate requested pages for scoped reads and all pages for document-wide operations

- Export whole FIG documents by default while keeping explicit page exports partial
2026-07-22 14:56:58 +03:00
Danila Poyarkov 072c159fdb fix(fig): preserve independent bound padding
- Treat leading-edge variable bindings as independent padding values

- Serialize trailing padding when raw layout metadata would collapse it
2026-07-21 10:49:54 +03:00
Danila Poyarkov b61957dff9 fix(fig): preserve swapped instance appearance
- Copy component visual and clipping properties during imported swaps

- Keep placed bounds and size-variable bindings authoritative
2026-07-21 10:30:58 +03:00
Danila Poyarkov f6e4dd4c83 fix(fig): preserve derived instance geometry
- Keep placed instance bounds authoritative over root symbol overrides

- Reject size-only positions outside the target parent

- Avoid applying constraint scale twice to derived vector paths
2026-07-21 02:31:44 +03:00
Danila Poyarkov 96de4e5fdb fix(fig): preserve regenerated override lineage
- Remap descendant clone sources when instance branches are repopulated

- Keep same-name component-property siblings distinct

- Name swapped variants after their component sets
2026-07-21 01:51:27 +03:00
Danila Poyarkov 584400e017 fix(fig): preserve variable references on export
- Reuse imported collection, mode, and variable GUIDs when safe

- Keep raw paint asset refs instead of promoting incomplete library bindings

- Retain GUID conversion only for effect aliases required by Kiwi
2026-07-21 01:16:37 +03:00
Danila Poyarkov 74d662d560 fix(fig): target nested instance overrides
- Resolve repeated descendants through source-component paths

- Replay property overrides after component swaps stabilize

- Preserve clone lineage for property-only synchronization
2026-07-21 01:16:17 +03:00
Danila Poyarkov e704343f13 fix(fig): honor node-scoped variable modes
- Import and round-trip per-node variable mode overrides

- Resolve fill and stroke aliases through the nearest mode scope

- Preserve mode maps across cloning and instance synchronization
2026-07-21 00:23:00 +03:00
Danila Poyarkov 86d6a63d0f fix(fig): restore strict parse validation
- Reject corrupt compressed Kiwi chunks instead of accepting raw garbage\n- Tolerate legacy source metadata without editedFields in renderer fallbacks\n- Align font, derived glyph, shared style, renderer mock, and ownership tests with current contracts\n- Restore clean quick and heavy unit quality gates
2026-07-18 03:58:18 +03:00
Danila Poyarkov 8b9be17a56 perf(fig): remove large fixture bottlenecks
- Index component property definitions and variable asset references once per export or override context\n- Skip converged instance fill and text propagation and use native binary hex encoding\n- Add a non-populating parse mode for raw fixture validation\n- Reduce the Material 3 round trip from minutes to about twenty seconds
2026-07-18 03:37:58 +03:00
Danila Poyarkov 02b7261c9e refactor(fig): own raw metadata invalidation
- Track normalized edited fields in SceneGraph without naming Figma payload fields\n- Preserve original raw provenance while filtering stale values through Fig-owned effective readers\n- Rewire export and renderer fallbacks and add package, SceneGraph, and exhaustive round-trip coverage
2026-07-18 02:22:01 +03:00
Danila Poyarkov 2bd2304bcb refactor(fig): own instance interpretation
- Move component properties, symbol overrides, derived symbol data, and clone synchronization into @open-pencil/fig\n- Add the public instance-overrides subpath with package tests and packed smoke coverage\n- Remove cross-package forwarding modules and enforce direct owner imports in architecture checks
2026-07-18 02:06:34 +03:00
Danila Poyarkov a10911c772 refactor(fig): extract NodeChange policy helpers
- Move style references, plugin metadata, text values, and font feature conversion into @open-pencil/fig\n- Add a public node-change subpath with package-local and packed smoke coverage\n- Keep core conversion callers on compatibility exports during the staged migration
2026-07-18 01:32:13 +03:00
Danila Poyarkov f9e2588b84 refactor(fig): own full archive parsing
- Move ZIP canvas and image discovery from Kiwi into @open-pencil/fig\n- Keep Kiwi focused on raw fig-kiwi payload decoding\n- Rewire core and fixture consumers through the public Fig package API
2026-07-18 01:17:21 +03:00
Danila Poyarkov 9ee36efa27 fix(kiwi): preserve unrelated raw metadata
- Invalidate only edited raw Figma fields and dependent geometry or text payloads\n- Prefer untouched component and layout-grid metadata during export\n- Restore exhaustive gold fixture scene parity and refresh coverage baselines
2026-07-18 00:44:49 +03:00
Danila Poyarkov 0bf74a418f feat(text): deepen typography controls
- Add case, justification, vertical alignment, truncation, max-lines, and OpenType controls

- Apply visual text case and measured vertical offsets in CanvasKit rendering

- Preserve nullable preview values, shared text styles, font demand, and .fig metadata

- Add panel, engine, and canvas visual coverage
2026-07-17 22:40:52 +03:00
Danila Poyarkov 23645d5a2d feat(editor): add component property controls
- Model typed component definitions, references, assignments, and preferred swap values

- Add SDK-owned variant, text, boolean, and nested instance-swap controls with mixed-selection undo

- Preserve assignments across variant changes and component synchronization

- Round-trip component property metadata through .fig import and export
2026-07-17 21:59:49 +03:00
Danila Poyarkov c96f87bf9b feat(editor): add shared style bindings
- Model imported fill, stroke, text, effect, and grid style definitions and references

- Add SDK and app selectors with mixed-selection batching, detach-on-edit, and undo restoration

- Preserve style definitions through .fig export and extracted subgraphs

- Document the shared-style composable and update compatibility coverage
2026-07-17 20:28:22 +03:00
Danila Poyarkov 1d8ddd951a feat(editor): add corner smoothing control
- Expose mixed smoothing percentages through Appearance controls
- Preserve per-node corner preview values in one-step undo
- Cover CanvasKit profiles and .fig import/export with engine and visual tests
2026-07-17 19:28:34 +03:00
Danila Poyarkov 2f8d799a5f feat(editor): add stroke geometry controls
- Add mixed-selection cap, join, and miter-limit controls with one-step undo
- Apply node miter limits across CanvasKit stroke paths and vector outline caches
- Preserve imported and edited stroke geometry through Plugin API and .fig roundtrips
2026-07-17 19:01:42 +03:00
Danila Poyarkov 47fe43c3ec feat(text): harden font resolution lifecycle
- Gate ingest rendering until required faces and script fallbacks resolve
- Key CanvasKit artifacts by font registration generation and invalidate affected nodes
- Load character-aware remote subsets with cumulative coverage and exact render aliases
- Use BCP-47 language hints for shaping and CJK fallback order
- Keep baked .fig glyphs as an exhaustion-only fallback
2026-07-17 14:52:35 +03:00
Danila Poyarkov 31e0634d3c test(fig): cover cross-page clone roundtrip 2026-07-04 12:42:06 +03:00
Danila Poyarkov 2c69e0949b fix(text): preserve Figma auto-size text imports
- Normalize auto-layout text imported from Figma when fixed bounds match derived text layout
- Recompute auto-size text bounds on typography metric changes
- Clear stale Figma derived text layout caches when text sizing changes
- Cover FIG import text sizing and line-height resize regressions
2026-07-02 17:30:15 +03:00
Danila Poyarkov c7c7b5da5f fix(fig): use hex image hashes 2026-06-30 10:55:20 +03:00
Danila Poyarkov 898431e646 feat: split SceneGraph and Pen packages 2026-06-30 10:54:32 +03:00
Danila Poyarkov 1eb227ed02 feat(dom-css): harden package boundaries 2026-06-30 10:51:37 +03:00
Danila Poyarkov 1ea6631ffd fix(kiwi): export EXCLUDE booleans as XOR 2026-06-30 10:51:05 +03:00
Danila Poyarkov 6bf930e9e9 refactor(kiwi): tidy direct Kiwi imports 2026-06-30 10:50:34 +03:00
Danila Poyarkov 7d27cac5ab refactor(kiwi): remove redundant core shims 2026-06-30 10:50:30 +03:00
Danila Poyarkov 6877516f06 refactor(kiwi): consume standalone Kiwi package 2026-06-30 10:49:14 +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