openpencil/packages/vue
Danila Poyarkov c1d7fc80b0 Fix property panel visibility toggles, independent corners/stroke sides, extract shared ColorStyleRow
- Fix fill/stroke/effect/appearance visibility eye toggle (stale clone reads)
- Fix independent corner radii toggle not showing 4 inputs
- Fix stroke sides toggle: replace dropdown with direct toggle like corners
- Copy uniform weight to all sides when expanding stroke sides
- Batch multi-selection visibility toggles into single undo entry
- Extract ColorStyleRow shared by FillSection and StrokeSection
- Replace useFillVariableBinding/useStrokeVariableBinding with useColorVariableBinding(kind)
- AppearanceSection uses useAppearance() directly instead of slot wrapper
- Add sceneVersion dependency to useNodeProps and PropertyListRoot computeds
- Add E2E tests for visibility toggles and corner/stroke side toggles
2026-03-27 17:06:33 +03:00
..
example SDK rewrite: delete 15 useless wrappers, replace with composables 2026-03-17 20:22:41 +03:00
src Fix property panel visibility toggles, independent corners/stroke sides, extract shared ColorStyleRow 2026-03-27 17:06:33 +03:00
ARCHITECTURE.md Reorganize Vue SDK modules by domain 2026-03-24 09:50:31 +03:00
package.json Add i18n to Vue SDK with nanostores 2026-03-25 16:17:03 +03:00
README.md Fix property panel visibility toggles, independent corners/stroke sides, extract shared ColorStyleRow 2026-03-27 17:06:33 +03:00
tsconfig.json Fix Vue package declaration build 2026-03-25 17:43:16 +03:00

@open-pencil/vue

Headless Vue 3 SDK for building OpenPencil-powered editors.

@open-pencil/vue sits on top of @open-pencil/core and provides:

  • Vue editor injection via provideEditor() / useEditor()
  • canvas integration via useCanvas(), useCanvasInput(), and useTextEdit()
  • selection, command, and panel composables
  • headless structural primitives like CanvasRoot, LayerTreeRoot, PageListRoot, and ToolbarRoot

The SDK is headless by design: it provides logic and structure, while your app owns styling and product-specific UI.

Install

bun add @open-pencil/vue @open-pencil/core canvaskit-wasm

Quick start

<script setup lang="ts">
import { createEditor } from '@open-pencil/core/editor'
import { provideEditor } from '@open-pencil/vue'

const editor = createEditor({
  width: 1200,
  height: 800,
})

editor.createShape('RECTANGLE', 100, 100, 200, 150)
editor.zoomToFit()

provideEditor(editor)
</script>

<template>
  <div class="h-screen">
    <CanvasRoot v-slot="{ canvasRef }">
      <canvas ref="canvasRef" class="size-full" />
    </CanvasRoot>
  </div>
</template>

Core concepts

Editor context

Use provideEditor(editor) once near the top of your subtree.

import { provideEditor } from '@open-pencil/vue'

provideEditor(editor)

Read it anywhere below with useEditor().

import { useEditor } from '@open-pencil/vue'

const editor = useEditor()

Canvas wiring

At the composable level, the main canvas APIs are:

  • useCanvas()
  • useCanvasInput()
  • useTextEdit()

If you want SDK-provided structure, use headless primitives like CanvasRoot and CanvasSurface.

Headless primitives

Main structural primitives include:

  • CanvasRoot
  • LayerTreeRoot
  • PageListRoot
  • PropertyListRoot
  • ToolbarRoot
  • ColorPickerRoot
  • FillPickerRoot
  • FontPickerRoot

These components coordinate structure and state, but do not impose app styling.

Public API tiers

Core API

These are the main APIs most SDK consumers should start with.

Context and canvas

  • provideEditor()
  • useEditor()
  • useCanvas()
  • useCanvasInput()
  • useTextEdit()

Selection and commands

  • useSelectionState()
  • useSelectionCapabilities()
  • useEditorCommands()
  • useMenuModel()

Property panels

  • usePosition()
  • useLayout()
  • useAppearance()
  • useTypography()
  • useExport()
  • useFillControls()
  • useStrokeControls()
  • useEffectsControls()

Variables and navigation

  • useVariablesEditor()
  • usePageList()

Headless primitives

  • CanvasRoot
  • LayerTreeRoot
  • PageListRoot
  • PropertyListRoot
  • ToolbarRoot

Advanced API

These exports are intentionally public, but they are lower-level or more specialized.

  • useNodeProps()
  • useSceneComputed()
  • useColorVariableBinding()
  • useFillPicker()
  • useGradientStops()
  • useFontPicker()
  • usePropScrub()
  • useLayerDrag()
  • useInlineRename()
  • useToolbarState()
  • useNodeFontStatus()
  • useCanvasDrop()
  • extractImageFilesFromClipboard()
  • toolCursor()

Primitive context helpers

These are mostly useful when extending SDK primitives rather than building from top-level composables.

  • useCanvasContext()
  • useLayerTree()
  • useToolbar()
  • usePropertyList()
  • useScrubInput()

Example patterns

Minimal provider component

<script setup lang="ts">
import { provideEditor } from '@open-pencil/vue'

import type { Editor } from '@open-pencil/core/editor'

const props = defineProps<{
  editor: Editor
}>()

provideEditor(props.editor)
</script>

<template>
  <slot />
</template>

Read selection state

import { useSelectionState } from '@open-pencil/vue'

const { hasSelection, selectedCount, selectedNode } = useSelectionState()

Build a menu

import { useMenuModel } from '@open-pencil/vue'

const { appMenu, canvasMenu } = useMenuModel()

Build a page list

<PageListRoot v-slot="{ pages, currentPageId, switchPage }">
  <ul>
    <li v-for="page in pages" :key="page.id">
      <button :data-active="page.id === currentPageId" @click="switchPage(page.id)">
        {{ page.name }}
      </button>
    </li>
  </ul>
</PageListRoot>

Documentation

For fuller guides and API docs, see the documentation site:

  • packages/docs/programmable/sdk/

Example app

Run the included example:

cd packages/vue/example
bun install
bun run dev