Commit graph

20 commits

Author SHA1 Message Date
Danila Poyarkov ce55af6171 Fix pasteID int overflow and make error toasts copyable
- Use Int32Array instead of Uint32Array for Kiwi pasteID (signed int field)
- Error toasts: don't auto-dismiss, show copy button, text is selectable
- Add clipboard roundtrip tests (encode → decode → verify)
2026-03-02 18:55:49 +03:00
Danila Poyarkov db7a019385 Fix Figma paste redo + add clipboard import tests
Redo was creating duplicate childIds because snapshots captured after
tree construction had populated childIds, and createNode appends to
parent.childIds again. Fix: clear childIds in redo snapshots.

New tests (7):
- layoutAlignSelf from stackChildAlignSelf
- clipsContent from frameMaskDisabled
- fontWeight from fontName.style via styleToWeight
- letterSpacing object → pixels conversion
- undo removes full subtree
- redo recreates correct parent-child tree
- redo without childIds:[] demonstrates the duplicate bug
2026-03-02 18:05:22 +03:00
Danila Poyarkov 8b1547ece7 Center pasted Figma nodes in viewport instead of using original coordinates 2026-03-02 17:32:06 +03:00
Danila Poyarkov d7ba0bfef9 Skip non-visual node types in clipboard import (variables, widgets, etc.)
Figma clipboard data includes VARIABLE_SET, VARIABLE, and other non-visual
types that were falling through to the default RECTANGLE case in mapNodeType.
These now join DOCUMENT and CANVAS in the skipTypes set.
2026-03-02 17:22:09 +03:00
Danila Poyarkov 4ad90d4819 Add MCP server package with stdio and HTTP transports
29 tools: open_file, save_file, new_document + all 26 from @open-pencil/core.
Stdio for MCP clients (Claude Code, Cursor), HTTP (Hono + Streamable HTTP
with sessions) for scripts, browser extensions, CI.
Runs on both Bun and Node.js (via tsx).
2026-03-02 14:20:49 +03:00
Danila Poyarkov 3849872b32 Unify tool definitions: define once, adapt for AI/CLI/MCP
Move tool logic to @open-pencil/core/tools/schema.ts as framework-agnostic
ToolDef objects. AI adapter generates valibot schemas + Vercel AI tool()
wrappers automatically.

26 tools (was 10): create_shape, render (JSX), set_fill, set_stroke,
set_effects, set_layout, set_constraints, update_node, delete, clone,
rename, reparent, group/ungroup, find_nodes, get_node, get_page_tree,
get_selection, select, list_pages, switch_page, list_variables,
list_collections, create_component, create_instance, eval.

src/ai/tools.ts: 269→28 lines (adapter only).

Tests for all 3 interfaces:
- 26 core tool tests (FigmaAPI directly)
- 11 AI adapter tests (valibot + Vercel AI SDK tool())
- 12 CLI integration tests (eval command on .fig fixture)
323 total, all passing.
2026-03-01 15:02:05 +03:00
Danila Poyarkov f22bd2e133 Merge eval-command: Figma Plugin API + eval CLI command
FigmaAPI wraps SceneGraph with Figma-compatible interface (Symbol-hidden internals).
CLI eval command runs Plugin API scripts headless on .fig files.
106 new tests (60 figma-api + 17 cli integration + 29 gap-filling).
2026-03-01 13:55:39 +03:00
Danila Poyarkov 48219af2fb Fill Figma Plugin API gaps: 29 new tests, 30+ new properties and methods
SceneNode: add minWidth/maxWidth/minHeight/maxHeight, isMask, maskType,
counterAxisAlignContent, itemReverseZIndex, strokesIncludedInLayout,
expanded, textTruncation, autoRename, strokeMiterLimit. All with defaults
and .fig import mapping.

FigmaNodeProxy: add clone(), strokeCap, strokeJoin, strokeMiterLimit,
strokeTopWeight/BottomWeight/LeftWeight/RightWeight, minWidth/maxWidth/
minHeight/maxHeight, isMask, maskType, expanded, textTruncation,
autoRename, insertCharacters, deleteCharacters, primaryAxisSizingMode,
counterAxisSizingMode, counterAxisAlignContent, itemReverseZIndex,
strokesIncludedInLayout, layoutAlign, findAllWithCriteria.

FigmaAPI: add createComponentFromNode, getVariableById, getLocalVariables,
getLocalVariableCollections, getVariableCollectionById.

Fix layoutSizingHorizontal/Vertical for self-framing auto-layout nodes
with no auto-layout parent — now checks the node's own layoutMode.
2026-03-01 13:35:02 +03:00
Danila Poyarkov b6e792846f Harden FigmaAPI: hide internals via symbols, freeze arrays, fix layoutSizing
- Replace _id/_graph/_api with Symbol-keyed properties so eval scripts
  can't access SceneGraph internals directly
- fills/strokes/effects return frozen structuredClone copies
- Fix currentPage getter to define selection only once per proxy
- Fix layoutSizingHorizontal/Vertical: respect parent layout direction
  even when the node itself is an auto-layout frame
