@open-pencil-lowcode/vue
v0.15.0
Published
Headless Vue 3 SDK for building OpenPencil-powered editors.
Readme
@open-pencil-lowcode/vue
Headless Vue 3 SDK for building OpenPencil-powered editors.
@open-pencil-lowcode/vue sits on top of @open-pencil-lowcode/core and provides:
- Vue editor injection via
provideEditor()/useEditor() - canvas integration via
useCanvas(),useCanvasInput(), anduseTextEdit() - selection, command, panel, variables, and i18n composables
- headless structural primitives like
CanvasRoot,LayerTreeRoot,PageListRoot, andToolbarRoot
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-lowcode/vue @open-pencil-lowcode/core canvaskit-wasmQuick start
<script setup lang="ts">
import { createEditor } from '@open-pencil-lowcode/core/editor'
import { provideEditor } from '@open-pencil-lowcode/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-lowcode/vue'
provideEditor(editor)Read it anywhere below with useEditor().
import { useEditor } from '@open-pencil-lowcode/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:
CanvasRootLayerTreeRootPageListRootPropertyListRootPropertySectionRootSegmentedControlRootToolbarRootColorPickerRootFontPickerRootNumberFieldRoot/NumberFieldInput/NumberFieldValueBindableValueRoot/BindableValueTrigger/BindableValuePickerLayoutControlsRootConstraintsControlRoot
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.
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()useAppearance()useSharedStyleBinding()useColorModel()useMask()useTypography()useExport()useFillControls()useStrokeControls()useEffectsControls()
Variables, navigation, and localization
useVariablesEditor()usePageList()useI18n()
Headless primitives
CanvasRootLayerTreeRootPageListRootPropertyListRootPropertyListItemPropertyListAdd/PropertyListRemove/PropertyListVisibilityPropertySectionRoot/PropertySectionHeader/PropertySectionTitlePropertySectionActions/PropertySectionContent/PropertySectionEmptyActionSegmentedControlRoot/SegmentedControlItemToolbarRootNumberFieldRootNumberFieldInputNumberFieldValueNumberFieldLeadingNumberFieldUnitNumberFieldTrailingNumberFieldMenuBindableValueRootBindableValueTriggerBindableValuePickerFillRoot/FillSwatchChannelSliderRoot/ChannelSliderTrack/ChannelSliderThumb
Advanced API
These exports are intentionally public, but they are lower-level or more specialized.
useNodeProps()useEditorPropertyList()useSceneComputed()useColorBindingProvider()useColorVariableBinding()provideBindingProvider()useBindingProvider()useNumberBindingProvider()useFill()useGradientStops()useFontPicker()useOkHCL()useVariables()useVariablesDialogState()useVariablesTable()usePropScrub()useLayerDrag()useInlineRename()useToolbarState()useNodeFontStatus()useCanvasDrop()extractImageFilesFromClipboard()useViewportKind()toolCursor()
Primitive context helpers and low-level stores
These are mostly useful when extending SDK primitives rather than building from top-level composables.
useCanvasContext()useLayerTree()useToolbar()usePropertyList()useNumberField()localelocaleSettingsetLocale()AVAILABLE_LOCALESLOCALE_LABELS
Example patterns
Minimal provider component
<script setup lang="ts">
import { provideEditor } from '@open-pencil-lowcode/vue'
import type { Editor } from '@open-pencil-lowcode/core/editor'
const props = defineProps<{
editor: Editor
}>()
provideEditor(props.editor)
</script>
<template>
<slot />
</template>Read selection state
import { useSelectionState } from '@open-pencil-lowcode/vue'
const { hasSelection, selectedCount, selectedNode } = useSelectionState()Build a menu
import { useMenuModel } from '@open-pencil-lowcode/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