2026-03-01 13:09:25 +00:00
# Changelog
2026-03-03 15:35:30 +00:00
## Unreleased
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`
### Improvements
- 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-06 15:33:02 +00:00
### Fixes
- Fix drawer animation jump on close — single spring transition instead of two-phase
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-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 18:19:07 +00:00
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