Find a file
Kayshen Xu ebaf093891 V0.1.0 (#13)
* feat(editor): implement Figma import functionality

- Added a new FigmaImportDialog component for importing .fig files.
- Integrated file processing and conversion logic to handle Figma files.
- Enhanced the editor layout to include a button for opening the Figma import dialog.
- Introduced page tabs for better navigation between document pages.
- Updated various components to utilize active page children for rendering and interactions.
- Added new dependencies: fzstd and kiwi-schema for improved functionality.

* fix(ai): harden Claude agent env variable resolution

- Filter empty strings and object values from settings.json env
- Validate ANTHROPIC_CUSTOM_HEADERS is valid JSON before passing
- Remove incorrect ANTHROPIC_CUSTOM_HEADERS auto-generation from auth token

* refactor(ai): remove silent fallback logic, surface errors in AI panel

- Remove direct-stream fallback in generateDesign, delegate to orchestrator
- Remove orchestrator planner fallback plan (buildFallbackPlanFromPrompt)
- Remove sub-agent retry logic, minimal mode retry, and placeholder insertion
- Throw on sub-agent error instead of silently degrading
- Call updateLastMessage in catch block so errors display immediately
- Clean up unused imports, functions, and console.log/warn statements

* fix(ai): add 30s API connect timeout for faster error detection

- Set maxRetries: 0 on Anthropic SDK to prevent retry delays on 429
- Add AbortController-based 30s timeout for initial API connection
- If the provider doesn't respond within 30s, abort and send SSE error
- Refactor streamViaAnthropicSDK with shared send() helper

* fix(editor): improve Figma import visual fidelity

- Extract image files from .fig ZIP archive and resolve by SHA-1 hash
- Map Figma textCase property (UPPER/LOWER/TITLE) to text content
- Convert ellipse nodes with image fills to image nodes
- Decode Figma vector paths and match icons via Lucide icon registry
- Fix z-order: move background rectangles behind content layers
- Set clipContent=true for Figma frames (default clip behavior)
- Unwrap single-artboard pages to use artboard as root frame

* fix(canvas): preserve clipPath during async image load and invalidate cache

- Copy clipPath from placeholder rect to FabricImage in onload callback
- Set obj.dirty=true after applying/removing clipPath so Fabric.js
  re-renders with the updated clip region instead of using stale cache

* fix(ai): use page-aware empty frame detection and place new designs as siblings

- Fix isCanvasOnlyEmptyFrame() to use getActivePageChildren() instead of
  document.children which is empty after page migration
- When canvas is empty, replace default frame (existing behavior restored)
- When canvas has content, add generated frame as top-level sibling
  positioned to the right with 100px gap
- Track generationRootFrameId so height adjustment targets correct frame
- Add WIDTH SELECTION rule: app screens use 375x812 mobile dimensions
- Include selected node dimensions in AI context string

* feat(electron): implement application menu with native actions

- Added a native application menu for macOS and Windows, including File, Edit, View, and Window menus.
- Integrated menu actions for creating, opening, saving files, and editing operations like undo/redo.
- Enhanced the editor layout to handle Electron menu actions through a custom hook.
- Updated icon components in the toolbar and top bar for consistent styling.
- Refactored padding and layout components for improved structure and readability.

* fix(canvas): use page-aware children for selection depth resolution

getSelectableNodeIds() used doc.children which is empty under the
pages architecture. Switch to getActivePageChildren() so canvas
selection correctly syncs to the layers panel.

* fix(panels): adjust property panel sections for image nodes

Hide fill/stroke sections (not applicable to images) and show
corner radius section for image elements.

* feat(canvas): implement image corner radius via clipPath

FabricImage does not support rx/ry natively. Use a rounded-rect
clipPath instead, with objectCaching disabled to ensure immediate
re-render on radius changes.

* fix(canvas): use page-aware forcePageResync for canvas re-sync

The old pattern { ...doc, children: [...doc.children] } only touched
root-level children which are empty under pages architecture. Extract
forcePageResync() into canvas-sync-utils and replace all 7 occurrences
across canvas-object-modified, drag-into-layout, and layout-reorder.

* fix(canvas): preserve image corner radius clipPath during drag

Three issues caused corner radius loss on image drag:

1. Scale baking in object:modified changed FabricImage width from
   natural to display dimensions, creating a clipPath size mismatch.
   Exclude images from scale baking (like paths/polygons).

2. object:moving unconditionally cleared all clipPaths to avoid stale
   frame clipping. Now only clears absolutePositioned (frame) clips,
   preserving object-level clips like image corner radius.

3. Post-drag re-sync overwrote the corner radius clipPath with the
   parent frame clip. Now skips frame clip when the object already
   has its own non-absolutePositioned clipPath.

* feat(ai): enhance chat functionality with image attachments support

- Added support for image attachments in chat messages, allowing users to send images alongside text.
- Implemented temporary file handling for attachments, ensuring they are accessible to the AI processing.
- Updated chat message structure to include attachments, and modified the prompt building logic to reference these images.
- Enhanced the chat panel UI to allow users to select and preview images before sending.
- Introduced a new state management for pending attachments in the AI store.
- Refactored related components to accommodate the new attachment feature, improving user experience and interaction.

* feat(multi-page): introduce multi-page architecture and enhance agent settings UI

- Added multi-page architecture to the document structure, allowing for page CRUD actions such as add, remove, rename, reorder, and duplicate.
- Implemented `activePageId` state management in the canvas store for better page handling.
- Enhanced the top bar with an `AgentStatusButton` to display connected AI providers and their statuses.
- Updated the agent settings dialog to improve the UI for managing AI integrations, including better layout and connection indicators.
- Refactored related components for improved usability and visual consistency.

* docs(README): add contributors section with dynamic contributor image

* feat(ai): improve chat streaming and error handling

- Added functionality to hide the checklist when streaming stops with no completed items.
- Implemented abort controller management to handle user-initiated stops during chat generation.
- Enhanced error handling to prevent displaying errors for user-initiated stops while preserving partial content.
- Updated orchestration and design generation functions to support abort signals, improving responsiveness during streaming.

* fix(canvas): align z-order with layer panel convention (top = front)

Establish consistent z-order: children[0] = top of layer panel = frontmost
on canvas. This matches the standard design tool convention (Figma, Sketch).

- Reverse flattenNodes iteration so children[0] is added last to canvas
- Add z-order reconciliation via moveObjectTo after each sync pass
- Figma import: sort children descending so front items are at children[0]
- addNode defaults to prepend (index 0) so new items appear at top
- duplicateNode inserts clone above original (at idx instead of idx+1)
- drag-reparent inserts at index 0 (frontmost) instead of childCount
- Async image loading preserves z-order via insertAt instead of add
- Add FigmaImportLayoutMode toggle (preserve vs openpencil auto-layout)

* chore: resolve .gitignore merge conflict

* fix(canvas): clamp corner radius to half the element height

* chore: update version to 0.1.0 and modify mac artifact naming in electron-builder configuration

---------

Co-authored-by: Fini <fini.yang@gmail.com>
2026-03-01 09:33:52 +08:00
.github/workflows V0.0.3 (#11) 2026-02-26 21:33:48 +08:00
.vscode Initialize OpenPencil project with essential files and configurations 2026-02-17 21:14:16 +08:00
build V0.0.3 (#11) 2026-02-26 21:33:48 +08:00
electron V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
public V0.0.3 (#11) 2026-02-26 21:33:48 +08:00
screenshot V0.0.3 (#11) 2026-02-26 21:33:48 +08:00
scripts feat(electron): integrate Electron framework for desktop application support 2026-02-20 20:19:06 +08:00
server V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
src V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
.cta.json Initialize OpenPencil project with essential files and configurations 2026-02-17 21:14:16 +08:00
.gitignore V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
bun.lock V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
CLAUDE.md V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
components.json feat(editor): scaffold core canvas engine and editor layout 2026-02-18 21:49:16 +08:00
electron-builder.yml V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
LICENSE chore: update documentation and add MIT License 2026-02-18 22:35:15 +08:00
package.json V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
README.md V0.1.0 (#13) 2026-03-01 09:33:52 +08:00
tsconfig.json Initialize OpenPencil project with essential files and configurations 2026-02-17 21:14:16 +08:00
vite.config.ts V0.0.3 (#11) 2026-02-26 21:33:48 +08:00

OpenPencil

OpenPencil

Open-source design tool with a Design-as-Code philosophy.
Design on canvas. Generate code. Let AI build screens from a prompt.

Stars License CI Discord

Quick Start · Features · AI Agents · Discord · Contributing


OpenPencil — click to watch demo

Click the image to watch the demo video


Quick Start

# Install dependencies
bun install

# Start dev server at http://localhost:3000
bun --bun run dev

Or run as a desktop app:

bun run electron:dev

Prerequisites: Bun >= 1.0 and Node.js >= 18

Features

Canvas & Drawing

  • Infinite canvas with pan, zoom, smart alignment guides, and snapping
  • Rectangle, Ellipse, Line, Polygon, Pen (Bezier), Frame, Text
  • Icon picker (Iconify) and image import (PNG/JPEG/SVG/WebP/GIF)
  • Auto-layout — vertical/horizontal with gap, padding, justify, align
  • Multi-page documents with tab navigation

Design System

  • Design variables — color, number, string tokens with $variable references
  • Multi-theme support — multiple axes, each with variants (Light/Dark, Compact/Comfortable)
  • Component system — reusable components with instances and overrides
  • CSS sync — auto-generated custom properties, var(--name) in code output

Code Generation

  • React + Tailwind CSS
  • HTML + CSS
  • CSS Variables from design tokens

Figma Import

  • Import .fig files with layout, fills, strokes, effects, text, images, and vectors preserved

Desktop App

  • Native macOS, Windows, and Linux via Electron
  • Auto-update from GitHub Releases
  • Native application menu and file dialogs

AI Agents

OpenPencil integrates with multiple AI coding agents to generate full-page designs from a single prompt — directly on canvas with streaming animation.

Agent Setup
Anthropic API Set ANTHROPIC_API_KEY in .env
Claude Code No config — uses Claude Agent SDK with local OAuth
Codex CLI Connect in Agent Settings (Cmd+,)
OpenCode Connect in Agent Settings (Cmd+,)
  • Text-to-design — describe a page, get it generated on canvas in real-time
  • Image attachments — attach screenshots or mockups for vision-based analysis
  • Orchestrator — decomposes complex pages into spatial sub-tasks for parallel generation
  • Design modification — select elements, then describe changes in natural language
  • MCP server — install into Claude Code / Codex / Gemini / OpenCode / Kiro CLIs for design automation from terminal

Tech Stack

Frontend React 19 · TanStack Start · Tailwind CSS v4 · shadcn/ui
Canvas Fabric.js v7
State Zustand v5
Server Nitro
Desktop Electron 35
AI Anthropic SDK · Claude Agent SDK · OpenCode SDK
Runtime Bun · Vite 7
File format .pen — JSON-based, human-readable, Git-friendly

Project Structure

src/
  canvas/          Fabric.js engine — drawing, sync, layout, guides, pen tool
  components/      React UI — editor, panels, shared dialogs, icons
  services/ai/     AI chat, orchestrator, design generation, streaming
  services/figma/  Figma .fig binary import pipeline
  services/codegen React+Tailwind and HTML+CSS code generators
  stores/          Zustand — canvas, document, pages, history, AI, settings
  variables/       Design token resolution and reference management
  mcp/             MCP server tools for external CLI integration
  uikit/           Reusable component kit system
server/
  api/ai/          Nitro API — streaming chat, generation, validation
  utils/           Claude CLI, OpenCode, Codex client wrappers
electron/
  main.ts          Window, Nitro fork, native menu, auto-updater
  preload.ts       IPC bridge

Keyboard Shortcuts

Key Action Key Action
V Select Cmd+S Save
R Rectangle Cmd+Z Undo
O Ellipse Cmd+Shift+Z Redo
L Line Cmd+C/X/V/D Copy/Cut/Paste/Duplicate
T Text Cmd+G Group
F Frame Cmd+Shift+G Ungroup
P Pen tool Cmd+Shift+E Export
H Hand (pan) Cmd+Shift+C Code panel
Del Delete Cmd+Shift+V Variables panel
[ / ] Reorder Cmd+J AI chat
Arrows Nudge 1px Cmd+, Agent settings

Scripts

bun --bun run dev          # Dev server (port 3000)
bun --bun run build        # Production build
bun --bun run test         # Run tests (Vitest)
npx tsc --noEmit           # Type check
bun run electron:dev       # Electron dev
bun run electron:build     # Electron package

Contributing

Contributions are welcome! See CLAUDE.md for architecture details and code style.

  1. Fork and clone
  2. Create a branch: git checkout -b feat/my-feature
  3. Run checks: npx tsc --noEmit && bun --bun run test
  4. Commit with Conventional Commits: feat(canvas): add rotation snapping
  5. Open a PR against main

Roadmap

  • Design variables & tokens with CSS sync
  • Component system (instances & overrides)
  • AI design generation with orchestrator
  • MCP server integration
  • Multi-page support
  • Figma .fig import
  • Boolean operations (union, subtract, intersect)
  • Collaborative editing
  • Plugin system

Contributors

Contributors

Community

Discord Join our Discord — Ask questions, share designs, suggest features.

License

MIT — Copyright (c) 2026 ZSeven-W