Commit graph

482 commits

Author SHA1 Message Date
Danila Poyarkov 4e966c4976 Use byte comparison for fractional index sorting
localeCompare is locale-dependent and produces different orderings
across runtimes (Bun vs browser, Safari vs Chrome). Figma's
fractional indexing uses base-95 ASCII which sorts correctly by
code point.
2026-03-04 10:27:22 +03:00
Danila Poyarkov 707313c3ff Update changelog 2026-03-04 10:17:18 +03:00
Danila Poyarkov 9e3a6c0e7f Extract collectFontKeys into core and add font tests
- Extract font key collection from editor store into
  collectFontKeys() in packages/core/src/fonts.ts
- 18 tests: styleToWeight, weightToStyle, markFontLoaded/isFontLoaded,
  collectFontKeys (nested nodes, style runs, deduplication, default
  font filtering, invalid IDs)
2026-03-04 10:16:26 +03:00
Danila Poyarkov ed85451579 Await font loading before hiding file-open loader
- Make loadFontsForNodes async and await it in openFigFile so the
  loading overlay stays visible until all Google Fonts are ready
- Load fonts when switching pages (previously only loaded for first page)
- Paste still fire-and-forgets font loading for instant feedback
2026-03-04 10:11:12 +03:00
Danila Poyarkov a92315149a Always show visibility toggle on fill, stroke, and effect rows 2026-03-04 09:56:16 +03:00
Danila Poyarkov 1ca96094ab Fix stroke color row overlap
- Remove opacity ScrubInput from color row (matches Figma layout)
- Constrain ColorInput with min-w-0 flex-1 so it doesn't push buttons off
- Add shrink-0 to eye/remove buttons to prevent squishing
2026-03-04 09:54:09 +03:00
Danila Poyarkov e7426f3c58 Fix stroke section layout to match Figma
- Move side selector from header to details row (right of weight input)
- Use 4-dot grid icon for the side selector button (matches Figma)
- Highlight button border with accent color when independent weights active
- Use 3-line stroke weight icon instead of filled rect
- Header now only has section label + add button
2026-03-04 09:51:39 +03:00
Danila Poyarkov 45cab3e5fb Merge branch 'individual-strokes' into compatibility-fixes 2026-03-04 09:49:23 +03:00
Danila Poyarkov c412b78ed8 Add stroke align, individual side strokes, and side selector UI
- Renderer: stroke align (INSIDE/CENTER/OUTSIDE) using clip-based
  approach for all shape types including sections
- Renderer: individual side strokes when independentStrokeWeights is
  enabled, drawing per-side lines with correct align offsets
- StrokeSection: align dropdown, side selector dropdown (All/Top/Bottom/
  Left/Right/Custom), individual weight inputs with side-specific icons
- Kiwi serialize: export correct strokeAlign and border weight fields
- Scene graph: sync border weight props through instance sync
- 19 tests covering align, individual weights, instance sync, kiwi
  export, and FigmaAPI properties
