2026-03-16 14:24:45 +00:00
|
|
|
# @open-pencil/vue
|
|
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
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()`
|
2026-03-30 12:11:31 +00:00
|
|
|
- selection, command, panel, variables, and i18n composables
|
2026-03-24 20:25:06 +00:00
|
|
|
- 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.
|
2026-03-16 14:24:45 +00:00
|
|
|
|
|
|
|
|
## Install
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-03-24 20:25:06 +00:00
|
|
|
bun add @open-pencil/vue @open-pencil/core canvaskit-wasm
|
2026-03-16 14:24:45 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Quick start
|
|
|
|
|
|
|
|
|
|
```vue
|
2026-03-24 20:25:06 +00:00
|
|
|
<script setup lang="ts">
|
2026-03-16 14:24:45 +00:00
|
|
|
import { createEditor } from '@open-pencil/core/editor'
|
2026-03-24 20:25:06 +00:00
|
|
|
import { provideEditor } from '@open-pencil/vue'
|
|
|
|
|
|
|
|
|
|
const editor = createEditor({
|
|
|
|
|
width: 1200,
|
|
|
|
|
height: 800,
|
|
|
|
|
})
|
2026-03-16 14:24:45 +00:00
|
|
|
|
|
|
|
|
editor.createShape('RECTANGLE', 100, 100, 200, 150)
|
|
|
|
|
editor.zoomToFit()
|
2026-03-24 20:25:06 +00:00
|
|
|
|
|
|
|
|
provideEditor(editor)
|
2026-03-16 14:24:45 +00:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-03-24 20:25:06 +00:00
|
|
|
<div class="h-screen">
|
|
|
|
|
<CanvasRoot v-slot="{ canvasRef }">
|
|
|
|
|
<canvas ref="canvasRef" class="size-full" />
|
|
|
|
|
</CanvasRoot>
|
|
|
|
|
</div>
|
2026-03-16 14:24:45 +00:00
|
|
|
</template>
|
|
|
|
|
```
|
|
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
## Core concepts
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
### Editor context
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
Use `provideEditor(editor)` once near the top of your subtree.
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
```ts
|
|
|
|
|
import { provideEditor } from '@open-pencil/vue'
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
provideEditor(editor)
|
2026-03-16 14:24:45 +00:00
|
|
|
```
|
|
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
Read it anywhere below with `useEditor()`.
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
```ts
|
|
|
|
|
import { useEditor } from '@open-pencil/vue'
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
const editor = useEditor()
|
|
|
|
|
```
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
### Canvas wiring
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
At the composable level, the main canvas APIs are:
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
- `useCanvas()`
|
|
|
|
|
- `useCanvasInput()`
|
|
|
|
|
- `useTextEdit()`
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
If you want SDK-provided structure, use headless primitives like `CanvasRoot` and `CanvasSurface`.
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
### Headless primitives
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
Main structural primitives include:
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
- `CanvasRoot`
|
|
|
|
|
- `LayerTreeRoot`
|
|
|
|
|
- `PageListRoot`
|
|
|
|
|
- `PropertyListRoot`
|
2026-07-13 09:36:03 +00:00
|
|
|
- `PropertySectionRoot`
|
|
|
|
|
- `SegmentedControlRoot`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `ToolbarRoot`
|
|
|
|
|
- `ColorPickerRoot`
|
|
|
|
|
- `FontPickerRoot`
|
2026-07-10 19:26:51 +00:00
|
|
|
- `NumberFieldRoot` / `NumberFieldInput` / `NumberFieldValue`
|
2026-07-13 08:02:54 +00:00
|
|
|
- `BindableValueRoot` / `BindableValueTrigger` / `BindableValuePicker`
|
2026-07-13 12:34:25 +00:00
|
|
|
- `LayoutControlsRoot`
|
2026-07-17 15:32:58 +00:00
|
|
|
- `ConstraintsControlRoot`
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-07-10 19:26:51 +00:00
|
|
|
These components coordinate structure and state, but do not impose app styling. `NumberField`
|
|
|
|
|
adds pointer scrubbing, Arrow-key stepping, mixed/bound state attributes, and safe arithmetic
|
2026-07-13 08:02:54 +00:00
|
|
|
expressions such as `+10`, `*2`, `50%`, and `12*8+4`. `BindableValue` composes fields with a
|
|
|
|
|
generic `BindingProvider` and supports detach-on-edit, read-only, and edit-variable policies.
|
2026-07-13 11:04:13 +00:00
|
|
|
Focusing a bound NumberField is non-destructive; the configured policy begins only on the first
|
2026-07-13 12:34:25 +00:00
|
|
|
value mutation. `LayoutControlsRoot` exposes axis-oriented sizing actions; editing a Hug or Fill
|
2026-07-17 15:32:58 +00:00
|
|
|
dimension can switch that axis to Fixed inside the same provider transaction.
|
|
|
|
|
`ConstraintsControlRoot` exposes eligible frame-child constraints, mixed axis values, pin actions,
|
|
|
|
|
and undo-batched multi-selection updates. `AppearanceControlsRoot`
|
2026-07-13 12:34:25 +00:00
|
|
|
exposes selection-derived independent-corner presentation state so consumers do not need parallel
|
|
|
|
|
expansion heuristics. `PropertyListRoot` is controlled and
|
2026-07-13 11:47:48 +00:00
|
|
|
editor-agnostic; OpenPencil panels connect it to selection and undo through
|
2026-07-13 18:48:36 +00:00
|
|
|
`useEditorPropertyList()`. `useColorModel()` provides precise scene-color/Reka bridges, reactive
|
|
|
|
|
RGB/HSL/HSB/OkHCL channels, extensible format state, and shared slider presentation data.
|
2026-07-13 20:41:04 +00:00
|
|
|
`FillRoot` and `FillSwatch` separate fill behavior and binding-aware previews from popover
|
|
|
|
|
composition; `ChannelSlider` provides accessible scalar OkHCL controls until Reka supports them.
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
## 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()`
|
2026-07-17 15:32:58 +00:00
|
|
|
- `useConstraints()`
|
2026-07-17 18:59:49 +00:00
|
|
|
- `useComponentProperties()`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `useAppearance()`
|
2026-07-17 17:28:22 +00:00
|
|
|
- `useSharedStyleBinding()`
|
2026-07-13 18:48:36 +00:00
|
|
|
- `useColorModel()`
|
2026-07-10 14:01:28 +00:00
|
|
|
- `useMask()`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `useTypography()`
|
|
|
|
|
- `useExport()`
|
|
|
|
|
- `useFillControls()`
|
|
|
|
|
- `useStrokeControls()`
|
|
|
|
|
- `useEffectsControls()`
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-30 12:11:31 +00:00
|
|
|
#### Variables, navigation, and localization
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
- `useVariablesEditor()`
|
|
|
|
|
- `usePageList()`
|
2026-03-30 12:11:31 +00:00
|
|
|
- `useI18n()`
|
2026-03-24 20:25:06 +00:00
|
|
|
|
|
|
|
|
#### Headless primitives
|
|
|
|
|
|
|
|
|
|
- `CanvasRoot`
|
|
|
|
|
- `LayerTreeRoot`
|
|
|
|
|
- `PageListRoot`
|
|
|
|
|
- `PropertyListRoot`
|
2026-07-13 09:36:03 +00:00
|
|
|
- `PropertyListItem`
|
|
|
|
|
- `PropertyListAdd` / `PropertyListRemove` / `PropertyListVisibility`
|
|
|
|
|
- `PropertySectionRoot` / `PropertySectionHeader` / `PropertySectionTitle`
|
|
|
|
|
- `PropertySectionActions` / `PropertySectionContent` / `PropertySectionEmptyAction`
|
|
|
|
|
- `SegmentedControlRoot` / `SegmentedControlItem`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `ToolbarRoot`
|
2026-07-10 19:26:51 +00:00
|
|
|
- `NumberFieldRoot`
|
|
|
|
|
- `NumberFieldInput`
|
|
|
|
|
- `NumberFieldValue`
|
|
|
|
|
- `NumberFieldLeading`
|
|
|
|
|
- `NumberFieldUnit`
|
|
|
|
|
- `NumberFieldTrailing`
|
|
|
|
|
- `NumberFieldMenu`
|
2026-07-13 08:02:54 +00:00
|
|
|
- `BindableValueRoot`
|
|
|
|
|
- `BindableValueTrigger`
|
|
|
|
|
- `BindableValuePicker`
|
2026-07-13 20:41:04 +00:00
|
|
|
- `FillRoot` / `FillSwatch`
|
|
|
|
|
- `ChannelSliderRoot` / `ChannelSliderTrack` / `ChannelSliderThumb`
|
2026-03-24 20:25:06 +00:00
|
|
|
|
|
|
|
|
### Advanced API
|
|
|
|
|
|
|
|
|
|
These exports are intentionally public, but they are lower-level or more specialized.
|
|
|
|
|
|
|
|
|
|
- `useNodeProps()`
|
2026-07-13 09:36:03 +00:00
|
|
|
- `useEditorPropertyList()`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `useSceneComputed()`
|
2026-07-14 14:06:28 +00:00
|
|
|
- `useColorBindingProvider()`
|
2026-03-27 14:06:33 +00:00
|
|
|
- `useColorVariableBinding()`
|
2026-07-13 08:02:54 +00:00
|
|
|
- `provideBindingProvider()`
|
|
|
|
|
- `useBindingProvider()`
|
|
|
|
|
- `useNumberBindingProvider()`
|
2026-07-13 20:41:04 +00:00
|
|
|
- `useFill()`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `useGradientStops()`
|
|
|
|
|
- `useFontPicker()`
|
2026-03-30 12:11:31 +00:00
|
|
|
- `useOkHCL()`
|
|
|
|
|
- `useVariables()`
|
|
|
|
|
- `useVariablesDialogState()`
|
|
|
|
|
- `useVariablesTable()`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `usePropScrub()`
|
|
|
|
|
- `useLayerDrag()`
|
|
|
|
|
- `useInlineRename()`
|
|
|
|
|
- `useToolbarState()`
|
|
|
|
|
- `useNodeFontStatus()`
|
|
|
|
|
- `useCanvasDrop()`
|
|
|
|
|
- `extractImageFilesFromClipboard()`
|
2026-03-30 12:11:31 +00:00
|
|
|
- `useViewportKind()`
|
2026-03-24 20:25:06 +00:00
|
|
|
- `toolCursor()`
|
|
|
|
|
|
2026-03-30 12:11:31 +00:00
|
|
|
### Primitive context helpers and low-level stores
|
2026-03-24 20:25:06 +00:00
|
|
|
|
|
|
|
|
These are mostly useful when extending SDK primitives rather than building from top-level composables.
|
|
|
|
|
|
|
|
|
|
- `useCanvasContext()`
|
|
|
|
|
- `useLayerTree()`
|
|
|
|
|
- `useToolbar()`
|
|
|
|
|
- `usePropertyList()`
|
2026-07-10 19:26:51 +00:00
|
|
|
- `useNumberField()`
|
2026-03-30 12:11:31 +00:00
|
|
|
- `locale`
|
|
|
|
|
- `localeSetting`
|
|
|
|
|
- `setLocale()`
|
|
|
|
|
- `AVAILABLE_LOCALES`
|
|
|
|
|
- `LOCALE_LABELS`
|
2026-03-24 20:25:06 +00:00
|
|
|
|
|
|
|
|
## Example patterns
|
|
|
|
|
|
|
|
|
|
### Minimal provider component
|
2026-03-16 14:24:45 +00:00
|
|
|
|
|
|
|
|
```vue
|
2026-03-24 20:25:06 +00:00
|
|
|
<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>
|
2026-03-16 14:24:45 +00:00
|
|
|
```
|
|
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
### Read selection state
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
```ts
|
|
|
|
|
import { useSelectionState } from '@open-pencil/vue'
|
|
|
|
|
|
|
|
|
|
const { hasSelection, selectedCount, selectedNode } = useSelectionState()
|
|
|
|
|
```
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
### Build a menu
|
2026-03-16 14:24:45 +00:00
|
|
|
|
|
|
|
|
```ts
|
2026-03-24 20:25:06 +00:00
|
|
|
import { useMenuModel } from '@open-pencil/vue'
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
const { appMenu, canvasMenu } = useMenuModel()
|
2026-03-16 14:24:45 +00:00
|
|
|
```
|
|
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
### Build a page list
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
<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:
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
- `packages/docs/programmable/sdk/`
|
2026-03-16 14:24:45 +00:00
|
|
|
|
2026-03-24 20:25:06 +00:00
|
|
|
## Example app
|
2026-03-16 14:24:45 +00:00
|
|
|
|
|
|
|
|
Run the included example:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
cd packages/vue/example
|
|
|
|
|
bun install
|
|
|
|
|
bun run dev
|
|
|
|
|
```
|