Commit graph

75 commits

Author SHA1 Message Date
Danila Poyarkov 45df90c5c7
CSS Grid layout mode (#73)
* Switch to @open-pencil/yoga-layout with CSS Grid support

Use our fork (open-pencil/yoga, grid branch) which cherry-picks the
upstream CSS Grid PRs (#1893–#1898) onto current main. The JS bindings
are ported from the old embind approach to the new wasm_bridge.c API.

npm:@open-pencil/yoga-layout alias keeps all imports as 'yoga-layout'.

Also handle FinalizationRegistry change (upstream #1908) — node.free()
no longer exists, nodes are garbage collected automatically.

* Add CSS Grid layout mode

Scene graph:
- LayoutMode gains 'GRID' option alongside HORIZONTAL/VERTICAL
- GridTrack type (sizing: FIXED/FR/AUTO, value) for track definitions
- GridPosition type (column, row, columnSpan, rowSpan) for children
- New node props: gridTemplateColumns/Rows, gridColumnGap, gridRowGap,
  gridPosition

Layout engine:
- buildGridTree() sets Display.Grid and maps GridTrack[] to Yoga's
  setGridTemplateColumns/Rows API with FR/Points/Auto track types
- Grid children use gridPosition for column/row placement with span
- Flex layout path unchanged

Store:
- setLayoutMode('GRID') auto-creates NxM track grid based on child
  count (sqrt heuristic), defaults to 1fr tracks

UI (LayoutSection.vue):
- Grid button (grid-2x2 icon) added to flow direction row
- Columns/Rows track editors: ScrubInput for value + AppSelect for
  sizing mode (Fill fr / Fixed px / Auto), add/remove buttons
- Separate column gap and row gap ScrubInputs
- Wrap button hidden when grid is active
- Flex alignment grid hidden when grid is active

Kiwi serialization skips GRID mode for now (no .fig codec support).

* Add grid layout integration tests

12 new test scenarios covering:
- Basic 2x2 grid, fixed columns, mixed fr/fixed, unequal fr weights
- Column gap, row gap, both gaps combined
- Padding offsets
- Explicit gridPosition placement, column span, row span
- Absolute children skipped
- Hidden children collapsed
- Nested grid inside flex parent (computeAllLayouts)

Also fix: grid frames as children of flex parents now correctly use
Display.Grid via configureChildAsGrid() instead of falling through
to configureChildAsAutoLayout() which set FlexDirection.

* Deduplicate grid yoga configuration

Extract configureAsGrid() and createGridChildNode() shared by
buildGridTree (root-level grid) and configureChildAsGrid (grid
nested inside flex parent).

* Add grid layout to changelog

* Add grid support to JSX and Tailwind CSS export

OpenPencil format: grid → columns/rows/columnGap/rowGap props,
child colStart/rowStart/colSpan/rowSpan.

Tailwind format: grid grid-cols-N grid-rows-N gap-x-* gap-y-*,
mixed tracks use arbitrary values (grid-cols-[200px_1fr_auto]),
child col-start-*/row-start-*/col-span-*/row-span-*.

Both formats: padding emitted for grid frames (shared with flex),
flex-only props (justify/items/wrap/gap) scoped to isFlex.

* Remove duplicate gridTrackToTw, use shared formatTrack

Also drop unused GridTrackSizing import from LayoutSection.vue.

* Fix grid icon: use layout-grid instead of grid-2x2

unplugin-icons can't resolve icon names with digits after hyphens
(grid-2x2 → grid2x2 which doesn't exist).

* Polish layout UI and fix auto-layout behavior

- Replace text labels with compact icons: ↔/↕ for gap, ☐ for uniform padding, T/R/B/L for per-side padding
- Direction-aware gap icon: ↕ for vertical, ↔ for horizontal
- Pin +/− padding toggle button right of gap input
- Fix alignment grid axes for vertical layout (transpose primary/counter)
- Fix grid switch: set FIXED sizing, compute frame size from children
- Remove hardcoded white fill from Shift+A wrap
- Auto-detect horizontal vs vertical from selection bounds

* Add flex-to-grid switch integration test

Verify HUG frame expands and children are placed in 2x2 grid
when switching from vertical flex to grid layout.

* Update changelog for grid layout
2026-03-08 18:23:53 +03:00
Danila Poyarkov 9882375b4f Release v0.8.0 2026-03-08 00:20:47 +03:00
Danila Poyarkov 01c8753f6e Merge PR #70: E2E test coverage + mutation unit tests
48 new E2E tests across 9 spec files, 26 mutation unit tests,
store/canvas test helpers, data-test-id attributes.

Fixes: explicit error in canvasBounds(), remove any casts,
trim trailing blank line, condense changelog entries.
2026-03-07 23:47:00 +03:00
Danila Poyarkov 6eb206a451 Merge docs PR, fix all lint/typecheck errors, update changelog
- Merge add-vitepress-docs: docs site with 6 locales, SEO, user guide
- Remove VitePress cache files that slipped into git
- Fix all 11 oxlint warnings: replace non-null assertions in
  use-collab.ts with local const captures inside closures
- Update changelog with docs and lint fix entries
2026-03-07 23:19:19 +03:00
Anton A S a40c535a4d Update CHANGELOG for e2e-coverage 2026-03-07 23:13:08 +03:00
Danila Poyarkov 2ec382fc9d Fix RPC bridge review issues: centralize colors, ports, remove non-null assertions
- Use colorToHex/colorDistance from color.ts instead of hand-rolled hex math
  in rpc/commands.ts (removes toHex, hexToRgb, colorDistance duplicates)
- Extract AUTOMATION_HTTP_PORT/AUTOMATION_WS_PORT to core constants, import
  in bridge.ts, server.ts, app-client.ts instead of duplicating magic numbers
- Add requireFile() helper in app-client.ts, replace all file! non-null
  assertions across 12 CLI command files
- Document same-machine trust security model in bridge.ts header comment
- Replace (globalThis as any).Bun with typed isBunRuntime() helper
- Add changelog entry for CLI-to-app RPC bridge feature
2026-03-07 15:11:31 +03:00
Danila Poyarkov 72cbd195ac Trim internal refactor details from changelog 2026-03-07 14:56:35 +03:00
Danila Poyarkov 898932d5d7 Update changelog with reka-vueuse refactor 2026-03-07 14:55:24 +03:00
Danila Poyarkov 19fb22c267 Update changelog with refactor batch 1 2026-03-07 13:48:25 +03:00
Danila Poyarkov 2d58168ce0 Update changelog: add mobile/PWA, layer rename, drawer fix 2026-03-06 18:33:02 +03:00
Danila Poyarkov df8e303498
Add Homebrew tap with auto-update workflow (#61) 2026-03-06 18:08:47 +03:00
Danila Poyarkov 5ae6234e40
Tailwind CSS v4 JSX export (#54)
* Add Tailwind CSS v4 JSX export format

- Add JSXFormat type ('openpencil' | 'tailwind') to sceneNodeToJSX/selectionToJSX
- Tailwind resolver (tailwind.ts): px→spacing (v4 multiplier), hex→color class,
  fontSize/fontWeight/borderRadius named lookups with arbitrary value fallback
- Export: FRAME→div, TEXT→p, SECTION→section, className with TW utility classes
- CLI: export --format jsx --style tailwind
- CodePanel: format toggle button (OpenPencil / Tailwind)
- Rename Jsx→JSX in all public APIs (sceneNodeToJSX, selectionToJSX, renderJSX, etc.)
- 29 new Tailwind export tests, all 43 export tests pass

* Extract escapeJSXText — replace nested ternary with entity map lookup

* Deduplicate export-jsx: extract shared padding, corner radius, and node context helpers

* Update changelog

* Tighten Tailwind JSX export fidelity
2026-03-06 18:04:11 +03:00
Danila Poyarkov a2d7b89709 Adopt motion-v for mobile drawer and toolbar animations
- MobileDrawer: spring-animated height via motion.div :animate + @pan,
  replaces useSwipe + manual rAF. Tab state always set (never null),
  content stays rendered when drawer is closed.
- Toolbar: AnimatePresence with directional slide variants and
  layout-animated width via motion.div layout, replaces manual
  scrollWidth measuring + inline CSS transitions.
- Add SWIPE_VELOCITY_THRESHOLD constant.
- activeRibbonTab type narrowed from union | null to union.
2026-03-06 08:50:38 +03:00
Anton Soldatov 168c25c189
feat: mobile layout, PWA support (#27) (#27)
- Mobile-responsive editor with bottom drawer, ribbon nav, HUD overlay
- Touch support: single-finger tools, two-finger pinch-zoom
- PWA: manifest, service worker, installability
- Extract LayerTree from LayersPanel, shared utils (colorToCSS, initials, toolIcons)
- Constants moved to src/constants.ts, clipboard state to editor store
- reka-ui Popover/DropdownMenu in MobileHud

Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-03-05 19:51:51 +03:00
Danila Poyarkov 661b83e749 Port analyze/diff tools from figma-use, split tools into domain files
Analyze: colors, typography, spacing, clusters
Diff: diff_create (tree diff), diff_show (preview changes)
Utility: get_components, get_current_page, arrange, node_to_component

Split schema.ts (2600 lines) into read, create, modify, structure,
variables, vector, analyze, registry — each under 600 lines.

Clean up inline types (use Color, Vector, SceneNode from existing defs).
Update AGENTS.md and CONTRIBUTING.md with code quality guidelines.
2026-03-05 19:16:23 +03:00
Danila Poyarkov be44b4d404 Release v0.7.0 2026-03-05 10:32:35 +03:00
Danila Poyarkov ed61445e2d
SVG export (#52)
* Add SVG export

Scene graph → SVG serializer with support for rectangles, ellipses,
lines, stars, polygons, vectors (fill/stroke geometry and vector
networks), text with style runs, gradients (linear, radial, angular),
image fills, effects (drop shadow, inner shadow, blur), opacity,
rotation, flips, blend modes, clip paths, and nested groups.

- packages/core: svg-node.ts (minimal XML builder), svg-export.ts
  (serializer), ExportFormat now includes 'SVG', computeContentBounds
  extracted for reuse
- packages/cli: export command accepts --format svg
- UI: SVG added to ExportSection format picker (scale hidden for SVG),
  context menu 'Export as SVG' option
- 47 new tests covering SVGNode builder, geometry blob → path, vector
  network → path, and full node export for all shape types

* Update changelog

* Add export_svg tool to ToolDefs

Available via MCP, AI chat, and CLI eval command.

* Replace standalone export items with Copy/Paste as submenu

Figma-style submenu: Copy as text, Copy as SVG, Copy as PNG (⇧⌘C),
Copy as JSX. Keeps Export as PNG (⇧⌘E) as a separate item.

* Remove standalone Export as PNG from context menu

* Update changelog
2026-03-05 10:31:02 +03:00
Danila Poyarkov 78f249a7c5 Merge remote-tracking branch 'origin/fix-render-jsx-windows'
# Conflicts:
#	CHANGELOG.md
2026-03-05 09:51:26 +03:00
Danila Poyarkov ad96b1c654 Add CJK font fallback for text rendering
Load a system CJK font (PingFang SC, Microsoft YaHei, Noto Sans CJK, etc.)
at startup and pass it as a fallback in CanvasKit's fontFamilies array.
Falls back to Google Fonts (Noto Sans SC) when no system font is available.

Also fixes loadFont to only cache fonts that successfully register with
CanvasKit's TypefaceFontProvider, preventing invalid font data from being
treated as loaded.

Closes #48
2026-03-05 09:48:11 +03:00
Danila Poyarkov 43e0274213 Add changelog entry 2026-03-05 09:20:25 +03:00
Danila Poyarkov d6922caf00 Add ACP agent package with tests 2026-03-05 09:14:54 +03:00
Danila Poyarkov 29096fa56b Merge remote-tracking branch 'origin/master' into reka-color-picker
# Conflicts:
#	src/components/HsvColorArea.vue
2026-03-05 09:05:06 +03:00
Danila Poyarkov 38e7fdd4ac Fix hit-testing: scope hover to current page, click-through empty containers
- Scope hover hit-test to current page (was searching all pages including
  internal component page, causing ghost hover outlines)
- Frames/sections without visible fills or strokes are click-through
- Groups are always click-through (only children are hittable)
- Clipping parents reject hits outside their bounds
- Instances/components check children before falling back to fill check
- Switch test fixtures from material3.fig (55MB) / nuxtui.fig (82MB) to
  gold-preview.fig (537KB) for fast dev runs, gate heavy fixtures behind
  BUN_HEAVY_TESTS env var (enabled in CI)
2026-03-05 08:56:35 +03:00
Danila Poyarkov 82df928b56 s/reka-ui/Reka UI/ in changelog 2026-03-05 08:00:49 +03:00
Danila Poyarkov 5b3f0368e5 Update changelog 2026-03-05 07:59:24 +03:00
Danila Poyarkov 8b05fa6ea4 Update changelog with vector rendering and nested property fixes 2026-03-04 19:30:46 +03:00
Danila Poyarkov 5fa8895ef7 Update changelog with component properties and derivedSymbolData fixes 2026-03-04 18:14:23 +03:00
Danila Poyarkov d087eb9b5d Update changelog with instance override fixes and profiler 2026-03-04 14:22:30 +03:00
Danila Poyarkov 6f605562e5 Update changelog 2026-03-04 10:44:06 +03:00
Danila Poyarkov 707313c3ff Update changelog 2026-03-04 10:17:18 +03:00
Danila Poyarkov 8d22415ceb Update changelog with unreleased fixes 2026-03-04 02:33:30 +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 6c87cf1a74 Update changelog with remaining paste fixes and infra changes 2026-03-04 01:43:52 +03:00
Danila Poyarkov d8c1610d9a Update changelog with clipboard paste fixes 2026-03-04 00:12:33 +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 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 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 9c9bfc38b2 Release v0.5.1 2026-03-03 17:30:09 +03:00
Danila Poyarkov 5eac653f4c Release v0.5.0 2026-03-03 14:51:56 +03:00
Danila Poyarkov 299d9b73b8 Auto-expand layers tree to reveal selected node 2026-03-03 14:48:51 +03:00
Danila Poyarkov 3e054f0885 Resizable pages/layers split in left panel 2026-03-03 14:22:32 +03:00
Danila Poyarkov 93c549bad0 Reuse GL context on panel resize
Panel resizing now creates a new surface from the existing
GrDirectContext instead of destroying and recreating the entire
renderer. All caches (paints, fonts, node pictures, scene picture,
filters) are preserved across resizes.
2026-03-03 12:48:51 +03:00
Danila Poyarkov 921f28e9f9 Show loading overlay on canvas while opening .fig files 2026-03-03 12:35:54 +03:00
Danila Poyarkov e13e910bed Update changelog with zoom smoothness and abspos cache 2026-03-03 11:48:03 +03:00
Danila Poyarkov a32b134eba
Improve zoom smoothness, add absolute position cache (#21)
* Improve trackpad pinch-zoom smoothness

- Replace Math.pow(0.99, delta) with Math.exp(-delta/100) zoom curve
  for natural, symmetric scaling that handles both tiny trackpad deltas
  and large mouse wheel jumps gracefully
- Normalize wheel deltaMode (LINE → 40px, PAGE → 800px) so external
  mice on Firefox produce consistent zoom behavior
- Clamp per-flush scale factor to 0.75–1.25 to prevent jarring jumps
  from discrete mouse wheel ticks

* Add per-frame absolute position cache for SceneGraph
2026-03-03 11:44:31 +03:00
Danila Poyarkov a9a1f9ac1f
Shadow rendering performance: per-node SkPicture cache (#25)
* Replace saveLayer with MaskFilter for drop shadows

Drop shadows now draw directly with MaskFilter.MakeBlur instead of
saveLayer + ImageFilter. Eliminates per-shadow offscreen buffer
allocation, significantly improving render throughput for scenes
with many shadowed nodes.

Text shadows still use saveLayer (MaskFilter can't be applied to
text glyphs individually).

Benchmark: 50 shadow nodes render at 1.55ms/frame (scene change).

* Cache per-node SkPictures for effect rendering

Nodes with visible effects (shadows, blurs) are recorded into
individual SkPictures and replayed on subsequent scene redraws.
Only the modified node re-records; unchanged shadow nodes replay
from cache.

- MaskFilter.MakeBlur for drop shadows instead of saveLayer
- Reusable effectLayerPaint and cached ImageFilters/MaskFilters
- invalidateNodePicture() called on node update
- invalidateAllPictures() on font load; invalidateScenePicture()
  preserves node cache for scene-only invalidation
- Shadow benchmark: 50 nodes at 1.47ms/frame (scene change)

* Update changelog with shadow rendering performance improvements
2026-03-03 11:37:01 +03:00
Danila Poyarkov faa02d8ab0 Update changelog with effects fixes, zoom optimization, and tests 2026-03-03 09:56:41 +03:00
Danila Poyarkov 69afd3311f Update changelog with AppSelect and font picker fixes 2026-03-02 21:33:37 +03:00