openpencil/packages/vue/README.md

291 lines
7 KiB
Markdown
Raw Permalink Normal View History

# @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.
## Install
```bash
2026-03-24 20:25:06 +00:00
bun add @open-pencil/vue @open-pencil/core canvaskit-wasm
```
## Quick start
```vue
2026-03-24 20:25:06 +00:00
<script setup lang="ts">
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,
})
editor.createShape('RECTANGLE', 100, 100, 200, 150)
editor.zoomToFit()
2026-03-24 20:25:06 +00:00
provideEditor(editor)
</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>
</template>
```
2026-03-24 20:25:06 +00:00
## Core concepts
2026-03-24 20:25:06 +00:00
### Editor context
2026-03-24 20:25:06 +00:00
Use `provideEditor(editor)` once near the top of your subtree.
2026-03-24 20:25:06 +00:00
```ts
import { provideEditor } from '@open-pencil/vue'
2026-03-24 20:25:06 +00:00
provideEditor(editor)
```
2026-03-24 20:25:06 +00:00
Read it anywhere below with `useEditor()`.
2026-03-24 20:25:06 +00:00
```ts
import { useEditor } from '@open-pencil/vue'
2026-03-24 20:25:06 +00:00
const editor = useEditor()
```
2026-03-24 20:25:06 +00:00
### Canvas wiring
2026-03-24 20:25:06 +00:00
At the composable level, the main canvas APIs are:
2026-03-24 20:25:06 +00:00
- `useCanvas()`
- `useCanvasInput()`
- `useTextEdit()`
2026-03-24 20:25:06 +00:00
If you want SDK-provided structure, use headless primitives like `CanvasRoot` and `CanvasSurface`.
2026-03-24 20:25:06 +00:00
### Headless primitives
2026-03-24 20:25:06 +00:00
Main structural primitives include:
2026-03-24 20:25:06 +00:00
- `CanvasRoot`
- `LayerTreeRoot`
- `PageListRoot`
- `PropertyListRoot`
- `PropertySectionRoot`
- `SegmentedControlRoot`
2026-03-24 20:25:06 +00:00
- `ToolbarRoot`
- `ColorPickerRoot`
- `FontPickerRoot`
- `NumberFieldRoot` / `NumberFieldInput` / `NumberFieldValue`
- `BindableValueRoot` / `BindableValueTrigger` / `BindableValuePicker`
- `LayoutControlsRoot`
- `ConstraintsControlRoot`
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
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.
Focusing a bound NumberField is non-destructive; the configured policy begins only on the first
value mutation. `LayoutControlsRoot` exposes axis-oriented sizing actions; editing a Hug or Fill
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`
exposes selection-derived independent-corner presentation state so consumers do not need parallel
expansion heuristics. `PropertyListRoot` is controlled and
editor-agnostic; OpenPencil panels connect it to selection and undo through
`useEditorPropertyList()`. `useColorModel()` provides precise scene-color/Reka bridges, reactive
RGB/HSL/HSB/OkHCL channels, extensible format state, and shared slider presentation data.
`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-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()`
- `useConstraints()`
- `useComponentProperties()`
2026-03-24 20:25:06 +00:00
- `useAppearance()`
- `useSharedStyleBinding()`
- `useColorModel()`
- `useMask()`
2026-03-24 20:25:06 +00:00
- `useTypography()`
- `useExport()`
- `useFillControls()`
- `useStrokeControls()`
- `useEffectsControls()`
2026-03-30 12:11:31 +00:00
#### Variables, navigation, and localization
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`
- `PropertyListItem`
- `PropertyListAdd` / `PropertyListRemove` / `PropertyListVisibility`
- `PropertySectionRoot` / `PropertySectionHeader` / `PropertySectionTitle`
- `PropertySectionActions` / `PropertySectionContent` / `PropertySectionEmptyAction`
- `SegmentedControlRoot` / `SegmentedControlItem`
2026-03-24 20:25:06 +00:00
- `ToolbarRoot`
- `NumberFieldRoot`
- `NumberFieldInput`
- `NumberFieldValue`
- `NumberFieldLeading`
- `NumberFieldUnit`
- `NumberFieldTrailing`
- `NumberFieldMenu`
- `BindableValueRoot`
- `BindableValueTrigger`
- `BindableValuePicker`
- `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()`
- `useEditorPropertyList()`
2026-03-24 20:25:06 +00:00
- `useSceneComputed()`
- `useColorBindingProvider()`
- `useColorVariableBinding()`
- `provideBindingProvider()`
- `useBindingProvider()`
- `useNumberBindingProvider()`
- `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()`
- `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
```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-24 20:25:06 +00:00
### Read selection state
2026-03-24 20:25:06 +00:00
```ts
import { useSelectionState } from '@open-pencil/vue'
const { hasSelection, selectedCount, selectedNode } = useSelectionState()
```
2026-03-24 20:25:06 +00:00
### Build a menu
```ts
2026-03-24 20:25:06 +00:00
import { useMenuModel } from '@open-pencil/vue'
2026-03-24 20:25:06 +00:00
const { appMenu, canvasMenu } = useMenuModel()
```
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-24 20:25:06 +00:00
- `packages/docs/programmable/sdk/`
2026-03-24 20:25:06 +00:00
## Example app
Run the included example:
```bash
cd packages/vue/example
bun install
bun run dev
```