Contributing
Project Structure
packages/
core/ @open-pencil/core — engine (zero DOM deps)
src/ Scene graph, renderer, layout, codec, kiwi, types
cli/ @open-pencil/cli — headless CLI for .fig operations
src/commands/ info, tree, find, export, eval, analyze
mcp/ @open-pencil/mcp — MCP server for AI tools
src/ stdio + HTTP (Hono) transports, 87 tools
src/
components/ Vue SFCs (canvas, panels, toolbar, color picker)
properties/ Property panel sections (Appearance, Fill, Stroke, etc.)
composables/ Canvas input, keyboard shortcuts, rendering hooks
stores/ Editor state (Vue reactivity)
engine/ Re-export shims from @open-pencil/core
kiwi/ Re-export shims from @open-pencil/core
types.ts Shared types (re-exported from core)
constants.ts UI colors, defaults, thresholds
desktop/ Tauri v2 (Rust + config)
tests/
e2e/ Playwright visual regression
engine/ Unit tests (bun:test)
docs/ VitePress documentation siteDevelopment Setup
bun install
bun run dev # Editor at localhost:1420
bun run docs:dev # Docs at localhost:5173SDK documentation
VitePress is the canonical public documentation, while Storybook is the internal component-state workshop. Shared Vue demos live beside their SDK primitives and are embedded in both surfaces. The docs Tailwind entry scans these demos, so examples use the same utility-first styling in both environments.
Component API tables are extracted from Vue source and JSDoc with vue-component-meta. Keep descriptions next to the public props, events, and slots instead of duplicating signatures in Markdown. VitePress processes SDK code examples with Twoslash so imports and types stay aligned with the public package API.
Code Style
Tooling
| Tool | Command | Purpose |
|---|---|---|
| oxlint | bun run lint | Linting (Rust-based, fast) |
| oxfmt | bun run format | Code formatting |
| tsgo | bun run typecheck | Type checking (Go-based TypeScript checker) |
Run all checks:
bun run checkConventions
- File names — kebab-case (
scene-graph.ts,use-canvas-input.ts) - Components — PascalCase Vue SFCs (
EditorCanvas.vue,NumberField.vue) - Constants — SCREAMING_SNAKE_CASE
- Functions/variables — camelCase
- Types/interfaces — PascalCase
Test selectors
Playwright tests should locate behavior the way users and assistive technology do: prefer roles and accessible names, labels, and visible text. Scope repeated controls to a named region. Multi-part UI components expose local data-slot anatomy, while stable app concepts may expose semantic attributes such as data-property, data-command, or data-node-id.
Reserve data-test-id for integration boundaries that have no meaningful user-facing or domain identity. Do not add test-ID props to reusable components or generate compound IDs from current component nesting.
AI Agent Conventions
Developers and AI agents working on the codebase should read AGENTS.md in the repo root (view on GitHub). Covers rendering, scene graph, components & instances, layout, UI, file format, Tauri conventions, and known issues.
Making Changes
- Implement the change
- Run
bun run checkandbun run test - Submit a pull request
Key Files
Core engine source lives in packages/core/src/. App-specific editor, document, AI, collaboration, shell, demo, and automation code lives under src/app/*; the Vue SDK owns reusable canvas/composable code under packages/vue/src/.
| File | Purpose |
|---|---|
packages/scene-graph/src/ | Scene graph: nodes, variables, instances, hit testing, undo |
packages/core/src/canvas/renderer.ts | CanvasKit rendering pipeline |
packages/core/src/layout/ | Yoga layout adapter |
packages/core/src/clipboard.ts | Figma-compatible clipboard |
packages/core/src/vector/ | Vector network model |
packages/core/src/io/formats/raster/render.ts | Offscreen image export (PNG/JPG/WEBP) |
packages/kiwi/src/schema-runtime/ | Kiwi schema runtime and binary codec |
packages/fig/src/node-change/ | SceneGraph and Figma NodeChange conversion policy |
packages/core/src/io/formats/fig/ | App-facing .fig read/write orchestration |
packages/cli/src/index.ts | CLI entry point |
packages/core/src/tools/ | Unified tool definitions split by domain (read, create, modify, structure, variables, vector, analyze) |
packages/core/src/figma-api/ | Figma Plugin API implementation |
packages/mcp/src/server.ts | MCP server factory |
packages/cli/src/commands/ | CLI commands (info, tree, find, export, eval, analyze) |
src/app/editor/session/create.ts | Editor session assembly |
packages/vue/src/canvas/CanvasRoot.vue | Canvas rendering composable |
packages/vue/src/canvas/useCanvasInput.ts | Mouse/touch input handling |
src/app/shell/keyboard/use.ts | Keyboard shortcut handling |