- Add 6 new tests: layoutSizing, frozen arrays, internals not exposed
2026-03-01 13:12:26 +03:00
Danila Poyarkov 9a7d170887 Add 17 CLI integration tests for eval command
Tests cover: page name, primitives, findAll queries, node creation,
auto-layout, text, top-level await, stdin piping, JSON output,
syntax/runtime errors, no-code error, undefined output, --output write,
array serialization, getNodeById on real .fig files.
2026-03-01 13:07:06 +03:00
Danila Poyarkov 0cba4fb4b7 Add FigmaAPI + eval CLI command with 60 unit tests
FigmaAPI in @open-pencil/core provides a Figma Plugin API-compatible
interface backed by SceneGraph. Covers node creation, property access,
tree operations, auto-layout, text, traversal, grouping, components,
selection, and serialization.

CLI: bun open-pencil eval <file> --code 'figma.createFrame()'
2026-03-01 12:21:07 +03:00
Danila Poyarkov c59d5fc397 Add Code tab with JSX export and syntax highlighting
- sceneNodeToJsx() in @open-pencil/core: converts SceneNode subtree to JSX
- CodePanel.vue with Prism.js highlighting, line numbers, copy button
- Third tab in properties panel: Design | Code | AI
- 14 new tests covering shapes, text, layout, effects, multi-selection
2026-03-01 12:12:45 +03:00
Danila Poyarkov 13f562adc6 JSX renderer in @open-pencil/core
TreeNode builder functions (Frame, Text, Rectangle, etc.) produce a
lightweight tree structure. Two rendering paths:

- renderTreeNode(): tree → scene graph (browser, no deps)
- renderJsx(): JSX string → esbuild → tree → scene graph (CLI/headless)

Tailwind-like shorthand props: w/h, bg, rounded, flex, gap, p/px/py,
justify, items, shadow, blur, etc.

27 tests covering all node types, layout props, effects, and nesting.
2026-03-01 10:42:06 +03:00
Danila Poyarkov ca422147b4 Add .fig roundtrip tests: real file parsing, property invariants, encode/decode cycle 2026-03-01 10:33:58 +03:00
Danila Poyarkov d30ab4c5d5 Add variables support: collections, modes, bindings, UI panel
Core:
- Variable, VariableCollection types on SceneGraph
- resolveVariable with alias chain resolution + cycle detection
- Mode switching (activeMode per collection)
- Bind/unbind variables to node properties (fills/strokes colors)
- Renderer resolves variable bindings before painting

Import:
- Parse VARIABLE type NodeChanges from .fig files
- Extract paint variable bindings (fills/strokes)

UI:
- VariablesPanel with reka-ui Tabs (collections) + Editable (names/values)
- FillSection: variable picker (reka-ui Popover + Combobox)
- Bound fills show purple variable name badge with detach button
- Toggle variables panel from bottom of layers sidebar

7 new unit tests (83 total)
2026-03-01 01:47:52 +03:00
Danila Poyarkov f77ad708b8 Live component-instance sync with override support
Editing a main component now propagates to all instances:
- Instance children are mapped to component children via componentId
- syncInstances() copies properties from component to instances,
  skipping any property marked in the overrides record
- New children added to a component appear in existing instances
- Store calls syncIfInsideComponent after updateNode, commitMove,
  commitResize — changes propagate automatically
- Demo creates real instances (Button, Badge) in the App Preview

Tests added: instance creation with child mapping, sync propagation,
override preservation, new child addition, detach.
2026-02-28 19:57:52 +03:00
Danila Poyarkov 07f0f559da Add fig-import unit tests for Tier 1 rendering features
23 tests covering: node type mapping (ROUNDED_RECTANGLE, COMPONENT,
INSTANCE, SYMBOL, POLYGON), gradient fills (linear, radial), image
fills with hash, effects (drop shadow, inner shadow), stroke options
(cap, join, dash pattern), text properties (auto-resize, font weight
mapping with space normalization), arc data (partial ellipse, donut),
constraints, blend modes, independent stroke weights, stacked fills.
2026-02-28 16:43:43 +03:00
Danila Poyarkov 27f83c721e Add layout tests, fix cross-axis sizing bugs
- Fix axis mapping for nested frames with different layout directions
  (e.g. vertical child inside horizontal parent had swapped sizing)
- Fix stretch alignment not applying to leaf children when set on parent
- Fix layoutGrow being ignored on children with FIXED primaryAxisSizing
- Add 42 unit tests covering all layout scenarios
2026-02-28 13:59:39 +03:00
Danila Poyarkov 1d79473bc6 Pages support: multi-page documents like Figma
Document structure: Document (root) → Pages (CANVAS) → Nodes.
Each page has its own viewport (pan/zoom) and background color.

- CANVAS node type for pages
- SceneGraph: addPage(), getPages(), getAbsolutePosition stops at CANVAS
- Store: currentPageId, switchPage(), addPage(), deletePage(), renamePage()
- All operations (create, select, paste, hit test, render) scoped to current page
- Per-page viewport state saved/restored on page switch
- Pages list in LayersPanel with add/switch/double-click rename
- .fig import creates proper pages from DOCUMENT→CANVAS hierarchy
- Renderer takes pageId to render correct page's children
- Unit test for pages, updated E2E tests for page-scoped graph
2026-02-28 13:12:27 +03:00
Danila Poyarkov e03a01dbe3 Add bun:test unit tests for SceneGraph (15ms) 2026-02-28 07:45:50 +03:00