2026-02-28 08:38:27 +00:00
# Contributing
## Project Structure
```
Sync specs & docs: variables, image export, CLI, core extraction
- Update specs: scene-graph (variables/collections/modes/bindings/.fig import),
editor-ui (VariablesPanel, ExportSection, splash), canvas-rendering
(variable resolution, image export, sceneVersion/renderVersion),
desktop-app (monorepo), tooling (Bun workspace), testing (variable tests)
- Create cli spec: info, tree, find, export commands
- Update docs: features, figma-comparison (79/150), roadmap (Phase 4 ✅,
Phase 5 🟡), keyboard-shortcuts (⇧⌘E), contributing (monorepo structure)
- Restore vitepress devDependency lost during merge
- Archive sync-variables-export-cli change
2026-02-28 23:17:48 +00:00
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
Sync docs with v0.5.0–v0.6.0: full locale translations, 75 tools
- Update tool count from 29 to 75 across all docs and locales
- Expand all locale pages (de/es/fr/it/pl) to full translations
- Add missing German sections: Advanced Rendering, Z-Order, ScrubInput,
CI/CD, JSX Renderer, Code Panel, full figma-comparison matrix
- Write complete figma-comparison (152-row matrix) for all locales
- Expand comparison, features, tech-stack, contributing for es/fr/it/pl
- Fix garbled text in locale mcp-tools reference pages
- Architecture diagrams use '75+ tools' to avoid staleness
2026-03-04 15:12:01 +00:00
src/commands/ info, tree, find, export, eval, analyze
mcp/ @open -pencil/mcp — MCP server for AI tools
2026-03-07 21:28:11 +00:00
src/ stdio + HTTP (Hono) transports, 87 tools
2026-02-28 08:38:27 +00:00
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)
Sync specs & docs: variables, image export, CLI, core extraction
- Update specs: scene-graph (variables/collections/modes/bindings/.fig import),
editor-ui (VariablesPanel, ExportSection, splash), canvas-rendering
(variable resolution, image export, sceneVersion/renderVersion),
desktop-app (monorepo), tooling (Bun workspace), testing (variable tests)
- Create cli spec: info, tree, find, export commands
- Update docs: features, figma-comparison (79/150), roadmap (Phase 4 ✅,
Phase 5 🟡), keyboard-shortcuts (⇧⌘E), contributing (monorepo structure)
- Restore vitepress devDependency lost during merge
- Archive sync-variables-export-cli change
2026-02-28 23:17:48 +00:00
engine/ Re-export shims from @open -pencil/core
kiwi/ Re-export shims from @open -pencil/core
types.ts Shared types (re-exported from core)
2026-02-28 08:38:27 +00:00
constants.ts UI colors, defaults, thresholds
desktop/ Tauri v2 (Rust + config)
tests/
e2e/ Playwright visual regression
engine/ Unit tests (bun:test)
docs/ VitePress documentation site
```
## Development Setup
```sh
bun install
bun run dev # Editor at localhost:1420
bun run docs:dev # Docs at localhost:5173
```
2026-07-13 07:19:24 +00:00
## SDK 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.
2026-02-28 08:38:27 +00:00
## 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:
```sh
bun run check
```
### Conventions
- **File names** — kebab-case (`scene-graph.ts`, `use-canvas-input.ts` )
2026-07-13 07:19:24 +00:00
- **Components** — PascalCase Vue SFCs (`EditorCanvas.vue`, `NumberField.vue` )
2026-02-28 08:38:27 +00:00
- **Constants** — SCREAMING_SNAKE_CASE
- **Functions/variables** — camelCase
- **Types/interfaces** — PascalCase
2026-07-13 11:04:13 +00:00
### 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.
2026-02-28 21:02:05 +00:00
### AI Agent Conventions
2026-03-01 13:18:11 +00:00
Developers and AI agents working on the codebase should read `AGENTS.md` in the repo root ([view on GitHub](https://github.com/open-pencil/open-pencil/blob/master/AGENTS.md)). Covers rendering, scene graph, components & instances, layout, UI, file format, Tauri conventions, and known issues.
2026-02-28 21:02:05 +00:00
2026-02-28 08:38:27 +00:00
## Making Changes
2026-03-27 19:01:38 +00:00
1. Implement the change
2. Run `bun run check` and `bun run test`
3. Submit a pull request
2026-02-28 08:38:27 +00:00
## Key Files
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
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/` .
Sync specs & docs: variables, image export, CLI, core extraction
- Update specs: scene-graph (variables/collections/modes/bindings/.fig import),
editor-ui (VariablesPanel, ExportSection, splash), canvas-rendering
(variable resolution, image export, sceneVersion/renderVersion),
desktop-app (monorepo), tooling (Bun workspace), testing (variable tests)
- Create cli spec: info, tree, find, export commands
- Update docs: features, figma-comparison (79/150), roadmap (Phase 4 ✅,
Phase 5 🟡), keyboard-shortcuts (⇧⌘E), contributing (monorepo structure)
- Restore vitepress devDependency lost during merge
- Archive sync-variables-export-cli change
2026-02-28 23:17:48 +00:00
2026-02-28 08:38:27 +00:00
| File | Purpose |
|------|---------|
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
| `packages/core/src/scene-graph/` | Scene graph: nodes, variables, instances, hit testing |
| `packages/core/src/canvas/renderer.ts` | CanvasKit rendering pipeline |
Sync specs & docs: variables, image export, CLI, core extraction
- Update specs: scene-graph (variables/collections/modes/bindings/.fig import),
editor-ui (VariablesPanel, ExportSection, splash), canvas-rendering
(variable resolution, image export, sceneVersion/renderVersion),
desktop-app (monorepo), tooling (Bun workspace), testing (variable tests)
- Create cli spec: info, tree, find, export commands
- Update docs: features, figma-comparison (79/150), roadmap (Phase 4 ✅,
Phase 5 🟡), keyboard-shortcuts (⇧⌘E), contributing (monorepo structure)
- Restore vitepress devDependency lost during merge
- Archive sync-variables-export-cli change
2026-02-28 23:17:48 +00:00
| `packages/core/src/layout.ts` | Yoga layout adapter |
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
| `packages/core/src/scene-graph/undo.ts` | Undo/redo manager |
Sync specs & docs: variables, image export, CLI, core extraction
- Update specs: scene-graph (variables/collections/modes/bindings/.fig import),
editor-ui (VariablesPanel, ExportSection, splash), canvas-rendering
(variable resolution, image export, sceneVersion/renderVersion),
desktop-app (monorepo), tooling (Bun workspace), testing (variable tests)
- Create cli spec: info, tree, find, export commands
- Update docs: features, figma-comparison (79/150), roadmap (Phase 4 ✅,
Phase 5 🟡), keyboard-shortcuts (⇧⌘E), contributing (monorepo structure)
- Restore vitepress devDependency lost during merge
- Archive sync-variables-export-cli change
2026-02-28 23:17:48 +00:00
| `packages/core/src/clipboard.ts` | Figma-compatible clipboard |
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
| `packages/core/src/vector/` | Vector network model |
| `packages/core/src/io/formats/raster/render.ts` | Offscreen image export (PNG/JPG/WEBP) |
| `packages/core/src/kiwi/binary/codec.ts` | Kiwi binary encoder/decoder |
Sync specs & docs: variables, image export, CLI, core extraction
- Update specs: scene-graph (variables/collections/modes/bindings/.fig import),
editor-ui (VariablesPanel, ExportSection, splash), canvas-rendering
(variable resolution, image export, sceneVersion/renderVersion),
desktop-app (monorepo), tooling (Bun workspace), testing (variable tests)
- Create cli spec: info, tree, find, export commands
- Update docs: features, figma-comparison (79/150), roadmap (Phase 4 ✅,
Phase 5 🟡), keyboard-shortcuts (⇧⌘E), contributing (monorepo structure)
- Restore vitepress devDependency lost during merge
- Archive sync-variables-export-cli change
2026-02-28 23:17:48 +00:00
| `packages/core/src/kiwi/fig-import.ts` | .fig file import logic |
| `packages/cli/src/index.ts` | CLI entry point |
2026-03-07 21:28:11 +00:00
| `packages/core/src/tools/` | Unified tool definitions split by domain (read, create, modify, structure, variables, vector, analyze) |
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
| `packages/core/src/figma-api/` | Figma Plugin API implementation |
Sync docs with v0.5.0–v0.6.0: full locale translations, 75 tools
- Update tool count from 29 to 75 across all docs and locales
- Expand all locale pages (de/es/fr/it/pl) to full translations
- Add missing German sections: Advanced Rendering, Z-Order, ScrubInput,
CI/CD, JSX Renderer, Code Panel, full figma-comparison matrix
- Write complete figma-comparison (152-row matrix) for all locales
- Expand comparison, features, tech-stack, contributing for es/fr/it/pl
- Fix garbled text in locale mcp-tools reference pages
- Architecture diagrams use '75+ tools' to avoid staleness
2026-03-04 15:12:01 +00:00
| `packages/mcp/src/server.ts` | MCP server factory |
| `packages/cli/src/commands/` | CLI commands (info, tree, find, export, eval, analyze) |
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
| `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 |