2026-03-01 13:09:25 +00:00
# Changelog
2026-04-22 18:11:53 +00:00
## Unreleased
2026-08-03 09:08:58 +00:00
### Breaking changes
2026-07-25 20:52:01 +00:00
2026-08-03 09:08:58 +00:00
- **Core SDK:** Import scene graph types, geometry, coordinate, matrix, snapping, undo, and path helpers from `@open-pencil/scene-graph` ; import `.pen` parsing from `@open-pencil/pen` ; and import synchronous Kiwi decompression from `@open-pencil/kiwi` instead of the `@open-pencil/core` compatibility barrel.
- **Vue SDK:** Replace the removed color-picker model helpers with `useColorModel()` ; replace the deprecated `FillPickerRoot` and `useFillPicker()` APIs with `FillRoot` , `FillSwatch` , `useFill()` , and a consumer-owned popover; rename `FontPickerUi` to `FontPickerUI` ; and remove the exported `testId` prop helper types in favor of semantic component anatomy.
2026-07-25 20:52:01 +00:00
2026-07-20 11:07:11 +00:00
### Added
2026-08-03 09:08:58 +00:00
- Export selections, pages, and documents as editable PowerPoint (`.pptx`) files from the File menu, CLI, and SDK. Text, rectangles, ellipses, and lines remain editable; visually complex layers are embedded as images.
2026-07-20 11:07:11 +00:00
- Import HTML, CSS, Tailwind, and JSX as editable documents from the app, CLI, and SDK, and export standalone browser-ready HTML with compiled CSS and optional external assets.
2026-08-03 09:08:58 +00:00
- Drag image files into the desktop app and paste Figma layers with their remote image fills.
- Import dropped SVG files as editable vector layers, preserving compatible multi-color fills and transparent paints. (#386, #392 , #394 , #446 )
- Convert image layers into editable vectors with Recraft or fal.ai from the canvas context menu. (#322)
- Browse document components as thumbnails or a list in the Assets panel, group them by page, and drag instances directly onto the canvas. (#424)
- Create centered frames from Figma-style device and asset presets, or resize selected frames to a preset while preserving their names. (#418)
- Manage pages by renaming, deleting, and dragging to reorder them in the Pages panel.
- Create text by dragging a fixed-size text box or clicking for auto-width text.
- Create and edit layout grids for frames and components, including columns, rows, counts, gutters, margins, visibility, and grid size.
- Inspect and edit constraints, stroke caps and joins, corner smoothing, shared styles, component properties, blend modes, masks, advanced typography, text resizing, and per-node export settings from the Design panel.
- Edit solid fill colors by entering hex values directly in the Design panel.
2026-07-20 11:07:11 +00:00
- Use Figma-style number-key opacity shortcuts: `1` – `9` set 10%– 90%, `0` sets 100%, and two-digit sequences set exact values.
2026-08-03 09:08:58 +00:00
- Access more common design actions from the browser and desktop menus, including visibility, locking, masks, flipping, components, z-order, distribution, selection, rulers, multiplayer cursors, and Settings.
- Find overlapping layers and overflowing children from the CLI, AI tools, and MCP.
2026-07-20 11:07:11 +00:00
- Target a specific open document and page from live CLI and MCP automation, including sessions with multiple documents.
2026-07-25 19:48:53 +00:00
- Connect local MCP clients through automatically discovered private Unix sockets on macOS and Linux, with localhost TCP fallback. (#338)
2026-08-03 09:08:58 +00:00
- Test OpenAI-compatible provider connections from AI settings with clearer setup errors.
- Configure separate Design, Review, Fast, and Vision models, providers, endpoints, and credentials from AI settings.
- Manage AI, agent, media, and storage credentials from unified Settings, using the system credential store on desktop and encrypted browser storage by default, with a session-only browser option.
- Connect an S3-compatible storage workspace with local-first saves and background synchronization.
- Add Japanese localization and improve menu translations across the existing supported languages. (#367)
- Author richer Design JSX with components, instances, variables, gradients, structured fills, shadows, blur effects, masks, and inline SVG vectors.
- Build custom property panels with new Vue SDK number fields, bindable values, property sections, responsive grids, segmented controls, property lists, color models, fill controls, and gradient primitives.
- Use `useColorModel()` in the Vue SDK for extensible color formats and shared RGB, HSL, HSB, and OkHCL channel behavior.
- Add dedicated SceneGraph, Pen, Kiwi, Fig, and DOM/CSS packages with documented public entry points for building on OpenPencil.
2026-07-20 11:07:11 +00:00
2026-05-31 15:33:56 +00:00
### Changed
2026-08-03 09:08:58 +00:00
- Redesign the editor chrome and Design panel with denser, better-aligned controls, clearer selection and section states, improved menus and overlays, consistent light/dark theming, and better keyboard and screen-reader support.
- Choose Freeform, vertical, horizontal, or grid flow directly from the contextual Layout section, with sizing controls grouped alongside it.
- Choose Auto width, Auto height, or Fixed size directly from the Layout section for text layers.
2026-07-20 11:07:11 +00:00
- Scale the Layers panel to documents with thousands of nodes through virtualized rows, faster incremental updates, stable expansion, range selection, and scroll-to-selection.
2026-08-03 09:08:58 +00:00
- Open and save large `.fig` documents substantially faster while preserving original metadata and user edits, and switch between large pages with fewer Layers panel stalls. (#420)
- Resolve fonts before text appears, with language-aware CJK and Arabic fallbacks, character-specific remote subsets, and more reliable rendering as fonts load.
- Use MiniMax M3 as the default model for MiniMax AI connections. (#431)
- Center empty and setup states consistently across panels, dialogs, and workspaces.
2026-07-20 11:07:11 +00:00
### Fixed
2026-08-06 06:54:43 +00:00
- Preserve Figma’ s imported glyph outlines through layout and appearance updates so text keeps its intended weight and shape.
2026-08-03 20:09:02 +00:00
- Scale proportion-constrained `.fig` instance geometry through fixed wrapper layers so imported logos and icons retain their intended size.
2026-08-03 17:08:56 +00:00
- Match Figma auto-layout spacing, padding, min/max constraints, scalar variable bindings, CanvasKit-shaped generated text, imported text bounds, and nested instance geometry more closely.
2026-08-03 17:17:32 +00:00
- Match Figma Plugin API vector path and network editing, including bounds, transforms, winding rules, region fills, validation, and handle mirroring. (#444)
2026-08-03 09:08:58 +00:00
- Let AI and MCP tools create arbitrary vectors from SVG path data, validating input without leaving blank layers behind. (#440)
- Improve AI design accuracy by exposing every supported shape, including visible stroke colors and weights in visual descriptions, and accepting supported inline SVG attributes without false warnings. (#445, #447 , #448 )
- Restore Anthropic AI connections in the web app instead of failing with a browser endpoint error. (#438)
- Reconnect live CLI and automation sessions automatically after an unexpected bridge disconnection.
- Keep MCP file access inside its configured root when paths contain symlinks, and strengthen local authentication token checks. (#338)
- Start globally installed ACP agents correctly on Windows instead of reporting them as unavailable. (#361)
- Resume pending cloud saves after restarting the app or temporarily losing credentials, without reviving deleted documents or overwriting newer local changes.
- Handle S3 object listings, pagination, and escaped names more reliably, and explain required CORS setup without modifying bucket rules.
- Show HTML, CSS, Tailwind, and JSX import errors instead of failing silently.
- Preserve inline SVG attributes, nested transforms, and explicit icon dimensions when rendering Design JSX artwork.
- Save auto-layout frames that stretch their children to `.fig` without failing. (#427)
- Preserve nested instance text, visibility, paint, geometry, clipping, variable modes, and component swaps in `.fig` files.
2026-07-20 11:07:11 +00:00
- Improve `.fig` import and rendering fidelity for groups, booleans, instances, rotated vectors, complex text fills, auto-sized text, layout grids, page guides, patterns, noise effects, masks, and canvas backgrounds.
- Preserve pages, components, prototype and library metadata, export settings, unsupported effects, and other unrelated Figma data when editing and resaving `.fig` files.
- Prevent duplicate generated IDs from corrupting `.fig` round trips.
2026-08-03 09:08:58 +00:00
- Preserve the whole document when exporting FIG unless a page is explicitly requested, populate unopened pages for file-mode CLI inspection, and expose vector paths and variable modes to Plugin API scripts.
- Report corrupted compressed `.fig` data as an error instead of opening it as valid content.
2026-07-20 11:07:11 +00:00
- Match Figma auto-layout reflow after deleting children, hiding optional instance slots, or syncing component changes.
- Make group and boolean-operation children scale with their parent during resize.
2026-08-03 09:08:58 +00:00
- Edit vectors in opened documents at the correct position, with live fill updates and working undo and redo. (#390)
2026-07-20 11:07:11 +00:00
- Keep duplicated layers independent instead of sharing mutable fills, strokes, bindings, overrides, or vector data, and remove stale bindings when paints are deleted.
2026-08-03 09:08:58 +00:00
- Keep desktop text visible across scene and overlay canvases, refresh it after local fonts load, and preserve rendering when an italic face is unavailable. (#395)
- Vertically center text correctly when an explicit line height adds space above and below its glyphs. (#422)
2026-07-20 11:07:11 +00:00
- Preserve Hangul IME composition while editing text.
2026-08-03 09:08:58 +00:00
- Restore desktop copy, cut, and paste when browser clipboard events are unavailable.
- Finish pasting Figma layers promptly even when remote images are slow or unavailable, and hydrate their image fills when downloads complete.
- Reuse the existing tab when reopening a file through its desktop path or browser file handle, avoiding duplicate watchers and conflicting saves. (#297)
2026-07-20 11:07:11 +00:00
- Share public app links from the desktop collaboration panel and send the current document to newly joined collaborators.
2026-08-03 09:08:58 +00:00
- Show only the most specific tooltip when property controls contain nested actions.
- Match regional browser languages to supported locales without selecting a lower-priority language. (#417)
- Improve Simplified Chinese translations and correct localized menu terminology.
2026-07-20 11:07:11 +00:00
- Resolve published package types correctly for TypeScript consumers and keep file-backed CLI commands working under Node.
2026-05-31 15:33:56 +00:00
2026-07-20 11:07:11 +00:00
### Security
2026-05-31 10:28:41 +00:00
2026-07-20 11:07:11 +00:00
- Update the collaboration WebSocket dependency to address a protocol-length advisory.
2026-05-31 10:28:41 +00:00
2026-05-30 16:14:12 +00:00
## 0.13.2 — 2026-05-30
2026-05-30 10:14:08 +00:00
### Changed
2026-05-30 03:47:32 +00:00
- Update the Homebrew install command to use the published `openpencil` cask.
2026-05-30 10:14:08 +00:00
### Fixes
2026-05-30 16:14:12 +00:00
- Fix the published MCP package so global installs include the `openpencil-mcp` and `openpencil-mcp-http` launchers required by desktop app integrations.
2026-05-30 10:14:08 +00:00
2026-05-29 22:12:58 +00:00
## 0.13.1 — 2026-05-29
2026-05-21 11:57:58 +00:00
### Fixes
2026-05-29 22:12:58 +00:00
- Fix the npm package contents for the CLI so Bun installs include the built `openpencil` binary and runtime bundle.
2026-05-29 21:16:35 +00:00
## 0.13.0 — 2026-05-29
2026-05-21 11:57:58 +00:00
### Fixes
2026-05-29 21:16:35 +00:00
- Fix the published CLI package so Bun global installs run the built `openpencil` binary instead of raw TypeScript sources.
2026-05-21 12:01:33 +00:00
- Greatly improve importing Figma `.fig` files with complex component systems: badges, avatars, icons, links, input fields, lists, date pickers, nested instances, component swaps, and variant properties now open much closer to their original Figma appearance.
- Fix missing or white content in imported `.fig` files caused by unresolved Figma variable bindings, including image/avatar badges, icon colors, text colors, and variable-backed component overrides.
- Preserve more Figma document details when opening and saving `.fig` files, including internal component pages, component ordering, page metadata, canvas backgrounds, text layout, glyph rendering, vector geometry, effects, shadows, and instance overrides.
- Keep user edits after opening an imported `.fig` file: changing size, position, fills, text, or layout now wins over preserved Figma round-trip data when the document is saved again.
- Fix `.fig` exports so files reopened in Figma or OpenPencil keep their pages, components, instances, text wrapping, icons, avatars, and preview thumbnail intact.
- Fix live canvas updates during move/resize/edit previews so visible scene changes repaint immediately.
- Fix accidental duplicate creation when Alt-clicking without dragging.
- Fix MCP startup in the browser.
- Fix CanvasKit loading outside the browser when project paths contain spaces.
2026-05-22 17:59:53 +00:00
- Render imported Figma layer and fill blend modes such as multiply, screen, overlay, difference, hue, saturation, color, and luminosity.
2026-05-22 21:18:33 +00:00
- Render common imported Figma mask stacks so visible layers above alpha, vector, or luminance masks are clipped by the mask shape, including consecutive mask layers.
2026-05-22 20:05:24 +00:00
- Render Figma-style smoothed rectangle corners, including independent corner radii, and effect blend modes from imported Figma files.
2026-05-22 20:44:10 +00:00
- Improve imported tiled image fills by applying Figma image transforms when repeating image patterns.
2026-05-22 21:18:33 +00:00
- Keep imported Figma boolean operations editable as boolean-operation nodes instead of flattening them to vectors.
- Apply imported variable font axes from Figma `fontVariations` when rendering text.
2026-05-30 10:14:08 +00:00
- Render more imported Figma visual metadata, including text decoration styles, leading trim, pattern fills, layout grids, page guides, and deterministic fallbacks for raw noise effects.
2026-05-21 12:01:33 +00:00
### Performance
- Open large `.fig` files faster by deferring work for pages you have not viewed yet while still preparing all needed content before export.
- Improve canvas responsiveness during zooming, panning, dragging, and editing by reusing cached scene backing where safe.
- Speed up `.fig` export for documents with many preserved Figma paint and variable payloads.
2026-05-21 11:57:58 +00:00
2026-05-19 15:10:25 +00:00
## 0.12.2 — 2026-05-19
### Added
2026-05-19 10:59:31 +00:00
- Allow OpenRouter users to enter any model ID from provider settings with cached autocomplete suggestions for tool-capable models, while keeping the curated dropdown as the default when no custom model is set.
2026-05-19 15:10:25 +00:00
### Changed
- Use localized app tooltips instead of native browser titles across editor controls, panels, and menus.
- Update Claude Code MCP setup documentation and the docs landing screenshot.
- Ignore non-source Markdown files in the app dev watcher so documentation edits do not reload the running editor.
### Fixes
- Route Claude Code stdio MCP requests through the live OpenPencil app connection, including immediate disconnected errors when no document is connected.
- Keep MCP disconnected guidance focused on starting OpenPencil and opening a document.
- Improve agent-rendered JSX compatibility with Figma-style text, alignment, and rotation aliases; strip HTML comments; and report unsupported props from render tools.
- Load exact text font styles after MCP and AI tool mutations so newly created bold/weighted text renders immediately.
- Include text style fields in MCP `get_node` output so agents can verify generated text accurately.
- Keep provider settings tooltip/popover composition working in WebKit.
2026-05-19 10:27:24 +00:00
## 0.12.1 — 2026-05-19
2026-05-19 10:02:15 +00:00
### Fixes
2026-05-19 10:27:24 +00:00
- Fix `.fig` round-trips for OpenPencil component sets and variable bindings, and recompute imported layouts after opening documents.
- Report desktop/MCP package version mismatches explicitly and include package-manager-aware install guidance from the MCP server.
- Support scoped MCP `save_file({ path })` workflows while keeping file saving in the desktop app.
- Use native Tauri path handling for save parent directories so Unicode and Windows paths are handled correctly.
2026-05-19 10:02:15 +00:00
- Fix the web font picker so Google Fonts remain available in Safari, local font access is requested on first open when supported, font sources are labeled, and Google font previews load lazily for visible rows.
2026-05-19 10:18:47 +00:00
- Fix background blur rendering so it blurs the backdrop behind a layer instead of applying a no-op content filter, and keep effect parameter controls visible in the properties panel.
2026-05-19 10:02:15 +00:00
2026-05-18 18:06:06 +00:00
## 0.12.0 — 2026-05-18
2026-05-06 12:38:45 +00:00
### Added
2026-05-16 12:42:18 +00:00
- Assets panel — browse, search, and insert document components directly from the left sidebar.
- Component variants — switch instance variants from the right inspector; default variant respects property definitions.
- Figma library metadata — component keys, source libraries, version IDs, descriptions, and docs links are preserved on import/export.
- Desktop file associations — double-click `.fig` or `.pen` files in Finder/Explorer to open them in OpenPencil.
- Auto-update — startup update checks and a Check for Updates menu item on desktop.
- Light theme with theme-aware canvas rulers.
- PDF export — available in the export panel, CLI (`--format pdf`), and MCP.
- SVG import tool for automation workflows.
- DeepSeek AI provider.
- Variable modes — create, rename, duplicate, delete, and set defaults per collection.
- Variable binding controls for fills, strokes, sizing, min/max, and typography fields.
- Auto-layout inspector controls for min/max dimensions, auto gap, wrap gap, and two-axis padding.
- Stroke dash/gap controls.
- Font settings — local font access, fallback predownloads, and downloaded font cache management.
2026-05-17 19:20:55 +00:00
- Editor commands for frame selection, paste to replace, Boolean operations, flatten, outline text, and outline stroke.
- Boolean operations panel control and canvas context-menu entries for flattening and outlining supported selections.
2026-05-06 12:38:45 +00:00
Refactor architecture boundaries across core, app, and packages (#234)
* refactor(core): decompose editor factory and action modules
Split the monolithic editor factory and large action modules into focused
domain helpers:
- create.ts assembles context through bridge modules (clipboard,
components, structure, undo) and delegates to graph-reads, graph-events,
layout-runner, component-sync, and state factory
- structure.ts delegates to group, container-wrap, auto-layout-wrap,
reorder, and state-toggle helpers
- selection.ts delegates to hit-test, overlays, container navigation,
and read helpers
- clipboard.ts delegates to subtree-history, images, export, copy,
fonts, and placement helpers
- shapes.ts delegates to pen actions and section-adopt
- components.ts delegates to focus and instances helpers
- alignment.ts delegates to flip-rotate helper
- text.ts uses explicit TextEditSession for snapshot comparison
New focused modules: nudge, variable-bindings, layout-mode,
page-viewports, tool-registry, color-space
Undo: history/position and history/snapshot helpers, hardened
batch/rollback with nested batch support and configurable limit
* refactor(core): split tool definitions by domain
Split the monolithic tool registry into domain-specific modules:
- read/ — selection, find, pages, fonts, components, nodes, query, jsx
- create/ — basic shapes, components, vector, JSX render
- modify/ — paint, effects, geometry, layout, state, text, update
- structure/ — basic, arrange, batch, hierarchy, replace, tree
- variables/ — bindings, collections, read, values
- vector/ — boolean, path, export, viewport
- analyze/ — colors, typography, spacing, clusters, diff, eval
- describe/ — summaries, tree, roles, layout-issues
- stock-photo/ — providers, requests, apply
- codegen/ — component-map, tokens
Split registry into core/extended tiers; refine schema and AI adapter
* refactor(core): restructure kiwi codec and instance overrides
Reorganize the Kiwi .fig codec into domain subdirectories:
- binary/ — codec, schema, protocol
- fig/ — file, import, parse (core, worker, transfer)
- node-change/ — convert, export-node, serialize, plugin-data
- instance-overrides/ — constraints, dsd, populate, props, resolve,
symbol-overrides, symbol-props, sync, types
Vendored kiwi-schema/ left isolated
* refactor(core): split profiler, icons, IO, and add subpath exports
Profiler: speedscope-export, capture-session, hud-controller
Icons: api, svg, types, render, create-icons tool
IO: format registry and subpath exports
Canvas/color/text/vector: targeted cleanup
Add deliberate subpath exports: random, xpath, vector, color, canvas,
scene-graph, kiwi, design-jsx, io, tools, editor, layout, canvaskit,
profiler, text, lint, rpc, figma-api, constants
* refactor(vue): decompose canvas input, surface lifecycle, and controls
Canvas surface: gl-surface, kit-loader, render-loop, resize-observer
Canvas input handlers:
- move: drop-target, move-snap, duplicate-drag
- select: select-move, select-hover, select-hit
- resize: resize-rect, resize-vector, resize-start
- transform: rotation, marquee, pan, text-selection
- text-edit: navigation, clipboard, textarea lifecycle
- Shared: click-count, space-key, pan, pan-zoom, draw, raf-scheduler
Editor composition:
- commands split: actions, context, metadata, edit, selection, view
- menu-model split: command-groups, builders, types
- Gradient stop composable reuse in primitive root
Controls: fill, layout, typography, appearance, effects, stroke,
okhcl, prop-scrub, node-props, undo-batch, color-variable-binding
Variables/i18n/document/export helpers
Organize canvas, primitives, controls, editor, and variables into
cohesive module directories with package-local import aliases
Expose MenuActionNode/MenuSeparatorNode from public API
* refactor(app): split document IO, editor session, and automation bridge
Document IO: source-state, naming, writer, reload-source, reload-state,
imported-document, watch-targets, save-targets
Editor session: create, modules, types, accessors, computed, refs
Editor canvas: loader-overlay, collaboration-awareness,
context-selection, menu-actions, menu-model
Automation bridge: eval, tools, exports, files, selection, RPC fallback
AI/ACP: transport, map-update, permission, debug, chat effects/storage
Collab: awareness, graph-bindings, yjs-sync, follow, session, types
Shell keyboard: actions, bindings, clipboard, focus, nudging,
raw-events, registry, reserved, shortcuts, space-tool
Shell menu: app-menu, document-name, entry, files
Demo: colors, effects, helpers, section builders (components,
app-preview, effects, standalone, variables) — document.ts reduced
from 981 to 32 lines as pure orchestrator
Move app modules under src/app/ with organized domain structure:
editor, document, ai, collab, shell, automation, demo, tabs
* refactor(app): decompose UI components with provide/inject context
Split monolithic components using Reka UI-inspired namespace folders
with scoped provide/inject context — no prop drilling:
- CollabPanel/ — context, avatars, share, connected, join
- ColorPickerPanel/ — context, area, format, field groups, sliders
- MobileHud/ — context, action toast, tool badge, file menu, presence
- ProviderSettings/ — context, API key/type, endpoint, tokens, photos
- Toolbar/ — actions, types, desktop, mobile, tool button, flyout
- LayoutSection/ — types, auto-layout, flex, grid, padding, size, clip
Properties helpers: fill-okhcl adapter, fill-label, color-style-row
Menu: entry helpers, document-name rename, stale type removal
* refactor(mcp): split server into focused modules
- browser-rpc — WebSocket client management
- mcp-sessions — session lifecycle
- tool-output — response formatting
- tool-schema — Zod schema generation from ToolDefs
- jsx-preprocess — JSX source transformation
- result — result helpers
- tool-registration — MCP tool wiring
- auth — API key validation
- http-options — CORS/request handling
- stdio-bridge — stdio transport adapter
* refactor(cli): split analyze subcommands and shared helpers
- Analyze subcommands: clusters, colors, spacing, typography
- RPC data loading helper
- Migrate imports to targeted core subpath exports
* refactor(docs): split VitePress config and shared table component
Config helpers: sdk-sidebar, seo, labels, sidebars, locale-theme,
root-theme, locales
Shared SdkDataTable component replaces duplicated table markup in
SdkPropsTable, SdkEventsTable, and SdkSlotsTable
Update contributing and testing docs
* refactor(tauri): decompose desktop entrypoint
Split lib.rs into focused service modules:
- fig_container.rs — .fig archive/compression commands
- fonts.rs — font cache and system font enumeration
- menu.rs — native menu construction
- menu_events.rs — menu event dispatch and devtools toggle
- window.rs — main window show/focus lifecycle
* test: share domain test factories and migrate fixtures
New shared helpers:
- tests/helpers/scene.ts — makeSceneGraph factory
- tests/helpers/vector-network.ts — vertex/segment/network builders
- tests/helpers/fig-traversal.ts — all-node collection, type counts
- tests/helpers/undo.ts — undo test utilities
- tests/helpers/editor-history.ts — editor history test helpers
Migrate render, vector, fig-roundtrip, and undo tests to use shared
factories instead of inline fixture construction
* build: add structural lint rules, split vite config, update docs
Structural lint (oxlint.structure.json + lint/plugin.js):
- 20+ custom rules enforcing package boundaries, lifecycle patterns,
naming conventions, and import discipline
Vite config split: raw-markdown, canvaskit-assets, pwa, server,
aliases, automation plugins
Remove legacy shims and utils superseded by SDK/core modules
Update AGENTS.md, CONTRIBUTING.md, eval-command docs, tsconfig
* fix(vue): normalize canvas directory casing and remove duplicate export
- Rename Canvas/ to canvas/ in git index to match #vue/canvas/* imports
(PascalCase was correct for component primitives but canvas/ is a
non-component domain directory)
- Remove duplicate ./random subpath export in core package.json
* fix: add #vue and #core Vite resolve aliases for dev server
* refactor(core): reduce remaining large modules
Split the remaining large core hotspots into cohesive domain modules while preserving public facades and behavior.
- Extract scene graph types, variables, node defaults, and vector-network helpers
- Decompose canvas renderer orchestration, state, paints, colors, lifecycle, labels, and delegated domain methods into renderer/ and labels/ subfolders
- Split Kiwi node-change, binary variable binding, layout, RPC, vector, JSX export, clipboard, design JSX, and Figma proxy helpers
- Replace collision-driven *Fn import aliases with namespace imports and enforce the pattern in lint
Validation:
- bun run check
- bun --filter @open-pencil/vue build
- bun run test:dupes
* fix(app): forward color input attrs
* fix(app): cover section drawing errors
* fix(editor): undo option-drag duplicates
* docs: document domain subfolder convention
* fix(app): handle undo redo on keydown
* refactor(app): dispatch shortcuts from keydown
* refactor: group prefixed domain modules
* refactor(app): use tinykeys for shortcuts
* refactor(core): group symbol override modules
* refactor(core): group fig kiwi container helper
* refactor(canvas): split overlay rendering modules
* refactor(vue): remove unused internal barrels
* fix(app): lay out demo components before instancing
* fix(app): restore demo badge spacing
* perf(canvas): split scene and overlay rendering
* refactor(vue): wrap wheel gesture lifecycle
* fix(canvas): wait for fonts before hiding loader
* docs: update unreleased changelog
2026-04-30 12:14:19 +00:00
### Changed
2026-05-16 12:42:18 +00:00
- Smaller domain modules across core, app, Vue SDK, CLI, MCP, docs, and desktop with enforced package boundaries.
- Separate scene and overlay canvas layers — rulers, labels, and selections no longer cause scene redraws.
- Shared menu schema between browser and native Tauri menus.
2026-05-17 19:20:55 +00:00
- Editor command metadata now drives shortcut display across browser menus, native menus, tooltips, and context menus.
- Text-to-vector conversion now uses shared loaded-font outline geometry across Boolean, flatten, and outline commands.
Refactor architecture boundaries across core, app, and packages (#234)
* refactor(core): decompose editor factory and action modules
Split the monolithic editor factory and large action modules into focused
domain helpers:
- create.ts assembles context through bridge modules (clipboard,
components, structure, undo) and delegates to graph-reads, graph-events,
layout-runner, component-sync, and state factory
- structure.ts delegates to group, container-wrap, auto-layout-wrap,
reorder, and state-toggle helpers
- selection.ts delegates to hit-test, overlays, container navigation,
and read helpers
- clipboard.ts delegates to subtree-history, images, export, copy,
fonts, and placement helpers
- shapes.ts delegates to pen actions and section-adopt
- components.ts delegates to focus and instances helpers
- alignment.ts delegates to flip-rotate helper
- text.ts uses explicit TextEditSession for snapshot comparison
New focused modules: nudge, variable-bindings, layout-mode,
page-viewports, tool-registry, color-space
Undo: history/position and history/snapshot helpers, hardened
batch/rollback with nested batch support and configurable limit
* refactor(core): split tool definitions by domain
Split the monolithic tool registry into domain-specific modules:
- read/ — selection, find, pages, fonts, components, nodes, query, jsx
- create/ — basic shapes, components, vector, JSX render
- modify/ — paint, effects, geometry, layout, state, text, update
- structure/ — basic, arrange, batch, hierarchy, replace, tree
- variables/ — bindings, collections, read, values
- vector/ — boolean, path, export, viewport
- analyze/ — colors, typography, spacing, clusters, diff, eval
- describe/ — summaries, tree, roles, layout-issues
- stock-photo/ — providers, requests, apply
- codegen/ — component-map, tokens
Split registry into core/extended tiers; refine schema and AI adapter
* refactor(core): restructure kiwi codec and instance overrides
Reorganize the Kiwi .fig codec into domain subdirectories:
- binary/ — codec, schema, protocol
- fig/ — file, import, parse (core, worker, transfer)
- node-change/ — convert, export-node, serialize, plugin-data
- instance-overrides/ — constraints, dsd, populate, props, resolve,
symbol-overrides, symbol-props, sync, types
Vendored kiwi-schema/ left isolated
* refactor(core): split profiler, icons, IO, and add subpath exports
Profiler: speedscope-export, capture-session, hud-controller
Icons: api, svg, types, render, create-icons tool
IO: format registry and subpath exports
Canvas/color/text/vector: targeted cleanup
Add deliberate subpath exports: random, xpath, vector, color, canvas,
scene-graph, kiwi, design-jsx, io, tools, editor, layout, canvaskit,
profiler, text, lint, rpc, figma-api, constants
* refactor(vue): decompose canvas input, surface lifecycle, and controls
Canvas surface: gl-surface, kit-loader, render-loop, resize-observer
Canvas input handlers:
- move: drop-target, move-snap, duplicate-drag
- select: select-move, select-hover, select-hit
- resize: resize-rect, resize-vector, resize-start
- transform: rotation, marquee, pan, text-selection
- text-edit: navigation, clipboard, textarea lifecycle
- Shared: click-count, space-key, pan, pan-zoom, draw, raf-scheduler
Editor composition:
- commands split: actions, context, metadata, edit, selection, view
- menu-model split: command-groups, builders, types
- Gradient stop composable reuse in primitive root
Controls: fill, layout, typography, appearance, effects, stroke,
okhcl, prop-scrub, node-props, undo-batch, color-variable-binding
Variables/i18n/document/export helpers
Organize canvas, primitives, controls, editor, and variables into
cohesive module directories with package-local import aliases
Expose MenuActionNode/MenuSeparatorNode from public API
* refactor(app): split document IO, editor session, and automation bridge
Document IO: source-state, naming, writer, reload-source, reload-state,
imported-document, watch-targets, save-targets
Editor session: create, modules, types, accessors, computed, refs
Editor canvas: loader-overlay, collaboration-awareness,
context-selection, menu-actions, menu-model
Automation bridge: eval, tools, exports, files, selection, RPC fallback
AI/ACP: transport, map-update, permission, debug, chat effects/storage
Collab: awareness, graph-bindings, yjs-sync, follow, session, types
Shell keyboard: actions, bindings, clipboard, focus, nudging,
raw-events, registry, reserved, shortcuts, space-tool
Shell menu: app-menu, document-name, entry, files
Demo: colors, effects, helpers, section builders (components,
app-preview, effects, standalone, variables) — document.ts reduced
from 981 to 32 lines as pure orchestrator
Move app modules under src/app/ with organized domain structure:
editor, document, ai, collab, shell, automation, demo, tabs
* refactor(app): decompose UI components with provide/inject context
Split monolithic components using Reka UI-inspired namespace folders
with scoped provide/inject context — no prop drilling:
- CollabPanel/ — context, avatars, share, connected, join
- ColorPickerPanel/ — context, area, format, field groups, sliders
- MobileHud/ — context, action toast, tool badge, file menu, presence
- ProviderSettings/ — context, API key/type, endpoint, tokens, photos
- Toolbar/ — actions, types, desktop, mobile, tool button, flyout
- LayoutSection/ — types, auto-layout, flex, grid, padding, size, clip
Properties helpers: fill-okhcl adapter, fill-label, color-style-row
Menu: entry helpers, document-name rename, stale type removal
* refactor(mcp): split server into focused modules
- browser-rpc — WebSocket client management
- mcp-sessions — session lifecycle
- tool-output — response formatting
- tool-schema — Zod schema generation from ToolDefs
- jsx-preprocess — JSX source transformation
- result — result helpers
- tool-registration — MCP tool wiring
- auth — API key validation
- http-options — CORS/request handling
- stdio-bridge — stdio transport adapter
* refactor(cli): split analyze subcommands and shared helpers
- Analyze subcommands: clusters, colors, spacing, typography
- RPC data loading helper
- Migrate imports to targeted core subpath exports
* refactor(docs): split VitePress config and shared table component
Config helpers: sdk-sidebar, seo, labels, sidebars, locale-theme,
root-theme, locales
Shared SdkDataTable component replaces duplicated table markup in
SdkPropsTable, SdkEventsTable, and SdkSlotsTable
Update contributing and testing docs
* refactor(tauri): decompose desktop entrypoint
Split lib.rs into focused service modules:
- fig_container.rs — .fig archive/compression commands
- fonts.rs — font cache and system font enumeration
- menu.rs — native menu construction
- menu_events.rs — menu event dispatch and devtools toggle
- window.rs — main window show/focus lifecycle
* test: share domain test factories and migrate fixtures
New shared helpers:
- tests/helpers/scene.ts — makeSceneGraph factory
- tests/helpers/vector-network.ts — vertex/segment/network builders
- tests/helpers/fig-traversal.ts — all-node collection, type counts
- tests/helpers/undo.ts — undo test utilities
- tests/helpers/editor-history.ts — editor history test helpers
Migrate render, vector, fig-roundtrip, and undo tests to use shared
factories instead of inline fixture construction
* build: add structural lint rules, split vite config, update docs
Structural lint (oxlint.structure.json + lint/plugin.js):
- 20+ custom rules enforcing package boundaries, lifecycle patterns,
naming conventions, and import discipline
Vite config split: raw-markdown, canvaskit-assets, pwa, server,
aliases, automation plugins
Remove legacy shims and utils superseded by SDK/core modules
Update AGENTS.md, CONTRIBUTING.md, eval-command docs, tsconfig
* fix(vue): normalize canvas directory casing and remove duplicate export
- Rename Canvas/ to canvas/ in git index to match #vue/canvas/* imports
(PascalCase was correct for component primitives but canvas/ is a
non-component domain directory)
- Remove duplicate ./random subpath export in core package.json
* fix: add #vue and #core Vite resolve aliases for dev server
* refactor(core): reduce remaining large modules
Split the remaining large core hotspots into cohesive domain modules while preserving public facades and behavior.
- Extract scene graph types, variables, node defaults, and vector-network helpers
- Decompose canvas renderer orchestration, state, paints, colors, lifecycle, labels, and delegated domain methods into renderer/ and labels/ subfolders
- Split Kiwi node-change, binary variable binding, layout, RPC, vector, JSX export, clipboard, design JSX, and Figma proxy helpers
- Replace collision-driven *Fn import aliases with namespace imports and enforce the pattern in lint
Validation:
- bun run check
- bun --filter @open-pencil/vue build
- bun run test:dupes
* fix(app): forward color input attrs
* fix(app): cover section drawing errors
* fix(editor): undo option-drag duplicates
* docs: document domain subfolder convention
* fix(app): handle undo redo on keydown
* refactor(app): dispatch shortcuts from keydown
* refactor: group prefixed domain modules
* refactor(app): use tinykeys for shortcuts
* refactor(core): group symbol override modules
* refactor(core): group fig kiwi container helper
* refactor(canvas): split overlay rendering modules
* refactor(vue): remove unused internal barrels
* fix(app): lay out demo components before instancing
* fix(app): restore demo badge spacing
* perf(canvas): split scene and overlay rendering
* refactor(vue): wrap wheel gesture lifecycle
* fix(canvas): wait for fonts before hiding loader
* docs: update unreleased changelog
2026-04-30 12:14:19 +00:00
2026-04-22 18:11:53 +00:00
### Fixes
2026-05-16 12:42:18 +00:00
- Fix `.fig` export of component variant properties and text stretch alignment so designs round-trip correctly through Figma.
- Fix CJK and Arabic text rendering — fallback fonts now load before the first paint instead of showing blank text.
- Fix large `.fig` files freezing on open — parsing runs in a worker, and the viewport fits to content after loading.
- Improve Figma import fidelity — variable aliases, nested instances, avatar swaps, badge internals, and input text alignment are preserved.
- Improve Figma export fidelity — flipped vectors, stroke geometry, visual overflow, and stroked-shape drop shadows are preserved.
- Fix variant switching so instances update their contents, not just the component reference.
- Fix text editing inside components and instances on double-click.
- Fix paste into selected containers and entered frames.
- Fix clipboard parsing to safely ignore invalid data.
- Fix undo/redo for duplicate, state restore, and modifier-key release.
- Prevent browser from intercepting app-level undo/redo shortcuts.
- Fix font loading and bundled font resolution.
- Show the startup loader until fonts load and the first render completes.
- Improve light theme polish and canvas ruler colors.
- Normalize browser zoom speed.
- Fix variable picker popovers and color binding swatches.
- Fix dashed strokes on vector nodes and gradient fills on text.
- Fix inner shadow rendering on text nodes.
2026-05-18 17:30:10 +00:00
- Fix imported Figma-derived underlined text rendering.
2026-05-16 12:42:18 +00:00
- Fix exponential `.fig` file growth on repeated save/load cycles.
2026-05-18 17:30:10 +00:00
- Fix opening large `.fig` files so every page populates component instances, preventing missing nested content when switching pages.
2026-05-16 12:42:18 +00:00
- Fix canvas size badges scaling with zoom.
2026-05-16 12:42:52 +00:00
- Fix layout inspector dropdown anchoring and spacing/padding icon clarity.
- Fix section drawing and color input forwarding in the property panel.
- Fix asset insertion coordinates inside entered containers.
- Fix MCP stdio handshake and eval return values.
- Fix `@open-pencil/vue` npm imports referencing an unexported core subpath.
2026-05-17 19:20:55 +00:00
- Fix Figma clipboard text compatibility — pasted OpenPencil text keeps editable fixed bounds, line wrapping, baselines, glyph offsets, and Figma edit-mode layout.
- Fix local font matching so requested upright and weighted faces do not fall back to italic or regular faces.
- Fix CanvasKit paragraph rendering to preserve requested text weights and slants.
- Fix nested text editing interactions — drill double-click enters nested text edit mode, and clicking another text node switches edit targets while editing.
- Fix auto-height text edit commits so text bounds and undo state stay in sync.
- Fix Boolean, flatten, and outline operations to reject unsupported image/complex-script sources safely instead of silently dropping geometry.
- Fix outline stroke enablement for stroked descendants inside groups and containers.
2026-04-25 19:52:25 +00:00
### Performance
2026-05-16 12:42:18 +00:00
- Event-driven canvas rendering — scene and overlay layers only repaint when their inputs change, replacing continuous polling.
- Shared RAF scheduler coalesces scene and overlay frames into a single animation frame per editor.
- Font-family fallback arrays and downloaded remote fonts are cached to avoid repeated work.
- WebGL draw-call instrumentation only runs while the profiler is active.
- Instance override resolution is cached and `.fig` pages load lazily for large files.
- Live drag/resize uses repaint-only previews to skip layout during interaction.
2026-04-22 18:11:53 +00:00
2026-04-23 13:15:37 +00:00
## 0.11.8 — 2026-04-23
### Fixes
- Fix MCP server not spawning on Windows — use `cmd /c` to resolve `.cmd` wrappers from npm global installs
- Fix MCP server and automation WebSocket not connecting on Windows/Linux — inline `__TAURI_INTERNALS__` check at call time instead of using stale module-level `IS_TAURI` constant
- Fix shell PATH not inherited by GUI app on macOS/Linux — add `fix-path-env-rs` to read shell config
2026-04-22 17:31:33 +00:00
## 0.11.7 — 2026-04-22
2026-03-16 10:27:02 +00:00
2026-04-13 07:11:23 +00:00
### Features
- Add stdio transport for MCP server — `openpencil-mcp` now works as a proper stdio MCP server for Claude Code, Cursor, etc. HTTP server available as `openpencil-mcp-http` .
2026-04-22 10:48:39 +00:00
- Default canvas background to dark when system prefers dark color scheme
- Add `list_available_fonts` MCP tool for font discovery
- Copy node ID / XPath from context menu; CLI selection command
- Arrow key nudge for selected nodes (1px, Shift+arrow for 10px)
2026-04-22 12:53:07 +00:00
- JSX renderer: `position="absolute"` , `top` , `left` props for absolute children inside auto-layout containers
- MCP server sends `notifications/tools/list_changed` when the desktop app connects or disconnects
- Headless text measurement via opentype.js per-glyph advance widths — no CanvasKit needed
2026-04-22 14:53:32 +00:00
- Add `open_file` and `new_document` MCP tools with `OPENPENCIL_MCP_ROOT` path scoping
- Optional `path` param on `export_image` , `export_svg` , `get_jsx` — write output to disk instead of returning base64/string
- Multi-root JSX support — multiple top-level elements auto-wrapped in a fragment
- `Component` and `Instance` tag aliases in JSX renderer
- JSX prop reference doc — copy to clipboard via book icon in Code panel
- Prompts (`CODEGEN_PROMPT`, `JSX_REFERENCE` ) moved from embedded strings to markdown files
2026-04-13 07:11:23 +00:00
fix(vue): batch color picker drag updates into one undo entry (#187)
PropertyListRoot's update() and patch() pushed a new undo entry on
every call, so dragging a color slider or color area in the fill,
stroke, or effect pickers filled the undo stack with dozens of
intermediate entries per interaction. Cmd+Z had to be pressed many
times to revert a single conceptual color change.
Wrap update() and patch() in a debounced begin/commit batch keyed by
(op, propKey, index, nodeIds). Rapid events collapse into one undo
entry, and the batch commits after 300ms of idle. Changing prop key,
index, or selection flushes the previous batch immediately, and
add/remove/toggleVisibility flush any pending batch before running so
button actions never mix with drag history. The existing explicit
batch in toggleVisibility for multi-node is preserved.
2026-04-13 06:45:50 +00:00
### Fixes
2026-04-22 10:48:39 +00:00
- Fix Backspace not deleting selected nodes after clicking on canvas — canvas now receives focus on click so keyboard shortcuts aren't blocked by stale input focus
- Support Cmd/Ctrl+click for additive multi-select in layers panel (previously only Shift+click worked)
- Fix macOS Tauri build — move `NSAllowsLocalNetworking` ATS config from invalid `tauri.conf.json` property to a proper `Info.plist` file
- Fix tab order and keyboard handling in inspector panel
- Fix design token variables not resolved before passing to yoga-layout
- Suppress keyboard shortcuts while editing property panel inputs
- Fix tooltip competing with popover trigger on Windows
- Fix hit area for nodes with rotated parents
- Error toasts auto-dismiss, deduplicate, and cap stack at 5
- Bump yoga-layout to 3.3.0-grid.3 with `Node.free()` support
- Bump PWA precache limit for canvaskit-webgpu
fix(vue): batch color picker drag updates into one undo entry (#187)
PropertyListRoot's update() and patch() pushed a new undo entry on
every call, so dragging a color slider or color area in the fill,
stroke, or effect pickers filled the undo stack with dozens of
intermediate entries per interaction. Cmd+Z had to be pressed many
times to revert a single conceptual color change.
Wrap update() and patch() in a debounced begin/commit batch keyed by
(op, propKey, index, nodeIds). Rapid events collapse into one undo
entry, and the batch commits after 300ms of idle. Changing prop key,
index, or selection flushes the previous batch immediately, and
add/remove/toggleVisibility flush any pending batch before running so
button actions never mix with drag history. The existing explicit
batch in toggleVisibility for multi-node is preserved.
2026-04-13 06:45:50 +00:00
- Fix color picker dragging flooding the undo stack — fill/stroke/effect color and opacity drags now collapse into a single undo entry per interaction via debounced batching in `PropertyListRoot`
2026-04-13 07:11:23 +00:00
- Fix .fig import crash on alias variables without a GUID
2026-04-22 12:53:07 +00:00
- Fix `save_file` crash on vectors with missing tangent control points — default to straight segments
- Validate `create_vector` path JSON upfront with clear error messages for malformed input
- Fix MCP/AI tools rejecting string-encoded numeric arguments from MCP clients (`"42"` → `42` )
- Fix "Create Instance" context menu item always grayed out — inverted disabled flag
- Show "Create Instance" instead of "Create Component" in context menu when a component is selected
- Fix headless layout: use stored .fig dimensions instead of rough text size estimates (26K → 11K mismatches on material3.fig)
- Fix `--help` output with huge vertical gaps between commands — remove inline examples from query description
- Fix `openpencil-mcp` npm package missing `dist/stdio.js` — explicitly list entry points in tsconfig
- Show toast when MCP server fails to start instead of silently swallowing the error
2026-04-22 14:53:32 +00:00
- Fix provider settings popover not appearing — tooltip wrapper broke floating-ui positioning
- Fix `set_font_range` producing invalid style runs that crash `.fig` export — use `applyStyleToRange` , apply color and fontWeight from style name
- Fix MCP "app not connected" error — message now instructs the agent to stop and inform the user
2026-04-15 08:02:00 +00:00
- Fix external links in AI panel blocked by Tauri ACL — use opener plugin instead of shell
fix(vue): batch color picker drag updates into one undo entry (#187)
PropertyListRoot's update() and patch() pushed a new undo entry on
every call, so dragging a color slider or color area in the fill,
stroke, or effect pickers filled the undo stack with dozens of
intermediate entries per interaction. Cmd+Z had to be pressed many
times to revert a single conceptual color change.
Wrap update() and patch() in a debounced begin/commit batch keyed by
(op, propKey, index, nodeIds). Rapid events collapse into one undo
entry, and the batch commits after 300ms of idle. Changing prop key,
index, or selection flushes the previous batch immediately, and
add/remove/toggleVisibility flush any pending batch before running so
button actions never mix with drag history. The existing explicit
batch in toggleVisibility for multi-node is preserved.
2026-04-13 06:45:50 +00:00
2026-04-08 19:04:22 +00:00
## 0.11.6 — 2026-04-08
### Fixes
- Switch `@open-pencil/core` build from `tsgo` + `fix-esm-import-path` to `tsdown` — fixes bare directory imports that broke Node.js and Bun consumers
2026-04-08 18:38:44 +00:00
## 0.11.5 — 2026-04-08
### Fixes
- Fix published npm packages resolving to TypeScript source instead of compiled JavaScript — `publishConfig.exports` overrides are now applied during CI publish
- Fix Windows CI build failures caused by backslash file paths in custom lint rules
2026-04-08 17:52:09 +00:00
## 0.11.4 — 2026-04-08
### Fixes
- Fix `@open-pencil/core` published package containing stale import paths from before the domain module restructuring — CLI and MCP installs from npm now resolve correctly
- Add `save_file` MCP tool for saving the current document to disk
- Clipboard text export now writes richer v4 `derivedTextData` payloads with glyph outlines for better paste fidelity
2026-04-08 13:05:47 +00:00
## 0.11.3 — 2026-04-08
2026-03-31 13:49:07 +00:00
### Fixes
2026-04-08 10:18:02 +00:00
- Show actionable install errors in the chat panel when a required local AI CLI is missing
- Fix inline layer rename so clearing the name restores the default name, and Backspace/Delete inside rename inputs no longer delete the layer
- Fix rotated frame hit testing, hover highlights, and selection overlays so interactive areas and overlay labels stay aligned during rotation
- Fix text edit undo so it restores both the original text and `styleRuns`
- Pressing Enter with a selected text node now starts text editing and selects all text
- Fix `ScrubInput` Enter handling so committing a value no longer triggers a second blur-based commit that overwrites it
- Show the auto-layout panel for `COMPONENT` , `COMPONENT_SET` , and `INSTANCE` nodes
- Fix missing layout direction icons in the auto-layout controls
- Fix nested text selection inside gradient cards
- Unify the size control into a single inline sizing input/dropdown with shorter localized labels to prevent overflow
2026-03-31 13:49:07 +00:00
2026-03-30 15:38:36 +00:00
## 0.11.2 — 2026-03-30
### Fixes
- Stabilize npm publishing with isolated temp publish directories instead of mutating tracked package manifests in CI
- Strip build-time scripts and dev dependencies from generated publish manifests so tarballs pack from verified artifacts only
- Fix `@open-pencil/mcp` release packaging so the published npm tarball includes its built `dist/` CLI and server entrypoints deterministically
2026-03-30 15:54:18 +00:00
- Fix `@open-pencil/core` release build configuration so CI publish jobs include Node and Bun ambient types when compiling package artifacts
2026-03-30 15:38:36 +00:00
2026-03-30 14:24:00 +00:00
## 0.11.1 — 2026-03-30
### Fixes
- Fix npm publishing pipeline to publish packed tarballs instead of raw package folders
2026-03-30 15:38:36 +00:00
- Attempt to fix `@open-pencil/mcp` npm package contents so the published CLI includes its built `dist/` entrypoints
2026-03-30 14:24:00 +00:00
- Fix `@open-pencil/vue` npm package metadata and build output so the published package resolves from `dist/` while local workspace development keeps using source aliases
2026-03-30 13:48:27 +00:00
## 0.11.0 — 2026-03-30
2026-03-15 10:37:51 +00:00
### Features
- Lock and visibility toggle buttons in layers panel (hover to reveal, always shown when active)
- Figma-style selection scope — double-click to enter groups/frames/components, Escape to exit
- Nested container navigation — each double-click goes one level deeper
- Dashed border around entered container for visual feedback
- Layer panel click syncs canvas scope automatically
2026-03-25 18:45:52 +00:00
- Vue SDK internationalization primitives — `useI18n()` , locale detection, persisted locale selection, lazy-loaded locale JSON files, and exported locale metadata for custom editor shells
2026-03-30 12:11:31 +00:00
- Vue SDK docs and public API audit — documented advanced exports (`useOkHCL()`, variables helpers, viewport and locale APIs), aligned docs with the actual `provideEditor()` injection model, and expanded release-ready SDK guidance
2026-03-30 13:48:27 +00:00
- npm release pipeline now publishes `@open-pencil/core` , `@open-pencil/cli` , `@open-pencil/mcp` , and `@open-pencil/vue` together on version tags
2026-03-25 18:45:52 +00:00
- App language picker in the menu bar — switch UI locale without reloading
2026-03-25 21:28:55 +00:00
- Added a vector curve editor and improved drawing experience with the pen tool
- Resume pen drawing from existing open path endpoints — click an endpoint to continue the curve
- Close open paths by dragging one endpoint to the other
2026-03-26 06:55:56 +00:00
- Align selected anchor points relative to each other in vector edit mode — the standard alignment buttons in the position panel now operate on selected vertices when 2 or more are selected
2026-03-28 12:26:37 +00:00
- Unified core IO format registry — `.fig` is now modeled as the native document format alongside shared export adapters for PNG, JPG, WEBP, SVG, and JSX
- Export selection or current page as `.fig` from the app export UI and app menu
2026-03-28 13:34:32 +00:00
- New CLI commands: `open-pencil convert` for document conversion, `open-pencil formats` to inspect readable/writable/exportable formats, and `open-pencil lint` for design consistency, structure, and accessibility checks
2026-03-28 12:26:37 +00:00
- CLI export now supports `.fig` output and routes PNG/JPG/WEBP/SVG/JSX/`.fig` through the shared IO layer
- `Open…` now supports `.pen` Pencil documents through the shared document reader pipeline while keeping `.fig` as the native save format
2026-03-29 15:55:14 +00:00
- Display‑ P3 document color space pipeline — documents now default to Display‑ P3, `.fig` import/export preserves document color profiles, the live canvas requests P3 surfaces with sRGB fallback, and raster/SVG export paths accept explicit color-space targets
- Color picker overhaul — unified `RGB` / `HSL` / `HSB` / `OkHCL` field formats, slider-space-aware track/thumb previews, and better neutral-color editing behavior for fills, strokes, gradient stops, and component fills
- OkHCL metadata now round-trips through `.fig` plugin data and integrates directly into the main fill/stroke color workflow with preview gamut diagnostics
- Vue SDK now exposes reusable color-picker model helpers and solid fill/stroke commit helpers for custom editor shells
2026-03-28 12:26:37 +00:00
- Update built-in Z.ai and MiniMax model lists — Z.ai now uses the Anthropic-compatible endpoint for GLM coding models, adds GLM-5.1, and MiniMax adds M2.7 / M2.7-highspeed
2026-03-29 09:42:30 +00:00
- Arabic and RTL support across text rendering, editing, layout, export, and AI tooling — text nodes support `Auto` /`LTR`/`RTL`, auto-layout frames support `Auto` /`LTR`/`RTL` flow, and JSX/AI prompts/tools can now generate and edit both explicitly
2026-03-15 10:37:51 +00:00
2026-03-16 10:27:02 +00:00
### Fixes
2026-03-25 09:04:00 +00:00
- Fix shortcuts, now work on non-English keyboard layouts.
2026-03-24 13:52:45 +00:00
- Fix imported `.fig` file open and page-switch regressions — loaded documents now keep graph/store state in sync, remap imported canvas/page children correctly, and recompute imported auto-layout descendants when switching pages
2026-03-25 18:45:52 +00:00
- Fix first canvas render happening before fonts load — wait for fonts before the initial draw to avoid Safari and text measurement glitches
- Preserve `fig-kiwi` version on `.fig` roundtrip — imports keep the original header version instead of rewriting everything to a hardcoded value; new files default to version 101
- Normalize auto-layout text export for Figma — text children inside auto-layout frames now serialize with `NONE` auto-resize to match Figma behavior and avoid overflow on reimport
- Fix keyboard editing regressions after the refactor — canvas shortcuts no longer fire while editing text, and Delete/Backspace no longer delete nodes during text entry
- Fix MCP page switching persistence — `switch_page` now survives across tool calls in the same session
- Improve CJK font fallback coverage — load multiple Google Fonts for broader Han/Japanese/Korean text support
- Normalize more visible UI strings for localized app chrome — menus, panels, variables dialog, code panel, chat setup, and editor controls now respect the selected locale instead of falling back to English in common flows
2026-03-24 13:52:45 +00:00
- Fix imported text rendering in browser and headless export — preserve stored bounds until fonts are ready, restore missing font-loaded guards, use natural width for `WIDTH_AND_HEIGHT` text, and clip text to node bounds
- Fix browser/headless rendering mismatch for imported toolbar/instance content by correcting runtime imported layout recomputation instead of diverging browser rendering behavior
2026-03-16 10:27:02 +00:00
- Fix `set_layout` tool not defaulting to HUG sizing when enabling auto-layout — frames now shrink/grow to fit children instead of keeping fixed dimensions
2026-03-16 17:57:58 +00:00
- Normalize font family names on `.fig` export — strip optical size suffixes (e.g. "DM Sans 9pt" → "DM Sans") so Figma recognizes the font
2026-03-16 18:15:02 +00:00
- TEXT nodes now default to a solid black fill — previously exported with no fill, making text invisible when opened in Figma
2026-03-16 11:18:26 +00:00
- Fix save crash when COLOR variable is missing alpha channel
- Fix console error spam on deployed web app from automation WebSocket reconnect loop
- Fix headless CLI font fallback — bundled Inter font now ships with `@open-pencil/core` and loads without a web server
2026-03-15 10:37:51 +00:00
- Locked nodes now block move, resize, rotate, and delete on canvas
- Locked containers block double-click enter
- Marquee selection skips locked and hidden nodes
- COMPONENT/INSTANCE containers are now enterable via double-click
2026-03-26 12:51:45 +00:00
- Replaced the alignment and reflection icons with the correct ones
2026-03-16 10:27:02 +00:00
2026-03-15 16:27:37 +00:00
## 0.10.0 — 2026-03-15
2026-03-10 13:31:51 +00:00
2026-03-12 11:49:28 +00:00
### Performance
- Offload .fig parsing (unzip + Kiwi decode) to a Web Worker — main thread stays responsive during file open
- Offload .fig compression to a Web Worker during save (was blocking 450ms+)
- Add instance index (`componentId → Set< nodeId > `) — `getInstances()` is O(1) instead of scanning all nodes
- Defer graph event subscription until after layout computation during file open — eliminates redundant `syncInstances` calls
- Cache label collection (sections/components) per scene mutation instead of walking the full tree every frame
2026-03-12 19:07:10 +00:00
- Blocking font loading — fonts load before first render to ensure correct glyphs
2026-03-12 11:49:28 +00:00
2026-03-10 13:31:51 +00:00
### Features
2026-03-15 16:27:37 +00:00
- ACP agent support — use Claude Code, Codex, or Gemini CLI as AI assistants in the desktop chat panel
2026-03-15 15:13:29 +00:00
- Permission confirmation dialog — ACP agents request user approval for file/shell operations, MCP design tools auto-approved
Harden ACP transport, MCP server, and add permission dialog
- Extract mapUpdate to testable module, dynamic import for @tauri-apps/plugin-shell
- Add warnings for unhandled ACP content types and empty tool titles
- MCP server: session limit (max 10), fix null WS comparison, guard JSX preprocessing
- MCP server: read version from package.json instead of hardcoded 0.0.0
- MCP tests: use port 0 (OS-assigned) to prevent collision
- Connection error handling with user-friendly messages, stale session recovery
- Agent crash detection via close handler, destroying flag, buildCrashChunks
- Port collision: detect EADDRINUSE in vite-plugin stderr and log clear error
- Production Tauri: spawn openpencil-mcp via shell plugin, orphan reuse via health check
- Permission confirmation dialog (reka-ui AlertDialog) with queue, 60s auto-reject timeout
- Health check in ProviderSelect hides ACP agents when MCP server unavailable
- Move DESIGN_CONTEXT to app constants, add ACP_PERMISSION_TIMEOUT_MS
- 36 tests across 3 files (acp-transport, acp-permission, mcp-server)
- Update CHANGELOG, README, CONTRIBUTING, AGENTS.md
2026-03-15 13:37:15 +00:00
- Unified MCP server — single HTTP + WebSocket proxy replaces Vite SSR bridge
2026-03-15 16:27:37 +00:00
- Stock photo integration — `stock_photo` tool fetches images from Pexels or Unsplash and applies to design nodes. Provider adapter supports custom providers.
- Skeleton-first AI workflow — 4-phase design process (plan → skeleton → content fill via `replace_id` → polish) for more reliable AI-generated layouts
- Batched AI tools — `calc` accepts arrays of expressions, `stock_photo` fetches all images in parallel, `batch_update` applies multiple property changes in one call, `describe` accepts `ids` array for multi-node inspection
- AI visual feedback — blue pulsing border on nodes being modified, green flash on completion
- Auto-depth `describe` — adapts inspection depth to subtree size (small block → deeper, large page → shallower)
- `set_fill` gradient support — linear gradients with `color_end` and `gradient` direction params
- `render` tool `replace_id` — atomically swap skeleton placeholders with real content
- MCP `export_image_file` tool for headless PNG rendering
- Grid layout in AI chat — JSX renderer supports `grid` , `columns` , `rows` , `gap` props
2026-03-10 13:31:51 +00:00
- Configurable max output tokens in AI provider settings (default 16384)
2026-03-11 10:26:55 +00:00
- Z.ai AI provider with GLM-5, GLM-4.7, GLM-4.6, GLM-4.5 model families
- MiniMax AI provider with M2.5, M2.1, M2 models
2026-03-10 13:31:51 +00:00
2026-03-15 16:27:37 +00:00
### Improved .fig import fidelity
- Resolve variable-bound fill colors through alias chains
- Fix SCALE constraint resizing for auto-layout instances
- Propagate SCALE constraints through instance clone chains
- Skip self-referencing symbolOverrides on nodes with explicit kiwi properties
- Fix DSD resolution for swapped instance children
- Fix instance swap override propagation through clone chains
- Fix component property override resolution through clone chains
- Fix text/property overrides clobbered by second transitive sync
2026-03-10 13:31:51 +00:00
### Fixes
2026-03-12 19:07:10 +00:00
- Fix text rendering with wrong fonts on file open — all font weights (including default family) are now loaded before the first render
- Fix `weightToStyle` mapping: weight 400 now correctly maps to "Regular" instead of "Medium"
2026-03-12 11:49:28 +00:00
- Fix detached ArrayBuffer crash when switching pages after saving — export worker now copies image buffers before transferring
- Show warning toast when fonts fail to load, error toast when file open fails
- Fix FillPicker crash when selecting image fills (missing `ref` import from #92 )
- Fix Google Fonts TLS/network errors not cached — failed families no longer retry on every render
2026-03-10 13:59:18 +00:00
- Fix CJK text garbled when font is unavailable — fallback now renders through paragraph shaper instead of raw `drawText` , preserving CJK characters via the fallback font chain
2026-03-10 13:31:51 +00:00
- Fix auto-layout overflow in AI-generated designs — text wrapping, min/max constraints, absolute positioning, and FILL sizing now work correctly
- Fix `layoutAlignSelf` limited to STRETCH — full range supported (CENTER, MAX, MIN, BASELINE)
- Fix hidden auto-layout children losing their dimensions on layout recompute
- Fix ProviderSettings popover not visible in AI chat
- Fix paste/copy/cut intercepted by canvas in AI chat input
- Strip TypeScript casts from AI-generated JSX (`as any`, `as const` )
- Fix parsing complex .fig files crashing on missing GUIDs in component overrides
2026-03-15 16:27:37 +00:00
- Fix headless text layout using 100× 100 default size instead of estimated dimensions — multi-line wrapping now estimated correctly
2026-03-12 14:29:43 +00:00
- Fix clipboard roundtrip losing properties — clipsContent, constraints, arcData, strokeCap/Join, layoutAlignSelf, textAutoResize, autoRename now preserved in Figma Kiwi serialization
2026-03-13 11:46:20 +00:00
- Fix MCP headless export crashing on `window.queryLocalFonts` in non-browser runtimes (Bun/Node)
- Fix MCP `export_image` rendering blank text — fonts now loaded before rasterization
2026-03-15 16:27:37 +00:00
- Fix text always using paragraph rendering with Inter fallback chain (no more missing-font garbling)
- Clip children to rounded corners when `clipsContent` is true
- Use child shape for drop shadows on transparent containers
- Treat `FOREGROUND_BLUR` as layer blur wrapping children
- Fix radial, angular, and diamond gradient rendering
- Fix .fig export roundtrip: variable GUIDs colliding with document
- Fix file open dialog not working on first click in Safari
- Skip variable fonts from local font access, use Google Fonts instead
- Disable autosave by default
2026-03-10 13:31:51 +00:00
2026-03-09 14:43:11 +00:00
## 0.9.0 — 2026-03-09
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 15:23:53 +00:00
### Features
XPath query for node selection (#82)
* Add XPath query for node selection
- packages/core/src/xpath.ts: XPath engine using fontoxpath with custom
DOM facade over SceneGraph nodes
- RPC command: 'query' with selector, page, limit params
- CLI command: 'open-pencil query <file> <xpath>' with --json, --page, --limit
- AI/MCP tool: 'query_nodes' with XPath examples in description
- Queryable attributes: name, width, height, x, y, visible, opacity,
cornerRadius, fontSize, fontFamily, fontWeight, layoutMode, itemSpacing,
padding*, strokeWeight, rotation, locked, blendMode, text, lineHeight,
letterSpacing
* Add tests and docs for XPath query
- 9 unit tests for queryByXPath/matchByXPath core functions
- 6 tool tests for query_nodes AI/MCP tool
- README: query examples in CLI section
- VitePress docs: patterns, queryable attributes, example output
- CHANGELOG: feature entry
* Lazy-load fontoxpath to avoid bundling in desktop app
- Dynamic import() instead of static import for fontoxpath
- queryByXPath/matchByXPath now async (they were sync before)
- RpcCommand.execute allows R | Promise<R> for async commands
- Fix 2 lint errors (unnecessary optional chain, always-truthy)
- Update all tests and CLI to await results
2026-03-09 12:32:27 +00:00
- XPath query command — `open-pencil query design.fig "//FRAME[@width < 300]"` to find nodes by type, attributes, and tree structure using XPath selectors
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 15:23:53 +00:00
- CSS Grid layout mode — select a frame, click the grid icon in the auto layout toolbar to switch from flex to grid. Configure column/row tracks (fr, fixed px, auto), column and row gaps, and per-side padding. Powered by a [Yoga fork ](https://github.com/open-pencil/yoga/tree/grid ) with cherry-picked CSS Grid PRs from upstream
- JSX and Tailwind CSS export for grid layouts — `grid grid-cols-N` , `gap-x-*` /`gap-y-*`, child `col-start-*` /`row-start-*`/`col-span-*`/`row-span-*`
Multi-provider AI, sucrase JSX, Figma zoom shortcuts (#72)
* Add multi-provider AI support (Anthropic, OpenAI, Google AI, OpenAI-compatible)
- Add AI_PROVIDERS registry with per-provider model lists, key placeholders, and URLs
- Refactor use-chat composable: provider factory creates the right AI SDK model
- Per-provider API key storage in localStorage with automatic legacy migration
- New ProviderSetup.vue replaces APIKeySetup.vue with provider selector
- New ProviderSettings.vue popover accessible from gear icon in chat input
- OpenAI-compatible provider with custom base URL and model ID fields
- Install @ai-sdk/anthropic and @ai-sdk/google dependencies
- Update E2E tests for new provider setup flow
* Fix reactivity, key masking, missing model ID field, and chat reset on model change
- Use ref instead of computed for apiKey — computed getter over localStorage
wasn't triggering reactivity when the key was set
- Load stored key for new provider in providerId watcher
- Reset chat on modelId and customModelId change, not just provider switch
- Add custom model ID field to ProviderSetup.vue for OpenAI-compatible
- Simplify ProviderSettings key field — empty input with contextual placeholder
instead of fragile dot-masking that broke on partial edits
- Use data-test-id locator for model selector in E2E test
* Use useLocalStorage from vueuse instead of manual ref + watch + localStorage
Replaces 5 hand-rolled ref/watch/localStorage sync pairs with useLocalStorage.
API key uses a computed storage key that rebinds when the provider changes.
* Use useLocalStorage for collab name persistence
* Review fixes: changelog placement, remove dead class, cursor-pointer
* Extract ProviderSelect component and uiInput helper
- ProviderSelect.vue: shared select dropdown for AI providers
- ui/input.ts: shared input styling (sm/md sizes)
- Remove 7 duplicated input class strings across chat components
* Use uppercase acronyms: keyURL, customBaseURL, supportsCustomBaseURL
* Match select dropdown width to trigger via --reka-select-trigger-width
Move min-w-[var(--reka-select-trigger-width)] into selectContent base
style so all selects get it by default. Remove per-component overrides.
* Uppercase acronyms in variable names, improve setup form layout
- providerID, modelID, customModelID, setAPIKey, AIProviderID
- Keep apiKey lowercase at start (standard JS convention)
- Compact setup form: single column, full-width Connect button
- Fix grammar: 'a OpenRouter' → 'an OpenRouter'
- Shorter promo text
* Allow text selection in chat panel
* Fix chat E2E test: Save → Connect button text
* Disable mermaid in chat markdown renderer
Alias mermaid and beautiful-mermaid to empty shims in Vite config,
preventing vue-stream-markdown from attempting to load them.
* Interleave text and tool calls in chat messages
Render message parts in order instead of grouping all tool calls
first then all text. Use SDK's isToolUIPart/isTextUIPart/getToolName
instead of custom type guards. Fix error state: output-error, not error.
* Implement figma.viewport.scrollAndZoomIntoView()
Figma Plugin API method that centers the viewport on given nodes.
Reuses the same bounding box logic as the viewport_zoom_to_fit tool.
* Catch tool execution errors and return them to the AI
Instead of crashing with an unhandled exception, tool errors are
caught and returned as { error: message } so the AI can retry or
explain the failure. UI detects error outputs and shows them in red
with the error message when expanded.
* Replace esbuild with sucrase for JSX transform
Sucrase is a pure JS transform (201 KB / 46 KB gzip) that works in
both Node/Bun and the browser. Replaces esbuild (13 MB WASM) which
only worked in Node/Bun.
- buildComponent() and renderJSX() are now synchronous
- render tool works in browser AI chat (no more 'esbuild required')
- Handles full JS expressions (map, ternaries, Array.from, etc.)
* Update system prompt to prioritize render tool with JSX
Document available tags, props, layout, text, and sizing options.
Instruct the AI to use full JS expressions in JSX for complex layouts.
* Fix zoom shortcuts to match Figma
Cmd+0: Zoom to 100% (was incorrectly mapped to Zoom to fit)
Cmd+1: Zoom to fit
Cmd+2: Zoom to selection
Shift+1 / Shift+2: same as Cmd+1 / Cmd+2
Add zoomTo100() and zoomToSelection() to editor store.
Refactor zoomToFit() to use shared zoomToBounds() helper.
* Refactor keyboard shortcuts to useMagicKeys
Replace manual keydown handler with VueUse useMagicKeys + whenever
for declarative shortcut registration.
- mod() helper for cross-platform Meta/Control shortcuts
- plain() helper for modifier-free keys
- Proper modifier exclusion (⌘G vs ⌘⇧G no longer conflict)
- Add E2E tests: duplicate, zoom (⌘0/⌘1/⌘2/⇧1/⇧2), auto-layout (⇧A)
- All 26 keyboard shortcut tests pass
* Fix scrollAndZoomIntoView to actually zoom
Match Figma Plugin API behavior (equivalent to Shift-1): compute
zoom level that fits all nodes with padding, capped at 100%.
Previously only set center without adjusting zoom.
2026-03-08 19:05:18 +00:00
- Multi-provider AI support — connect to Anthropic, OpenAI, Google AI, or any OpenAI-compatible endpoint directly, in addition to OpenRouter. Per-provider API key storage, provider settings popover, automatic migration from single OpenRouter key
2026-03-09 12:55:03 +00:00
- Anthropic-compatible provider for custom API endpoints
- New AI tools: `get_jsx` (JSX roundtrip view), `diff_jsx` (structural diff), `describe` (semantic role, visual style, layout, design issues)
- AI visual verification — `export_image` returns image content to the model for vision-based review
- API type toggle (Completions/Responses) for OpenAI-compatible providers
2026-03-09 14:43:11 +00:00
- Figma zoom shortcuts — ⌘0 (100%), ⌘1 (zoom to fit), ⌘2 (zoom to selection), ⇧1/⇧2 alternatives
- XPath query tool — `query_nodes` for AI/MCP with attribute selectors, tree traversal, and type filtering
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 15:23:53 +00:00
2026-03-08 19:30:04 +00:00
### Fixes
2026-03-08 19:23:16 +00:00
- Serialize variables, collections, and bindings to `.fig` files — previously lost on save (#65)
2026-03-08 20:28:46 +00:00
- Text nodes created via MCP now render in Figma — emit `derivedTextData` with font metadata and layout size (#64)
2026-03-08 19:30:04 +00:00
- Double-click on layer tree no longer toggles expand/collapse — use the chevron instead
- Page rename input matches layer rename styling
2026-03-09 12:55:03 +00:00
- Fix `w="fill"` /`h="fill"` in JSX renderer — now direction-aware based on parent flex axis
- Fix text auto-resize defaulting to fixed 100× 100 — text without explicit width uses `WIDTH_AND_HEIGHT`
- Fix `clipsContent` not propagated to Yoga — frames with clip enabled now set `Overflow.Hidden`
- Fix `COUNTER_ALIGN_MAP` mapping stretch to `MIN` instead of `STRETCH`
- Fix JSX export omitting x/y for absolute-positioned children
- Fix JSX export ignoring `textAutoResize` for text sizing
- Fix drag terminating on mouseleave — drags now continue outside the canvas
- Fix `export_image` stack overflow on large nodes — chunked base64 encoding
- Undo support for auto-layout reorder, layer tree reorder, and drag reparent
- Page snapshot undo for AI tool mutations
2026-03-09 13:30:58 +00:00
- Fix collab sync for same-parent reorder — `node:reordered` events now propagated to Yjs peers
2026-03-09 14:43:11 +00:00
- Fix orphaned instances on clipboard paste — detach to FRAME when component is missing
- Fix text typography lost on Figma clipboard import — preserve fontFamily, fontWeight, fontSize, lineHeight
- Fix `copyFill` missing `gradientTransform` and `imageTransform` — gradient fills now round-trip correctly
2026-03-09 09:41:00 +00:00
2026-03-08 23:58:40 +00:00
### Performance
2026-03-09 13:30:58 +00:00
- Event-driven rendering and component sync — `SceneGraph` emits typed events on mutations; `requestRender()` calls reduced from 94 to 22, component instance sync uses microtask batching with deduplication
2026-03-08 23:58:40 +00:00
- Replace `structuredClone` with typed copy helpers for fills, strokes, effects, and style runs (~24× faster in hot paths)
- Filter .fig unzip to only decompress canvas and image entries, skipping metadata cruft
2026-03-09 17:48:27 +00:00
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 15:23:53 +00:00
### Improvements
2026-03-09 12:55:03 +00:00
- Padding on a frame auto-enables vertical auto-layout
- AI tools run `computeAllLayouts` after execution — layout updates immediately
- Enhanced AI system prompt with full JSX prop reference and verification workflow
- Chat panel preserves messages when toggling UI visibility
2026-03-09 13:30:58 +00:00
- SceneGraph event bus (nanoevents) — `node:created` , `node:updated` , `node:deleted` , `node:reparented` , `node:reordered` events replace monkey-patching in collab sync and manual render invalidation
2026-03-09 14:43:11 +00:00
- Replace esbuild-wasm (14 MB) with sucrase (201 KB) for JSX transform — `buildComponent()` and `renderJSX()` now synchronous and browser-compatible
- `useMagicKeys` keyboard shortcut system — replaces tinykeys with VueUse built-in, cross-platform Meta/Control handling, modifier exclusion for combo conflicts
2026-03-09 12:55:03 +00:00
- Dev-only debug toolbar for copying chat logs
2026-03-09 09:40:07 +00:00
- Auto-layout icons in layer tree — vertical (rows), horizontal (columns), and grid icons for auto-layout frames; components keep their purple diamond
- Frame titles on canvas are now draggable — clicking a selected top-level frame's name label starts a drag
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 15:23:53 +00:00
- Compact layout controls — icon-based gap (↔/↕) and padding (T/R/B/L) inputs instead of text labels
- Auto-detect horizontal vs vertical direction when wrapping in auto layout (Shift+A)
- Fix alignment grid for vertical layouts — visual positions now match spatial axes
- Fix grid switch from HUG-sized frames — frame expands to fit children
- Remove unwanted white fill when wrapping in auto layout
2026-03-07 21:20:47 +00:00
## 0.8.0 — 2026-03-07
2026-03-03 15:35:30 +00:00
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 16:00:40 +00:00
### Features
2026-03-06 15:33:02 +00:00
- Mobile layout & PWA — responsive editor with touch-optimized toolbar, swipeable bottom drawer (layers/properties/design/code), HUD overlay, and installable PWA with icons and service worker
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 15:04:11 +00:00
- Tailwind CSS v4 JSX export — export selections as HTML with Tailwind utility classes (`< div className = "flex gap-4 p-3" > `) from the Code panel, CLI (`bun open-pencil export --format jsx --style tailwind`), or programmatically via `sceneNodeToJSX(id, graph, 'tailwind')` . Supports layout, sizing, colors, border radius, opacity, rotation, overflow, shadows, blur, and typography. Uses v4 spacing semantics (px/4 multiplier) with automatic fallback to arbitrary values.
- Code panel format toggle — switch between OpenPencil (custom components) and Tailwind (HTML + utility classes) output
2026-03-06 15:08:47 +00:00
- Homebrew tap — `brew install open-pencil/tap/open-pencil` for macOS (arm64 + x64), auto-updated on each release
2026-03-06 15:33:02 +00:00
- Double-click to rename layers — inline rename in layer panel, shared `useInlineRename` composable
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 16:00:40 +00:00
- New AI/MCP tools: `analyze_colors` , `analyze_typography` , `analyze_spacing` , `analyze_clusters` , `diff_create` , `diff_show` , `get_components` , `get_current_page` , `arrange` , `node_to_component`
2026-03-07 12:11:31 +00:00
- CLI-to-app RPC bridge — all CLI commands work against the running app when no file is specified. Start the app, then run `bun open-pencil tree` to inspect the live document
2026-03-07 20:19:19 +00:00
- VitePress docs site — user guide, reference, architecture, and development docs at openpencil.dev with 6 locales (en, de, fr, es, it, pl), SEO (OG tags, hreflang, JSON-LD, sitemap), and dark theme
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 16:00:40 +00:00
### Improvements
2026-03-07 11:56:35 +00:00
- Refactor mobile drawer tabs, layout sizing dropdowns, and inline rename to use Reka UI primitives
- Add shared UI style helpers with tailwind-variants for menus, selects, buttons, and surfaces
2026-03-07 21:20:47 +00:00
- Unified tool definitions — define once in `packages/core/src/tools/` , automatically available in AI chat, CLI, and MCP
- Harden FigmaAPI — hide internals via Symbols, freeze arrays, fix `layoutSizing` , 30+ new properties and methods
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 16:00:40 +00:00
- Split tools into domain files (read, create, modify, structure, variables, vector, analyze) — easier to navigate and extend
- Replace inline type definitions with named types (`Color`, `Vector` , `SceneNode` ) across the codebase
2026-03-07 10:48:25 +00:00
- Split 3200-line `renderer.ts` into `packages/core/src/renderer/` with 10 focused files (scene, overlays, fills, strokes, shapes, effects, rulers, labels)
- Centralize all color utilities in `packages/core/src/color.ts` — `colorToHex8` , `colorToCSSCompact` , `normalizeColor` , `colorDistance` ; remove 5 duplicate implementations across the codebase
- Add `geometry.ts` with shared rotation math (`degToRad`, `radToDeg` , `rotatePoint` , `rotatedCorners` , `rotatedBBox` )
- Extract `isArrayMixed()` helper for multi-selection property panels
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 16:00:40 +00:00
2026-03-06 15:33:02 +00:00
### Fixes
- Fix drawer animation jump on close — single spring transition instead of two-phase
2026-03-07 10:48:25 +00:00
- Fix `ALL_TOOLS` registry missing newer tools (`analyzeColors`, `diffCreate` , `exportImage` , `arrangeNodes` )
- Fix `renderJSX` typo in tool definitions (`renderJsx` → `renderJSX` )
2026-03-07 20:19:19 +00:00
- Fix all oxlint warnings and tsgo errors — replace `!` non-null assertions in `use-collab.ts` with local const captures
2026-03-07 10:48:25 +00:00
- Fix broken test imports — stale `../../src/engine/` paths updated to `@open-pencil/core`
- Fix flaky E2E tests: layers panel navigates to `/demo` , zoom-to-fit test zooms in first, snapshot rendering stabilized with `workers: 1` and `colorScheme: dark`
2026-03-07 21:20:47 +00:00
- Fix bogus .fig import mappings for `expanded` and `strokeMiterLimit` fields
- Fix PWA manifest error in dev mode, handle invalid font data gracefully
- Fix eval response unwrapping and `export_jsx` page selection in RPC bridge
- Fix automation commands not recomputing layouts after mutations
- Fix workspace dependency not resolved when installing from npm (switch CI to pnpm publish)
2026-03-06 15:33:02 +00:00
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 16:51:51 +00:00
### Internal
2026-03-06 05:50:38 +00:00
- Add `motion-v` for declarative animations — used in mobile drawer (spring-animated height with pan gestures) and toolbar (layout-animated category switching with directional slide transitions)
- Mobile drawer: replace `useSwipe` + manual rAF animation with `motion.div` `:animate` + `@pan` /`@panEnd`; always-on tab state (no more null `activeRibbonTab` ); content stays rendered when closed
- Mobile toolbar: replace manual `scrollWidth` measuring + inline CSS transitions with `motion.div layout` + `AnimatePresence` directional slide variants
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 16:51:51 +00:00
- Mobile UI cleanup: extract shared `colorToCSS` util to core, `initials` to `src/utils/text` , `toolIcons` to `src/utils/tools` ; replace hand-rolled dropdowns with reka-ui Popover/DropdownMenu; narrow `mobileDrawerSnap` type to string union; move magic numbers to constants; disable PWA service worker in dev mode
2026-03-07 10:48:25 +00:00
- 83 new E2E tests (57 → 140): design panel, code panel, components, copy/paste, multi-page, text editing, keyboard shortcuts, context menu
- 150 new unit tests (588 → 738): color, undo, snap, vector, style-runs, text-editor
2026-03-07 20:47:00 +00:00
- 48 new E2E tests (9 spec files) + 26 mutation unit tests + store/canvas test helpers
- Add `data-test-id` attributes to AppearanceSection, LayoutSection, TypographySection, VariablesDialog, EditorView
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 16:51:51 +00:00
2026-03-05 07:32:35 +00:00
## 0.7.0 — 2026-03-05
2026-03-05 04:59:24 +00:00
2026-03-04 07:17:18 +00:00
### Features
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 07:31:02 +00:00
- SVG export — export selections as SVG from the export panel, context menu, CLI (`bun open-pencil export --format svg`), or MCP/AI tools (`export_svg`). Supports rectangles, ellipses, lines, stars, polygons, vectors, text with style runs, gradients, image fills, effects, blend modes, clip paths, and nested groups (#46)
- Copy/Paste as submenu in context menu — Copy as text, Copy as SVG, Copy as PNG (⇧⌘C), Copy as JSX
2026-03-04 07:17:18 +00:00
- Stroke align (Inside/Center/Outside) with clip-based rendering matching Figma behavior
- Individual stroke weights per side (Top/Right/Bottom/Left) with side selector dropdown
- Google Fonts fallback — automatically loads fonts from Google Fonts API when not available locally
2026-03-04 07:44:06 +00:00
- Auto-save toggle in File menu — disable to prevent automatic writes to the opened .fig file
2026-03-04 11:22:30 +00:00
- Renderer profiler with in-canvas HUD overlay, GPU timing, and phase instrumentation
2026-03-04 07:17:18 +00:00
2026-03-05 07:32:35 +00:00
### Improvements
- Replace custom color picker with Reka UI Color components (ColorArea, ColorSlider, ColorField) — adds keyboard navigation and accessibility to the color area, hue, and alpha controls
2026-03-03 23:33:30 +00:00
### Fixes
2026-03-05 06:19:42 +00:00
- CJK text rendering — load a system CJK font (PingFang SC, Microsoft YaHei, Noto Sans CJK) as fallback; falls back to Noto Sans SC from Google Fonts when no system font is available (#48)
- Font registration errors no longer cache invalid font data — `loadFont` only caches after successful CanvasKit registration
2026-03-05 06:20:25 +00:00
- Fix `render` tool failing on Windows + Bun with "Cannot find module" error (#43)
2026-03-05 05:56:35 +00:00
- Fix hover highlighting nodes from internal component pages — scope hit-test to current page
- Fix hit-testing on transparent frames and groups — empty containers without fills or strokes are now click-through, clipping parents reject hits outside their bounds, matching Figma behavior
2026-03-04 15:14:23 +00:00
- Fix instance overrides on .fig import and clipboard paste — resolve guidPaths by overrideKey, handle component swaps (`overriddenSymbolID`), propagate through nested clone chains. Import and paste now share a single override engine.
- Apply Figma component property assignments on import — boolean visibility toggles and instance swaps via `componentPropRefs` /`componentPropAssignments`
- Apply `derivedSymbolData` sizes on import — containers now shrink correctly when component properties hide children
- Fix override resolution for nested instance targets — check the current node before searching descendants
2026-03-04 16:30:46 +00:00
- Fix component property assignments for nested instances — resolve scoped `componentPropAssignments` inside `symbolOverrides` via guidPath, handle `guidValue` for instance swaps, reorder phases so transitive sync doesn't clobber visibility
- Pixel-perfect vector rendering using pre-computed `fillGeometry` /`strokeGeometry` blobs from .fig files — eliminates white gaps between adjacent stroked shapes
- Stroke outlines on clipboard paste — convert vectorNetwork paths to filled outlines via CanvasKit when geometry blobs are unavailable
- Apply `derivedSymbolData` transforms and geometry during import — instance children render at correct scale and position
2026-03-04 11:22:30 +00:00
- Fix internal pages becoming visible after .fig round-trip — preserve `internalOnly` flag on export
- Scope layout recomputation to current page for paste/undo/font-load (major speedup on large multi-page files)
2026-03-04 07:17:18 +00:00
- Show loading overlay until all document fonts are loaded (no more partially rendered text)
- Load fonts when switching pages (previously only loaded for the first page)
- Always show visibility toggle on fill, stroke, and effect rows (matches Figma)
2026-03-04 07:44:06 +00:00
- Fix renderer crash on double destroy when closing files quickly
- Fix .fig page ordering — use deterministic byte comparison for fractional index positions
2026-03-04 07:17:18 +00:00
- Fix text truncation using `textTruncation` field instead of `textAutoResize`
2026-03-03 23:33:30 +00:00
- Fix horizontal scrollbar on design and pages panels
- Style scrollbars for Tauri (thin dark overlay instead of default OS chrome)
- Enable file watcher in Tauri — `watch` feature was missing from `tauri-plugin-fs`
2026-03-03 22:47:37 +00:00
## 0.6.0 — 2026-03-04
2026-03-03 15:35:30 +00:00
### Features
2026-03-03 17:36:58 +00:00
- Multi-selection properties panel — edit position, size, appearance, fill, stroke, and effects across multiple selected nodes
- Shared values display normally, differing values show "Mixed"
- W/H inputs in multi-selection mode
2026-03-03 15:35:30 +00:00
- Flip horizontal/vertical using scale transform instead of rotation
- Single-node alignment aligns to parent frame bounds
2026-03-03 17:45:58 +00:00
- ACP agent package — Agent Communication Protocol server for AI coding tools, reusing core ToolDefs
2026-03-03 15:35:30 +00:00
2026-03-03 22:43:52 +00:00
### Build
- Apple code signing and notarization for macOS builds
- Git LFS storage moved from GitHub to Cloudflare R2
2026-03-03 15:35:30 +00:00
### Fixes
2026-03-03 18:10:45 +00:00
- Fix Figma clipboard paste: extract shared kiwi→SceneNode conversion, fixing broken auto-layout, missing gradient/image fills, effects, style runs, and text properties
- Fix vector rendering on paste — scale path coordinates from Figma's normalizedSize to actual node bounds
- Fix pasted instances having no children — populate from component via symbolData when both are in clipboard
2026-03-03 21:12:33 +00:00
- Detect component sets on import — promote FRAME nodes with VARIANT componentPropDefs to COMPONENT_SET
- Skip internal canvas on paste — components on Figma's hidden internal page populate instances but are not pasted as visible nodes
- Apply instance overrides on paste — text content, fills, visibility, layoutGrow, and textAutoResize from symbolOverrides
- Fix auto-layout child ordering — sort by geometric position instead of z-order position strings
- Load fonts on paste and .fig import — collect font families from text nodes and load into CanvasKit
- Text measurement in auto-layout — use CanvasKit paragraph metrics for WIDTH_AND_HEIGHT text nodes
2026-03-03 22:43:52 +00:00
- Recompute layouts after font loading completes
- Fix PERCENT line height conversion — was stored as raw value instead of pixels
- Fix InvalidCharacterError when copying nodes with non-ASCII text
- Load all font weight/style variants needed by pasted text nodes
- Fix font loading not registering in core cache
- Fix halfLeading applied to text measurement — enable only for rendering
- Clear hover on zoom/pinch to keep scene picture cache valid
2026-03-03 15:35:30 +00:00
- Fix flip buttons using rotation math instead of actual mirroring
2026-03-03 17:36:58 +00:00
- Fix flip transform encoding — scale first matrix column only (was incorrectly producing 180° rotation)
2026-03-03 15:35:30 +00:00
- Decode flip state from .fig transform matrix on import
2026-03-03 14:30:09 +00:00
## 0.5.1 — 2026-03-03
### Fixes
- Fix File → Save crash when document has layer blur effects
2026-03-03 11:51:56 +00:00
## 0.5.0 — 2026-03-03
2026-03-02 16:46:04 +00:00
### Features
2026-03-03 06:56:41 +00:00
- Effects rendering: drop shadow, inner shadow, shadow spread, layer blur, background blur, foreground blur
- Text shadows render on glyphs instead of bounding box
2026-03-02 15:56:59 +00:00
- Multi-file tabs — open multiple documents in tabs within a single window
- Tab bar with close buttons, middle-click to close, and new tab (+) button
- Keyboard shortcuts: ⌘N/⌘T new tab, ⌘W close tab, ⌘O opens in new tab
- Native Tauri menu: File → New and File → Close Tab wired to tab actions
2026-03-02 16:38:17 +00:00
- Render text from SkPicture cache when fonts are missing — pixel-perfect display without the font installed
- Missing font indicator (⚠) next to font picker in the sidebar
2026-03-02 16:46:04 +00:00
- Right-click context menu on layers panel — same actions as the canvas context menu
2026-03-02 16:35:18 +00:00
- 40+ new AI/MCP tools ported from figma-use:
- Granular set tools: `set_rotation` , `set_opacity` , `set_radius` , `set_minmax` , `set_text` , `set_font` , `set_font_range` , `set_text_resize` , `set_visible` , `set_blend` , `set_locked` , `set_stroke_align`
- Node operations: `node_bounds` , `node_move` , `node_resize` , `node_ancestors` , `node_children` , `node_tree` , `node_bindings` , `node_replace_with`
- Variable CRUD: `get_variable` , `find_variables` , `create_variable` , `set_variable` , `delete_variable` , `bind_variable`
- Collection CRUD: `get_collection` , `create_collection` , `delete_collection`
- Boolean operations: `boolean_union` , `boolean_subtract` , `boolean_intersect` , `boolean_exclude`
- Vector path tools: `path_get` , `path_set` , `path_scale` , `path_flip` , `path_move`
- Create tools: `create_page` , `create_vector` , `create_slice`
- Viewport: `viewport_get` , `viewport_set` , `viewport_zoom_to_fit` , `page_bounds`
- Misc: `flatten_nodes` , `list_fonts`
2026-03-02 18:16:46 +00:00
- `set_text_properties` tool: alignment, auto-resize, decoration
- `set_layout_child` tool: sizing, grow, align_self, positioning
- 13 MCP server integration tests via `InMemoryTransport`
2026-03-02 16:46:04 +00:00
2026-03-02 18:33:37 +00:00
### UI
2026-03-03 11:48:51 +00:00
- Resizable pages/layers split in left panel with reka-ui Splitter
- Layers tree auto-expands and scrolls to reveal selected node
2026-03-03 09:35:54 +00:00
- Loading overlay on canvas while opening .fig files
2026-03-03 11:48:51 +00:00
- Hide internal-only pages (e.g. "Internal Only Canvas" in design systems)
- Render page dividers — pages named with only dashes/asterisks/spaces show as horizontal lines
- Only show component labels for COMPONENT and COMPONENT_SET, not instances
2026-03-02 18:33:37 +00:00
- Replace all native `<select>` dropdowns with reka-ui `AppSelect` component
2026-03-03 08:44:31 +00:00
- Smoother trackpad pinch-to-zoom with `Math.exp` curve and deltaMode normalization
2026-03-02 18:33:37 +00:00
- Fix font picker dropdown truncating long font names
- Show explanation in font picker when Local Font Access API unavailable (Safari/Firefox)
2026-03-02 15:56:59 +00:00
### Fixes
2026-03-03 06:56:41 +00:00
- Fix drop shadow rendering on top of fills — shadow now draws behind opaque content
- Fix effect property changes not recorded in undo/redo history
- Fix active tab text invisible against same-color background
2026-03-02 15:56:59 +00:00
- Fix clipboard "Outside int range" error — `pasteID` used unsigned int exceeding Kiwi's signed 32-bit field
- Error toasts are now sticky (don't auto-dismiss), with selectable text, copy button, and close button
2026-03-02 16:38:17 +00:00
- Truncate long node names in export button
2026-03-02 15:56:59 +00:00
2026-03-03 06:56:41 +00:00
### Performance
2026-03-03 08:37:01 +00:00
- Per-node SkPicture cache for effect rendering — unchanged shadow/blur nodes replay from cache on scene redraws
- Drop shadows use `MaskFilter` direct draw instead of `saveLayer` offscreen buffers
- Cached `ImageFilter` , `MaskFilter` , reusable effect paint — zero per-frame WASM allocations for effects
2026-03-03 09:48:51 +00:00
- Reuse GL context on panel resize — swap surface without recreating renderer, preserving all caches
2026-03-03 08:48:03 +00:00
- Per-frame absolute position cache — avoids repeated parent-chain walks during rendering
2026-03-03 06:56:41 +00:00
- Optimize zoom/pan smoothness with `shallowReactive` , `useRafFn` , and input coalescing
2026-03-02 16:46:04 +00:00
### Build
- Auto-populate GitHub Release notes from CHANGELOG.md via `ffurrer2/extract-release-notes@v2`
- Skip already-published npm versions on CI re-runs instead of failing
2026-03-03 06:56:41 +00:00
- Exclude non-app directories from Vite file watcher
2026-03-02 16:46:04 +00:00
2026-03-02 16:35:18 +00:00
### Internal
- Extract shared color constants (`BLACK`, `TRANSPARENT` , `DEFAULT_SHADOW_COLOR` ) — replaces 8 inline literals across core
2026-03-02 16:52:03 +00:00
- Extract shared `NodeContextMenuContent` component to avoid menu duplication
2026-03-02 18:16:46 +00:00
- Fix `@open-pencil/core` dep in MCP package: `workspace:*` for local dev (pnpm resolves at publish time)
2026-03-03 06:56:41 +00:00
- Replace store thunks with a late-binding proxy
2026-03-02 16:52:03 +00:00
2026-03-02 15:56:59 +00:00
### Tests
- Clipboard roundtrip tests: encode to Figma Kiwi binary → decode → verify
2026-03-03 06:56:41 +00:00
- 9 visual regression snapshot tests for effects rendering
- Zoom/pan E2E tests and pipeline benchmark
- MCP server edge-case tests for `find_nodes` and Zod validation
2026-03-03 08:48:03 +00:00
- 6 unit tests for absolute position cache
2026-03-02 16:35:18 +00:00
2026-03-02 15:14:35 +00:00
## [0.4.2] (2026-03-02)
2026-03-02 15:07:59 +00:00
### Fixes
- Fix Figma clipboard paste: skip non-visual node types (variables, widgets, stickies, connectors)
- Fix text not rendering after paste — `letterSpacing` from Figma is a `{value, units}` object, was passed as-is → `NaN` broke CanvasKit paragraph layout
- Fix undo/redo for Figma paste — no undo entry was recorded; redo duplicated `childIds`
- Center pasted Figma content in viewport instead of using original coordinates
- Compute auto-layouts after clipboard paste (same as .fig import and demo creation)
### Improvements
- Import additional properties from Figma clipboard: `layoutAlignSelf` , `clipsContent` , `fontWeight` , `italic` , `letterSpacing` , `lineHeight`
- Convert `letterSpacing` PERCENT units to pixels based on font size
### Tests
- 7 new clipboard import unit tests (14 total)
2026-03-02 15:12:36 +00:00
## [0.4.1] (2026-03-02)
2026-03-02 12:51:34 +00:00
### Fixes
- Fix text disappearing after hover when SkPicture cache was recorded before fonts loaded
- Invalidate scene picture cache on font load to prevent stale fallback text
### Docs
- Highlight copy & paste with Figma in README and feature docs
- Replace "fig-kiwi" format name with "Kiwi binary" — the format is shared between .fig files and clipboard
2026-03-02 15:12:36 +00:00
## [0.4.0] (2026-03-02)
2026-03-02 10:57:03 +00:00
### Features
2026-03-02 11:59:59 +00:00
- MCP server (`@open-pencil/mcp`) — 29 tools for headless .fig editing via stdio (Claude Code, Cursor, Windsurf) or HTTP (Hono + Streamable HTTP with sessions)
- `openpencil-mcp` and `openpencil-mcp-http` binaries — install globally via `bun add -g @open-pencil/mcp`
### Build
- All packages emit JS via tsgo + fix-esm-import-path — `@open-pencil/core` and `@open-pencil/mcp` work on Node.js without Bun
- Core package exports: `bun` condition → src (dev), `import` condition → dist (npm consumers)
- `@open-pencil/mcp` added to CI publish workflow
2026-03-02 10:57:03 +00:00
2026-03-02 15:12:36 +00:00
## [0.3.2] (2026-03-02)
2026-03-02 08:33:49 +00:00
### Performance
- Re-apply SkPicture scene caching for ~7x faster pan/zoom (0.98ms vs 6.8ms per frame at 500 nodes)
### Tests
- Visual regression tests for SkPicture cache: hover on/off cycle, multiple cycles, mouse hover, scene change + hover
- Type `window.__OPEN_PENCIL_STORE__` globally, remove ad-hoc casts from tests
2026-03-02 15:12:36 +00:00
## [0.3.1] (2026-03-02)
2026-03-02 08:14:15 +00:00
### Fixes
- Fix text disappearing after hovering a frame (revert SkPicture scene caching)
- Fix macOS startup hang: async font loading, show window on reopen
2026-03-02 15:12:36 +00:00
## [0.3.0] (2026-03-01)
2026-03-01 19:16:13 +00:00
### Performance
2026-03-01 19:28:03 +00:00
- SkPicture scene caching — pan/zoom replays cached display list instead of re-rendering all nodes
2026-03-01 19:21:20 +00:00
- Cache vector network paths — avoid rebuilding WASM paths every frame
- Cache ruler and pen overlay paints — eliminate 10 WASM Paint allocations per frame
2026-03-01 19:16:13 +00:00
- Only enable `preserveDrawingBuffer` in test mode
2026-03-01 19:21:20 +00:00
- Hoist URL param parsing out of render loop
2026-03-01 19:07:47 +00:00
2026-03-01 19:46:06 +00:00
### Fixes
2026-03-01 19:07:47 +00:00
2026-03-01 19:46:06 +00:00
- Fix npm publish: use pnpm for workspace dependency resolution with provenance
- CLI version now reads from package.json instead of hardcoded value
- Update README: accurate app size (~7 MB), streamlined feature list, current project structure
2026-03-01 19:07:47 +00:00
2026-03-02 15:12:36 +00:00
## [0.2.1] (2026-03-01)
2026-03-01 18:17:07 +00:00
### UI
- Panel header with app logo, editable document name, and sidebar toggle
- ⌘\\ to toggle side panels for distraction-free canvas
- Panels hidden by default on mobile (< 768px )
- Floating bar with logo, filename, and restore button when panels hidden
- Always show local user avatar in collab header
- Touch support for pan and pinch-zoom on iOS
### Performance
- Stubbed shiki to remove 9MB of unused language grammars (20MB → 11MB bundle)
2026-03-02 15:12:36 +00:00
## [0.2.0] (2026-03-01)
P2P collaboration: cursors, follow mode, cleanup
- Broadcast cursor position from canvas mouse move via awareness
- Broadcast selection changes via reactive watcher on selectedIds
- Follow mode: click peer avatar to track their viewport (pan + zoom)
- Zoom broadcast via watcher on store.state.zoom, not just mouse move
- Figma-style cursor arrows: colored fill, white border, name pill
- Stale cursor cleanup: removeAwarenessStates on peer leave
- MQTT signaling (replace Nostr), STUN + TURN ICE servers
- Collab constants extracted to src/constants.ts
- crypto.getRandomValues() replaces Math.random() everywhere
- Provide/inject for collab composable (COLLAB_KEY)
- Update README (collab section, tech stack), CHANGELOG, AGENTS.md
- AGENTS.md: release process, CI workflows, documentation guidelines
2026-03-01 15:11:07 +00:00
### Collaboration
- Real-time P2P collaboration via Trystero (WebRTC) + Yjs CRDT
- Peer-to-peer sync — no server relay, zero hosting cost
- WebRTC signaling via MQTT public brokers
- STUN (Google, Cloudflare) + TURN (Open Relay) for NAT traversal
- Awareness protocol: live cursors, selections, presence
- Figma-style colored cursor arrows with name pills
- Click peer avatar to follow their viewport, click again to stop
- Stale cursor cleanup on peer disconnect
- Local persistence via y-indexeddb — room survives page refresh
- Share link at `/share/<room-id>` with vue-router
- Secure room IDs via `crypto.getRandomValues()`
- Removed Cloudflare Durable Object relay server (`packages/collab/`)
### UI
2026-03-01 15:25:04 +00:00
- Toast notifications via Reka UI Toast — top-center blue pill for info, red for errors
- Global error handler (window.error + unhandledrejection) shows errors as toasts
- Link copied toast on share and copy link actions
P2P collaboration: cursors, follow mode, cleanup
- Broadcast cursor position from canvas mouse move via awareness
- Broadcast selection changes via reactive watcher on selectedIds
- Follow mode: click peer avatar to track their viewport (pan + zoom)
- Zoom broadcast via watcher on store.state.zoom, not just mouse move
- Figma-style cursor arrows: colored fill, white border, name pill
- Stale cursor cleanup: removeAwarenessStates on peer leave
- MQTT signaling (replace Nostr), STUN + TURN ICE servers
- Collab constants extracted to src/constants.ts
- crypto.getRandomValues() replaces Math.random() everywhere
- Provide/inject for collab composable (COLLAB_KEY)
- Update README (collab section, tech stack), CHANGELOG, AGENTS.md
- AGENTS.md: release process, CI workflows, documentation guidelines
2026-03-01 15:11:07 +00:00
- HsvColorArea extracted as shared component (ColorPicker + FillPicker)
- Scrollable app menu without visible scrollbar
- Selection broadcasting to remote peers
2026-03-02 15:12:36 +00:00
## [0.1.0-alpha] (2026-03-01)
2026-03-01 13:09:25 +00:00
First public alpha. The editor is functional but not production-ready.
### Editor
- Canvas rendering via CanvasKit (Skia WASM) on WebGL surface
- Rectangle, Ellipse, Line, Polygon, Star drawing tools
- Pen tool with vector network model (bezier curves, open/closed paths)
- Inline text editing on canvas with phantom textarea for input/IME
- Rich text formatting: bold, italic, underline per-character via style runs
- Font picker with system font enumeration (font-kit on desktop, Local Font Access API in browser)
- Auto-layout via Yoga WASM (direction, gap, padding, justify, align, child sizing)
- Components, instances, component sets with live sync and override preservation
- Variables with collections, modes, color bindings, alias chains
- Undo/redo for all operations (inverse-command pattern)
- Snap guides with rotation-aware edge/center snapping
- Canvas rulers with selection range badges
- Marquee selection, multi-select, resize handles, rotation
- Group/ungroup, z-order, visibility, lock
- Sections with title pills and auto-adoption of overlapping nodes
- Multi-page documents with independent viewport state
- Hover highlight following node geometry (ellipses, rounded rects, vectors)
- Context menu with clipboard, z-order, grouping, component, and visibility actions
- Color picker with HSV, gradients (linear, radial, angular, diamond), image fills
- Properties panel: position, appearance, fill, stroke, effects, typography, layout, export
- ScrubInput drag-to-change number controls
- Resizable side panels via reka-ui Splitter
### File Format
- .fig file import via Kiwi binary codec (194 definitions, ~390 fields)
- .fig file export with Kiwi encoding, Zstd compression, thumbnail generation
- Figma clipboard: copy/paste between OpenPencil and Figma
- Round-trip fidelity for supported node types
### AI Integration
- Built-in AI chat in properties panel (⌘J)
- Direct browser → OpenRouter communication, no backend
- Model selector: Claude, Gemini, GPT, DeepSeek, Qwen, Kimi, Llama
- 10 AI tools: create_shape, set_fill, set_stroke, update_node, set_layout, delete_node, select_nodes, get_page_tree, get_selection, rename_node
- Streaming markdown responses (vue-stream-markdown)
- Tool call timeline with collapsible details
### Code Panel
- JSX export of selected nodes with Tailwind-like shorthand props
- Syntax highlighting via Prism.js
- Copy to clipboard
### CLI (`@open-pencil/cli`)
- `info` — document stats, node types, fonts
- `tree` — visual node tree
- `find` — search by name/type
- `export` — render to PNG/JPG/WEBP at any scale
- `node` — detailed properties by ID
- `pages` — list pages with node counts
- `variables` — list design variables and collections
- `eval` — run scripts with Figma-compatible plugin API
- `analyze colors` — color palette usage
- `analyze typography` — font/size/weight distribution
- `analyze spacing` — gap/padding values
- `analyze clusters` — repeated patterns
- All commands support `--json`
### Core (`@open-pencil/core`)
- Scene graph with flat Map storage and parentIndex tree
- FigmaAPI with ~65% Figma plugin API compatibility
- JSX renderer (TreeNode builder functions with shorthand props)
- Kiwi binary codec (encode/decode)
- Vector network blob encoder/decoder
### Desktop App
- Tauri v2 (~5 MB)
- Native menu bar, save/open dialogs
- System font enumeration via font-kit
- Zstd compression in Rust
- macOS and Windows builds via GitHub Actions
### Web App
- Runs at [app.openpencil.dev ](https://app.openpencil.dev )
- No installation required
- File System Access API for save/open (Chrome/Edge), download fallback elsewhere
### Documentation
- [openpencil.dev ](https://openpencil.dev ) — VitePress site with user guide, reference, and development docs
- Deployed via Cloudflare Pages