2026-03-04 09:47:14 +03:00
Danila Poyarkov c3e0810620 Fix scrollbar: use utility class instead of global selector
Global * scrollbar styles caused scrollbars to appear on the
menu bar and other elements. Replace with scrollbar-thin utility
applied only to panel scroll containers.
2026-03-04 09:35:30 +03:00
Danila Poyarkov 969158a6c2 Move Google Fonts API key to constants 2026-03-04 09:33:42 +03:00
Danila Poyarkov 630277b896 Add Google Fonts fallback via Developer API
Fetch TTF files from Google Fonts when local and bundled fonts
are unavailable. Uses the Webfonts API for direct TTF URLs per
variant. Font file listings are cached per family; failed lookups
are recorded to avoid retries.
2026-03-04 09:32:45 +03:00
Danila Poyarkov 68cfba3b6d Fix text truncation: use textTruncation field instead of textAutoResize 2026-03-04 09:04:04 +03:00
Danila Poyarkov 8d22415ceb Update changelog with unreleased fixes 2026-03-04 02:33:30 +03:00
Danila Poyarkov 038c876bc6 Fix horizontal scrollbar on pages panel 2026-03-04 02:27:09 +03:00
Danila Poyarkov e17b3f3851 Enable watch feature for tauri-plugin-fs 2026-03-04 02:23:32 +03:00
Danila Poyarkov c40c6c78a8 Fix horizontal scrollbar on right panel, style scrollbars for Tauri 2026-03-04 02:21:06 +03:00
Danila Poyarkov b38d088383 Fix core tsconfig: add ESNext and DOM libs for npm publish 2026-03-04 01:58:37 +03:00
Danila Poyarkov 7ad4116860 Release v0.6.0 2026-03-04 01:47:37 +03:00
Danila Poyarkov 698ffe389a Remove Gatekeeper workaround — app is now code-signed 2026-03-04 01:45:29 +03:00
Danila Poyarkov 630e18a869 Merge branch 'fix-paste-layout' 2026-03-04 01:43:56 +03:00
Danila Poyarkov 6c87cf1a74 Update changelog with remaining paste fixes and infra changes 2026-03-04 01:43:52 +03:00
Danila Poyarkov 0b56cc1e70 Set up Apple code signing and notarization 2026-03-04 01:40:19 +03:00
Danila Poyarkov 101ce6a79e Fix broken MCP and eval-cli tests
MCP test: find_nodes searches currentPage only — switch to
FOUNDATIONS page where Button components live.
eval-cli test: material3.fig first page has 1 TEXT node, not 3 —
assert >0 instead of ==3.
2026-03-04 01:38:26 +03:00
Danila Poyarkov f23acb968f Use Cloudflare R2 for Git LFS storage 2026-03-04 01:25:33 +03:00
Danila Poyarkov 81ef1fbc45 Enable halfLeading for text rendering only, not measurement
Figma distributes leading equally above and below text. CanvasKit
defaults to ascent-biased leading. halfLeading: true fixes this but
must only apply to rendering (drawParagraph), not measureTextNode —
otherwise measured sizes change and break grid layouts.
2026-03-04 01:15:44 +03:00
Danila Poyarkov 8a13c5ffb6 Load all font weight/style variants needed by pasted text nodes
loadFontsForNodes only loaded the Regular style for each family.
Now collects fontWeight and italic from each text node and style
run, converts to style name via weightToStyle, and loads each
variant separately (e.g. Medium, Bold, Bold Italic).
2026-03-04 01:15:44 +03:00
Danila Poyarkov 26ed064b07 Fix InvalidCharacterError when copying nodes with non-ASCII text
btoa() only handles Latin-1. Use TextEncoder + binaryToBase64 for
encoding and TextDecoder + base64ToBinary for decoding the internal
OpenPencil clipboard format.
2026-03-04 01:15:44 +03:00
Danila Poyarkov af9a0ebb5a Fix PERCENT line height conversion — was stored as raw value instead of pixels
Figma's lineHeight with units=PERCENT stores e.g. 100 meaning 100%
of fontSize. We stored the raw value (100) which the renderer
interpreted as 100px, producing ~6x line height. Now properly
converts: PERCENT → value/100 * fontSize, PIXELS → value as-is.
2026-03-04 00:20:43 +03:00
Danila Poyarkov 9c82cba672 Fix font loading not registering in core cache, add layout text measurement tests
Tauri's loadFont registered fonts with the CanvasKit provider but
not in core's loadedFamilies cache, so isFontLoaded returned false
and measureTextNode returned null during layout recomputation.

Add markFontLoaded() to core and call it from Tauri font loader.
Add tests verifying text measurement integrates with auto-layout
centering.
2026-03-04 00:16:15 +03:00
Danila Poyarkov bf83118f96 Recompute layouts after font loading completes 2026-03-04 00:13:24 +03:00
Danila Poyarkov d8c1610d9a Update changelog with clipboard paste fixes 2026-03-04 00:12:33 +03:00
Danila Poyarkov d7e2015967 Estimate text width for WIDTH_AND_HEIGHT auto-resize in layout
Text nodes with textAutoResize=WIDTH_AND_HEIGHT now use an estimated
content width instead of the node's current width when computing
auto-layout. Fixes month headers (and similar centered text in
auto-layout) appearing left-aligned after paste.
2026-03-04 00:11:48 +03:00
Danila Poyarkov 9eaab86733 Apply layout overrides (layoutGrow, textAutoResize) to instance children 2026-03-04 00:04:07 +03:00
Danila Poyarkov 809a1dfe75 Clear hover on zoom/pinch to keep scene picture cache valid 2026-03-03 23:58:46 +03:00
Danila Poyarkov 534a7b298e Add DEFAULT_FONT_FAMILY constant, load fonts on paste and .fig import
Replace all 'Inter' string literals with DEFAULT_FONT_FAMILY from
constants. Add loadFontsForNodes() that collects font families from
text nodes and loads them into CanvasKit, called after clipboard
paste and .fig file import.
2026-03-03 23:56:21 +03:00
Danila Poyarkov da2df1296b Extract shared sortChildren, reduce kiwi-convert export surface 2026-03-03 23:34:02 +03:00
Danila Poyarkov 9044b1b183 Sort auto-layout children by geometric position on import
Figma's parentIndex.position strings represent z-order, not visual
layout order. In auto-layout frames, children must be sorted by their
x (horizontal) or y (vertical) coordinate to match the visual order.
Fixes scrambled calendar dates and other auto-layout child ordering.
2026-03-03 23:31:23 +03:00
Danila Poyarkov 157c776c0c Apply instance symbolOverrides on clipboard paste
Each instance in Figma's clipboard carries symbolData.symbolOverrides
with per-child property changes (text, fills, visibility). The override
targets a child via guidPath which matches the overrideKey field on the
component's child node.

