* perf(canvas): add traced navigation benchmarks - Record and replay timestamped pan and zoom gestures through DOM and CDP input paths\n- Correlate input, viewport, render, long-task, and retained-backing events in Chromium traces\n- Report frame pacing, latency, jump, anchor drift, and crisp-settlement metrics * perf(canvas): stabilize navigation comparisons - Separate low-overhead metric runs from optional CPU-profile traces\n- Warm scenarios before recording and use a consistent SwiftShader browser configuration\n- Add a canonical momentum-pan reversal gesture alongside pinch reversal * fix(canvas): require hardware GPU navigation benchmarks - Run macOS performance captures through Metal-backed ANGLE and reject accidental SwiftShader fallback\n- Record the GL renderer and reserve software GPU mode for portable correctness smoke runs * perf(canvas): cache shadow rasters for crisp backing - Rasterize local drop and inner shadows only while constructing retained scene backing\n- Bound native image memory and invalidate cached entries with node and renderer lifecycle changes\n- Quantize zoom-aware raster resolution and reuse nearby scales without lowering normal scene quality * test(canvas): verify retained shadow raster fidelity - Compare settled retained-backing shadow output with direct CanvasKit rendering\n- Keep backdrop blur on the picture fallback and exercise graph-driven cache invalidation\n- Cover updates, deletion, and reparenting through actual SceneGraph events * perf(canvas): benchmark real FIG fixtures - Serve exact local fixture bytes through an isolated Playwright route for production preview runs\n- Wait for document loading and page population before zooming to fit and recording navigation\n- Record the resolved fixture path in benchmark environment artifacts * fix(canvas): preserve nested effect subtree pictures - Keep deeply nested shadow documents on one retained subtree picture instead of exploding them into per-node image draws\n- Restrict shadow raster acceleration to effect-bearing page children\n- Cover nested shadow fallback and restore gold-preview FIG pinch performance to master levels * refactor(canvas): share recorded wheel sample type * perf(canvas): defer backing settlement across zoom reversals - Track explicit navigation phases and gesture generations instead of inferring idle from viewport timing\n- Cancel or defer retained backing construction while pan, zoom, momentum, or tentative settlement is active\n- Add a repeated short-pause pinch reversal fixture based on the user trace * perf(canvas): index bounded render chunks - Split oversized painter subtrees into self-paint and bounded descendant chunks without dropping container visuals\n- Bulk-load chunk visual bounds into RBush for selective world-space queries\n- Cover bounded updates and gold-preview build/query complexity before tile rendering consumes the index * refactor(canvas): namespace render chunk coverage * perf(canvas): model chunk paint context - Preserve ancestor transform and clip dependencies for independently renderable chunks\n- Keep opacity, blend, blur, and mask isolation subtrees atomic until command-level splitting exists\n- Report oversized atomic chunks and lock gold-preview to bounded painter units * perf(canvas): record pixel-correct render chunks - Record interruptible chunks in world coordinates with ancestor transforms, clips, and chunk-local culling bounds\n- Draw opacity, blend, blur, and mask isolation chunks directly into destination surfaces in painter order\n- Compare composited chunk output with direct CanvasKit rendering instead of relaxing visual thresholds * perf(canvas): render selective world tiles - Map world regions to fixed 256-device-pixel tile targets and quantized sharpness levels\n- Query only intersecting render chunks and preserve atomic destination compositing\n- Match multi-tile CanvasKit output against direct rendering and measure gold-preview tile cost * perf(canvas): cache chunk pictures across tiles - Reuse world-space chunk command pictures for every intersecting tile\n- Pool 256-pixel tile surfaces and expose allocation, draw, flush, and snapshot timings\n- Keep expensive atomic foreground blur visible as an over-budget scheduler constraint * perf(canvas): schedule cached tile rendering - Bound tile images with an LRU cache and reuse pooled CanvasKit surfaces\n- Plan mandatory holes, stale visible refreshes, and overscan by navigation and content generation\n- Stop jobs at a strict deadline while reporting fallbacks, stale work, overruns, and over-budget effects * perf(canvas): integrate progressive tiled rendering - Keep retained scene output as the interaction fallback while exact tiles refine only after navigation becomes idle - Centralize runtime URL flags and pass renderer selection through the typed Vue canvas API - Replace benchmark sleeps with explicit mode-aware renderer settlement and report exact tiled coverage - Preserve bounded scheduler metrics, generation cancellation, native resource cleanup, and shared visual-bounds logic * refactor(app): centralize runtime query configuration - Parse collaboration, recent-files, benchmark, presentation, and renderer flags in one typed app module - Remove ad hoc URL parsing from workspace and collaboration runtime consumers - Cover supported values and production-safe defaults without adding a repository lint rule * fix(canvas): replace fallback pixels with exact tiles - Render opaque page-background tile cells and install them with source replacement instead of double-compositing translucent scene content - Exercise the live progressive controller against direct rendering across masks, effects, blend isolation, images, fallback text, transforms, and clipping - Preserve the bounded reversal path with zero Long Tasks and exact settlement near 128 ms on gold-preview.fig * perf(canvas): invalidate tiled content selectively - Index chunk dependencies across contained nodes and transform or clipping ancestors - Re-record affected chunk pictures and invalidate tiles intersecting old or new visual bounds - Advance unaffected cached tiles to the new scene generation instead of rebuilding the full chunk index and tile cache - Keep structural graph mutations on the safe full-rebuild path and cover selective refresh end to end * perf(canvas): bound atomic blur tile refresh - Render atomic blur chunks with tile-local isolation bounds and blur halos instead of replaying full-subtree layers - Keep content refresh behind the retained fallback, cap GPU submissions to four tile jobs per frame, and adapt estimates from measured work - Preserve large-radius CPU over-budget visibility while preventing Metal-backed refresh bursts and deferred GPU overload - Add deterministic node-mutation benchmarks and summarize scheduler throughput, job duration, overruns, and exact content settlement * perf(canvas): cancel obsolete tile refresh generations - Count and report queued jobs removed by content or navigation generation changes - Add deterministic mutation-then-reversal benchmark support without sleeps - Assert exact tile work remains suspended during navigation and resumes for the final viewport - Summarize cancellation alongside scheduler throughput, overruns, and settlement metrics * test(canvas): cover live tiled blur settlement - Load gold-preview.fig through the real tiled canvas surface and wait on explicit renderer settlement - Commit the settled radius-210 large-blur browser snapshot - Replay the canonical zoom reversal during refresh and require byte-identical final canvas convergence * fix(canvas): harden renderer resource lifecycle - Release tiled surfaces, images, pictures, and queued work across surface, font, graph, page, structure, and renderer lifecycle boundaries - Restore pooled canvas, viewport, and backing state through exception-safe native recording and raster paths - Rebuild tiled chunk topology only when isolation requirements actually change, preserving selective blur mutation performance - Document deterministic active-renderer settlement and add lifecycle, graph replacement, cache failure, and surface replacement regressions * test(canvas): remove source-matching renderer claims - Delete the autopsy suite that inferred runtime correctness from source text, regexes, line placement, and symbol counts - Keep renderer ordering, cache cleanup, effect behavior, and pixel fidelity covered by executable behavioral and lifecycle tests * perf(canvas): present retained backing during tiled navigation - Profile production reversal traces and attribute tiled p95 cost to GPU command-buffer flushes from full-scene fallback replay and tile presentation - Use the retained backing as the moving fallback while tile scheduling and cached lookup remain allocation-free - Defer tile image presentation until idle and expose visible versus presented tile counts in navigation telemetry - Reduce tiled reversal render p95 from about 8ms to 0.3ms while preserving exact idle replacement and visual parity * perf(canvas): prioritize visible tile settlement - Profile per-tile allocation, draw, flush, snapshot, and chunk costs through scheduler telemetry - Defer overscan until all visible exact tiles are covered - Replace the four-job idle cap with a higher safety ceiling while the measured five-millisecond deadline controls cheap work - Reduce mutation-plus-reversal exact settlement from about 272ms to 160ms without Long Tasks, overruns, or over-budget jobs * refactor(canvas): clarify renderer lifecycle boundaries - Extract retained backing state types and navigation preview timing\n- Isolate tiled scheduler telemetry from frame orchestration\n- Document settlement and CanvasKit ownership invariants\n- Preserve hot drawing loops, budgets, cache limits, and rendering decisions * fix(canvas): preserve current label rendering Retain the merged paragraph-label cache lifecycle and substituted-font readiness while reconstructing the renderer stack on current master. * test(canvas): keep tile benchmark assertions deterministic Keep performance timing in benchmark telemetry while asserting structural tile selectivity and cache behavior in CI. * feat(canvas): expose experimental tiled rendering - Persist retained or tiled canvas mode in General settings\n- Keep retained rendering as the default and apply changes after reload\n- Preserve URL overrides for deterministic benchmarks and support reproduction * refactor(app): centralize renderer preference state Expose renderer override provenance from runtime configuration and keep the settings control's derived state separate from its explicit persistence action. * refactor(app): share settings layout anatomy Reuse slot-based section headers and bordered groups while keeping each settings control row explicit. * fix(canvas): harden tiled renderer boundaries - Bound low-zoom tile planning and handle failed tile surface allocation\n- Preserve effect raster dependencies, runtime-safe clocks, and navigation timing contracts\n- Keep benchmarks deterministic, backward compatible, and accurately localized * fix(canvas): invalidate dependent node pictures Track first-child shadow dependencies for retained node pictures so child geometry updates cannot leave stale parent shadows. |
||
|---|---|---|
| .. | ||
| example | ||
| src | ||
| ARCHITECTURE.md | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsdown.config.ts | ||
@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(), 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/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:
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/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