After cloning component children into instances, overrides are resolved
through the overrideKey→figmaId→internalId→clonedChild chain and applied
to the instance's children.
2026-03-03 23:26:29 +03:00
Danila Poyarkov 3eb3a921a0 Detect component sets and skip internal canvas on paste
FRAME nodes with VARIANT componentPropDefs are now promoted to
COMPONENT_SET on import (both .fig files and clipboard paste).

Clipboard paste now respects the Internal Only Canvas: components
on it are used to populate instance children but are not pasted
as visible nodes, matching Figma's behavior.
2026-03-03 23:20:55 +03:00
Danila Poyarkov 3d4f083d5e Populate instance children from component on clipboard paste
When pasting from Figma, INSTANCE nodes reference their component via
symbolData.symbolID but have no children in the clipboard. If the
component was also pasted, clone its children into the instance using
the componentId mapping (cloneChildrenWithMapping).
2026-03-03 21:20:31 +03:00
Danila Poyarkov e737ee5c13 Scale vector paths from normalizedSize to node size on import
Figma vectors use a normalized coordinate space (vectorData.normalizedSize)
that can differ from the node's actual size. The decoded path vertices and
tangents must be scaled to fit the node bounds, otherwise vectors render
at the wrong size (e.g. 16×16 path in a 12×12 node).
2026-03-03 21:19:35 +03:00
Danila Poyarkov 22e9553b30 Fix Figma clipboard paste: broken layout and missing properties
Extract shared kiwi→SceneNode conversion into kiwi-convert.ts, used by
both fig-import.ts (.fig file opening) and clipboard.ts (Figma paste).

The clipboard import was a simplified copy that diverged over time:
- Missing SYMBOL→COMPONENT type mapping (Figma's kiwi name for components)
- textAutoResize hardcoded to NONE instead of using clipboard value
- No gradient/image fill support, no effects, no style runs
- Missing flipX/flipY, constraints, text decorations, arc data, etc.
- Percent letter spacing not converted to pixels

Now both paths use nodeChangeToProps() with identical conversion logic.
2026-03-03 21:19:35 +03:00
Danila Poyarkov 3f4169e32d Add macOS Gatekeeper workaround to README and docs 2026-03-03 21:19:07 +03:00
Danila Poyarkov f8218623ac Update changelog 2026-03-03 20:36:58 +03:00
Danila Poyarkov 7e06969ab5 Multi-selection properties panel with tests
Show position, appearance, fill, stroke, and effects sections
when multiple nodes are selected. Shared values display normally,
differing values show 'Mixed'. Editing applies to all selected.

- Shared composable (use-multi-props) for isMulti, active, activeNode
- ScrubInput supports MIXED symbol as model-value
- Per-node previous values for correct undo on multi-edit
- Fix flip transform encoding (scale first column only)
- Fix npm publish auth for MCP package
- 22 new tests: flip, alignment, multi-node merging, kiwi roundtrip
2026-03-03 20:35:51 +03:00
Danila Poyarkov 5a77f2c1cc Fix flip and single-node alignment
Flip horizontal/vertical now toggles flipX/flipY with scale
transform instead of manipulating rotation. Import detects flip
from negative transform determinant. Single-node alignment uses
parent frame bounds instead of bailing out.
2026-03-03 18:35:30 +03:00
Danila Poyarkov 264bc7fe2a Fix build workflow: only create releases and publish npm from version tags 2026-03-03 18:29:56 +03:00
Danila Poyarkov 2ad76108f0 Fix npm publish: run from repo root with dir argument 2026-03-03 18:13:52 +03:00
Danila Poyarkov 0c3cfcd1cc Update roadmap: .fig compatibility, shaders, component libraries 2026-03-03 18:08:03 +03:00