diff --git a/packages/vue/README.md b/packages/vue/README.md index 6ae86215f..ab8a865c0 100644 --- a/packages/vue/README.md +++ b/packages/vue/README.md @@ -1,125 +1,227 @@ # @open-pencil/vue -Headless Vue 3 SDK for embedding an OpenPencil design editor. Renderless components expose logic via scoped slots — bring your own UI. +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 ```bash -npm install @open-pencil/vue @open-pencil/core canvaskit-wasm +bun add @open-pencil/vue @open-pencil/core canvaskit-wasm ``` ## Quick start ```vue - ``` -## Components +## Core concepts -All components are **renderless** — they provide data and actions via scoped slots. You control the markup and styling. +### Editor context -### `` +Use `provideEditor(editor)` once near the top of your subtree. -Provides the editor instance to all child components via Vue injection. +```ts +import { provideEditor } from '@open-pencil/vue' -```vue - - - +provideEditor(editor) ``` -### `` - -Renders the CanvasKit/Skia drawing surface. Handles WebGL/WebGPU setup, resize, and the render loop. - -| Prop | Type | Description | -|------|------|-------------| -| `showRulers` | `boolean?` | Override ruler visibility | -| `preserveDrawingBuffer` | `boolean?` | Keep buffer for screenshots | -| `onReady` | `() => void` | Called when surface is ready | - -### `` - -Exposes page data and navigation. - -```vue - -
- {{ page.name }} -
-
-``` - -### `` - -Exposes the flattened layer tree with selection state. - -```vue - -
- {{ layer.node.name }} -
-
-``` - -### `` - -Exposes the active tool and setter. - -```vue - - - - -``` - -### `` - -Exposes the selected node and an update function. - -```vue - -
- -
-
-``` - -## Composables - -### `useEditor()` - -Access the editor instance from any descendant of ``. +Read it anywhere below with `useEditor()`. ```ts import { useEditor } from '@open-pencil/vue' const editor = useEditor() -editor.createShape('ELLIPSE', 0, 0, 100, 100) ``` -### `useCanvas(canvasRef, editor, options?)` +### Canvas wiring -Low-level composable for CanvasKit surface lifecycle. Used internally by `` — use directly only if you need custom canvas setup. +At the composable level, the main canvas APIs are: -## Example +- `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()` +- `useFillVariableBinding()` +- `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 + +```vue + + + +``` + +### Read selection state + +```ts +import { useSelectionState } from '@open-pencil/vue' + +const { hasSelection, selectedCount, selectedNode } = useSelectionState() +``` + +### Build a menu + +```ts +import { useMenuModel } from '@open-pencil/vue' + +const { appMenu, canvasMenu } = useMenuModel() +``` + +### Build a page list + +```vue + +
    +
  • + +
  • +
+
+``` + +## Documentation + +For fuller guides and API docs, see the documentation site: + +- `packages/docs/programmable/sdk/` + +## Example app Run the included example: diff --git a/packages/vue/src/Canvas/CanvasRoot.vue b/packages/vue/src/Canvas/CanvasRoot.vue index ab76aeeee..b4ccfdb9a 100644 --- a/packages/vue/src/Canvas/CanvasRoot.vue +++ b/packages/vue/src/Canvas/CanvasRoot.vue @@ -2,11 +2,9 @@ import { ref } from 'vue' import { useEditor } from '@open-pencil/vue/context/editorContext' -import { useCanvas } from '@open-pencil/vue/shared/useCanvas' +import { useCanvas, type UseCanvasOptions } from '@open-pencil/vue' import { provideCanvas } from './context' -import type { UseCanvasOptions } from '@open-pencil/vue/shared/useCanvas' - const props = withDefaults(defineProps(), { showRulers: undefined }) diff --git a/packages/vue/src/Canvas/useCanvasInput.ts b/packages/vue/src/Canvas/useCanvasInput.ts index 1427a72ea..153d08a9d 100644 --- a/packages/vue/src/Canvas/useCanvasInput.ts +++ b/packages/vue/src/Canvas/useCanvasInput.ts @@ -29,6 +29,13 @@ import type { DragState } from '@open-pencil/vue/shared/input/types' +/** + * Wires pointer and mouse interaction to an OpenPencil canvas. + * + * This composable coordinates selection, dragging, resizing, rotation, + * panning, drawing tools, scoped hit testing, and text-edit interaction. + * It is primarily intended for editor shell components that own the canvas. + */ export function useCanvasInput( canvasRef: Ref, editor: Editor, diff --git a/packages/vue/src/Canvas/useTextEdit.ts b/packages/vue/src/Canvas/useTextEdit.ts index 383cfbd93..38de89a82 100644 --- a/packages/vue/src/Canvas/useTextEdit.ts +++ b/packages/vue/src/Canvas/useTextEdit.ts @@ -14,6 +14,13 @@ import type { Editor } from '@open-pencil/core/editor' const CARET_BLINK_MS = 530 +/** + * Bridges DOM text input and the editor's canvas text-editing model. + * + * This composable manages textarea-backed input, IME composition, caret + * blinking, keyboard editing behavior, text formatting shortcuts, and syncing + * text/style-run updates back into the scene graph. + */ export function useTextEdit(canvasRef: Ref, store: Editor) { const textareaRef = shallowRef(null) let isComposing = false diff --git a/packages/vue/src/FillPicker/useFillPicker.ts b/packages/vue/src/FillPicker/useFillPicker.ts index 1f3a005af..e0b0255ac 100644 --- a/packages/vue/src/FillPicker/useFillPicker.ts +++ b/packages/vue/src/FillPicker/useFillPicker.ts @@ -19,6 +19,12 @@ function gradientCSS(stops: GradientStop[]): string { return stops.map((s) => `${colorToCSS(s.color)} ${s.position * 100}%`).join(', ') } +/** + * Returns category and conversion helpers for a single fill value. + * + * This composable is useful for fill pickers that switch between solid, + * gradient, and image modes while keeping a live fill model in sync. + */ export function useFillPicker(fill: Ref, onUpdate: (fill: Fill) => void) { const category = computed(() => FILL_CATEGORY[fill.value.type] ?? 'SOLID') diff --git a/packages/vue/src/FontPicker/useFontPicker.ts b/packages/vue/src/FontPicker/useFontPicker.ts index d997d3c33..a9de7ba5b 100644 --- a/packages/vue/src/FontPicker/useFontPicker.ts +++ b/packages/vue/src/FontPicker/useFontPicker.ts @@ -1,12 +1,21 @@ import { useFilter } from 'reka-ui' import { computed, onMounted, ref, watch } from 'vue' +/** + * Options for {@link useFontPicker}. + */ export interface UseFontPickerOptions { + /** Writable model for the selected font family. */ modelValue: { value: string } + /** Async source for available font families. */ listFamilies: () => Promise + /** Optional callback fired after a family is selected. */ onSelect?: (family: string) => void } +/** + * Returns searchable font-picker state and selection helpers. + */ export function useFontPicker(options: UseFontPickerOptions) { const families = ref([]) const searchTerm = ref('') diff --git a/packages/vue/src/GradientEditor/useGradientStops.ts b/packages/vue/src/GradientEditor/useGradientStops.ts index f5b30955e..1c50101a3 100644 --- a/packages/vue/src/GradientEditor/useGradientStops.ts +++ b/packages/vue/src/GradientEditor/useGradientStops.ts @@ -24,6 +24,12 @@ const DEFAULT_TRANSFORMS: Record = { GRADIENT_DIAMOND: { m00: 0.5, m01: 0, m02: 0.5, m10: 0, m11: 0.5, m12: 0.5 } } +/** + * Returns gradient-stop state and mutation helpers for a fill. + * + * Use this composable for gradient editors that need subtype switching, + * active-stop selection, stop dragging, and stop color/opacity editing. + */ export function useGradientStops(fill: Ref, onUpdate: (fill: Fill) => void) { const activeStopIndex = ref(0) const stops = computed(() => fill.value.gradientStops ?? []) diff --git a/packages/vue/src/PageList/usePageList.ts b/packages/vue/src/PageList/usePageList.ts index ef0cdf885..2143ad352 100644 --- a/packages/vue/src/PageList/usePageList.ts +++ b/packages/vue/src/PageList/usePageList.ts @@ -3,6 +3,12 @@ import { computed } from 'vue' import { useEditor } from '@open-pencil/vue/context/editorContext' import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed' +/** + * Returns reactive page state and page-management actions. + * + * Use this composable to build page switchers, page lists, or navigation + * panels without manually reading the graph in each component. + */ export function usePageList() { const editor = useEditor() diff --git a/packages/vue/src/Toolbar/useToolbarState.ts b/packages/vue/src/Toolbar/useToolbarState.ts index f5aa4a402..25175b22f 100644 --- a/packages/vue/src/Toolbar/useToolbarState.ts +++ b/packages/vue/src/Toolbar/useToolbarState.ts @@ -4,6 +4,12 @@ import type { Tool, EditorToolDef } from '@open-pencil/core/editor' const CATEGORY_COUNT = 3 +/** + * Returns responsive toolbar UI state for mobile category paging. + * + * This composable is presentation-oriented and complements {@link useToolbar} + * when building toolbar shells. + */ export function useToolbarState() { const mobileCategory = ref(0) const slideDirection = ref(1) diff --git a/packages/vue/src/VariablesEditor/useVariablesEditor.ts b/packages/vue/src/VariablesEditor/useVariablesEditor.ts index 0f017de91..c09347aa5 100644 --- a/packages/vue/src/VariablesEditor/useVariablesEditor.ts +++ b/packages/vue/src/VariablesEditor/useVariablesEditor.ts @@ -4,10 +4,18 @@ import { computed, type Component } from 'vue' import { useVariablesDialogState } from './useVariablesDialogState' import { useVariablesTable } from './useVariablesTable' +/** + * Composes variables dialog state, table columns, and TanStack table wiring + * into a single higher-level variables editor API. + */ export function useVariablesEditor(options: { + /** Component used for color variable editing. */ colorInput: Component + /** Icon map keyed by variable resolved type. */ icons: Record + /** Fallback icon when no specific icon matches a variable type. */ fallbackIcon: Component + /** Icon used for destructive remove actions. */ deleteIcon: Component }) { const ctx = useVariablesDialogState() diff --git a/packages/vue/src/commands/useEditorCommands.ts b/packages/vue/src/commands/useEditorCommands.ts index eae680c8e..06f886507 100644 --- a/packages/vue/src/commands/useEditorCommands.ts +++ b/packages/vue/src/commands/useEditorCommands.ts @@ -7,6 +7,9 @@ import { useSelectionState } from '@open-pencil/vue/selection/useSelectionState' import type { Component, ComputedRef } from 'vue' +/** + * Stable command identifiers exposed by {@link useEditorCommands}. + */ export type EditorCommandId = | 'edit.undo' | 'edit.redo' @@ -30,10 +33,17 @@ export type EditorCommandId = | 'view.zoomFit' | 'view.zoomSelection' +/** + * Reactive editor command descriptor. + */ export interface EditorCommand { + /** Stable command id. */ id: EditorCommandId + /** Human-readable label for UI. */ label: string + /** Whether the command can currently run. */ enabled: ComputedRef + /** Executes the command. */ run: () => void } @@ -53,6 +63,13 @@ export interface EditorCommandMenuSeparator { export type EditorCommandMenuEntry = EditorCommandMenuItem | EditorCommandMenuSeparator +/** + * Builds a command-oriented interface on top of the current editor. + * + * Use this composable when building menus, toolbars, keyboard handlers, or + * any other UI that should talk in terms of commands instead of raw editor + * method calls. + */ export function useEditorCommands() { const editor = useEditor() const selection = useSelectionState() diff --git a/packages/vue/src/commands/useMenuModel.ts b/packages/vue/src/commands/useMenuModel.ts index 6b560398b..9735065b9 100644 --- a/packages/vue/src/commands/useMenuModel.ts +++ b/packages/vue/src/commands/useMenuModel.ts @@ -4,6 +4,9 @@ import { useEditorCommands } from '@open-pencil/vue/commands/useEditorCommands' import { useEditor } from '@open-pencil/vue/context/editorContext' import { useSelectionState } from '@open-pencil/vue/selection/useSelectionState' +/** + * Action entry used by menu models returned from {@link useMenuModel}. + */ export interface MenuActionNode { separator?: false label: string @@ -21,6 +24,13 @@ export interface MenuSeparatorNode { export type MenuEntry = MenuActionNode | MenuSeparatorNode +/** + * Returns ready-to-render menu models derived from the current editor state. + * + * This is a higher-level API than {@link useEditorCommands}: it groups + * commands into app and canvas menu structures and computes context-sensitive + * labels like Hide/Show and Lock/Unlock. + */ export function useMenuModel() { const editor = useEditor() const { menuItem: commandMenuItem, otherPages, moveSelectionToPage } = useEditorCommands() diff --git a/packages/vue/src/context/editorContext.ts b/packages/vue/src/context/editorContext.ts index 2a8198587..76e3e8a5f 100644 --- a/packages/vue/src/context/editorContext.ts +++ b/packages/vue/src/context/editorContext.ts @@ -3,12 +3,30 @@ import { inject, provide } from 'vue' import type { Editor } from '@open-pencil/core/editor' import type { InjectionKey } from 'vue' +/** + * Injection key for the current OpenPencil editor instance. + * + * Most SDK consumers should use {@link provideEditor} and {@link useEditor} + * instead of interacting with this symbol directly. + */ export const EDITOR_KEY: InjectionKey = Symbol('open-pencil-editor') +/** + * Provides an OpenPencil editor instance to the current Vue subtree. + * + * Call this once near the top of your editor shell so descendant composables + * and headless primitives can access the editor with {@link useEditor}. + */ export function provideEditor(editor: Editor) { provide(EDITOR_KEY, editor) } +/** + * Returns the current injected OpenPencil editor. + * + * Throws if called outside a subtree where {@link provideEditor} has already + * been called. + */ export function useEditor(): Editor { const editor = inject(EDITOR_KEY) if (!editor) { diff --git a/packages/vue/src/controls/useAppearance.ts b/packages/vue/src/controls/useAppearance.ts index 384df6b14..32244612a 100644 --- a/packages/vue/src/controls/useAppearance.ts +++ b/packages/vue/src/controls/useAppearance.ts @@ -13,6 +13,12 @@ const CORNER_RADIUS_TYPES = new Set([ 'INSTANCE' ]) +/** + * Returns appearance-related state and actions for the current selection. + * + * Use this composable for visibility, opacity, and corner-radius controls in + * property panels. + */ export function useAppearance() { const editor = useEditor() const { nodes, node, active, isMulti, merged, updateProp, commitProp } = useNodeProps() diff --git a/packages/vue/src/controls/useEffectsControls.ts b/packages/vue/src/controls/useEffectsControls.ts index 90f2f411d..75f483ffe 100644 --- a/packages/vue/src/controls/useEffectsControls.ts +++ b/packages/vue/src/controls/useEffectsControls.ts @@ -16,6 +16,12 @@ const EFFECT_LABELS: Record = { const EFFECT_TYPES = Object.keys(EFFECT_LABELS) as EffectType[] +/** + * Returns effect-editing helpers for property panels. + * + * This composable manages default effect creation, expanded-row state, + * scrub-preview behavior, and effect type/color updates. + */ export function useEffectsControls() { const editor = useEditor() diff --git a/packages/vue/src/controls/useExport.ts b/packages/vue/src/controls/useExport.ts index 28416a312..980a6be3f 100644 --- a/packages/vue/src/controls/useExport.ts +++ b/packages/vue/src/controls/useExport.ts @@ -5,14 +5,25 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed' import type { ExportFormat } from '@open-pencil/core' +/** + * Single export preset row managed by {@link useExport}. + */ interface ExportSetting { + /** Export scale multiplier. */ scale: number + /** Output file format. */ format: ExportFormat } const SCALES = [0.5, 0.75, 1, 1.5, 2, 3, 4] as const const FORMATS: ExportFormat[] = ['PNG', 'JPG', 'WEBP', 'SVG'] +/** + * Returns selection-aware export settings for export panel UIs. + * + * This composable manages export presets such as scale and format while also + * exposing the current export target label derived from the selection. + */ export function useExport() { const editor = useEditor() diff --git a/packages/vue/src/controls/useFillControls.ts b/packages/vue/src/controls/useFillControls.ts index 614a4d0b9..0716f14b7 100644 --- a/packages/vue/src/controls/useFillControls.ts +++ b/packages/vue/src/controls/useFillControls.ts @@ -2,6 +2,12 @@ import { DEFAULT_SHAPE_FILL } from '@open-pencil/core' import { useFillVariableBinding } from './useFillVariableBinding' +/** + * Returns fill-related panel helpers and a reusable default fill value. + * + * This composable extends variable-binding behavior with SDK-level defaults for + * fill editing UIs. + */ export function useFillControls() { const ctx = useFillVariableBinding() diff --git a/packages/vue/src/controls/useFillVariableBinding.ts b/packages/vue/src/controls/useFillVariableBinding.ts index 023e52b91..43634687f 100644 --- a/packages/vue/src/controls/useFillVariableBinding.ts +++ b/packages/vue/src/controls/useFillVariableBinding.ts @@ -5,6 +5,12 @@ import { useEditor } from '@open-pencil/vue/context/editorContext' import type { Variable } from '@open-pencil/core' +/** + * Returns helpers for binding fill colors to color variables. + * + * This composable is used by fill editing UIs that need variable search, + * binding, and unbinding behavior. + */ export function useFillVariableBinding() { const store = useEditor() const colorVariables = computed(() => store.getVariablesByType('COLOR')) diff --git a/packages/vue/src/controls/useLayout.ts b/packages/vue/src/controls/useLayout.ts index 7e2a98348..b20e1a019 100644 --- a/packages/vue/src/controls/useLayout.ts +++ b/packages/vue/src/controls/useLayout.ts @@ -44,6 +44,12 @@ const TRACK_SIZING_OPTIONS: { value: GridTrackSizing; label: string }[] = [ { value: 'AUTO', label: 'Auto' } ] +/** + * Returns layout-related state and actions for the current selection. + * + * Use this composable to build auto-layout and grid panels that need sizing, + * padding, alignment, and track editing behavior. + */ export function useLayout() { const editor = useEditor() diff --git a/packages/vue/src/controls/useNodeProps.ts b/packages/vue/src/controls/useNodeProps.ts index 04382ccdb..057c1d52e 100644 --- a/packages/vue/src/controls/useNodeProps.ts +++ b/packages/vue/src/controls/useNodeProps.ts @@ -5,11 +5,20 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed' import type { Effect, Fill, SceneNode, Stroke } from '@open-pencil/core' +/** Sentinel value returned when a property differs across multiple selected nodes. */ export const MIXED = Symbol('mixed') + +/** Property value that may either be concrete or mixed across the selection. */ export type MixedValue = T | typeof MIXED type ArrayItem = Fill | Stroke | Effect | Record +/** + * Returns shared property-panel helpers for the current selection. + * + * This composable centralizes mixed-value detection, multi-selection updates, + * array-item editing, and commit semantics used by higher-level controls. + */ export function useNodeProps() { const store = useEditor() const node = useSceneComputed(() => store.getSelectedNode() ?? null) diff --git a/packages/vue/src/controls/usePosition.ts b/packages/vue/src/controls/usePosition.ts index 35f945cd4..7662d2812 100644 --- a/packages/vue/src/controls/usePosition.ts +++ b/packages/vue/src/controls/usePosition.ts @@ -6,6 +6,12 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed' import type { SceneNode } from '@open-pencil/core' +/** + * Returns position-related state and actions for the current selection. + * + * This composable is designed for property panels that edit x/y, size, + * rotation, alignment, flipping, and multi-node transforms. + */ export function usePosition() { const editor = useEditor() diff --git a/packages/vue/src/controls/useStrokeControls.ts b/packages/vue/src/controls/useStrokeControls.ts index 7904a23dc..3fdf2d555 100644 --- a/packages/vue/src/controls/useStrokeControls.ts +++ b/packages/vue/src/controls/useStrokeControls.ts @@ -30,6 +30,12 @@ const DEFAULT_STROKE: Stroke = { align: 'CENTER' } +/** + * Returns stroke-related helpers for property panels. + * + * This composable provides alignment options, side presets, a default stroke, + * and helpers for per-side border weight editing. + */ export function useStrokeControls() { const store = useEditor() const sideMenuOpen = ref(false) diff --git a/packages/vue/src/controls/useTypography.ts b/packages/vue/src/controls/useTypography.ts index ad589399f..392460968 100644 --- a/packages/vue/src/controls/useTypography.ts +++ b/packages/vue/src/controls/useTypography.ts @@ -14,10 +14,21 @@ const WEIGHTS = Object.entries(FONT_WEIGHT_NAMES).map(([value, label]) => ({ label })) +/** + * Options for {@link useTypography}. + */ export interface UseTypographyOptions { + /** + * Optional font loader invoked before changing family or weight. + */ loadFont?: (family: string, style: string) => Promise } +/** + * Returns typography-related state and actions for the current text selection. + * + * This composable is designed for text property panels and formatting controls. + */ export function useTypography(options: UseTypographyOptions = {}) { const editor = useEditor() diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 0d3b97155..9f8b29863 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -7,22 +7,35 @@ export type { } from '@open-pencil/core/editor' export { createEditor, EDITOR_TOOLS, TOOL_SHORTCUTS } from '@open-pencil/core/editor' +/** + * Public editor-context API for the Vue SDK. + * + * These are the primary entry points for making an editor available to a Vue + * subtree and reading it back inside composables and headless primitives. + */ export { provideEditor, useEditor, EDITOR_KEY } from './context/editorContext' +/** Canvas and input integration composables. */ export { useCanvas } from './shared/useCanvas' export type { UseCanvasOptions } from './shared/useCanvas' export { useCanvasInput } from './Canvas/useCanvasInput' export { useTextEdit } from './Canvas/useTextEdit' export { useCanvasDrop, extractImageFilesFromClipboard } from './Canvas/useCanvasDrop' + +/** Low-level selection, graph, and derived-state helpers. */ export { useNodeProps, MIXED } from './controls/useNodeProps' export type { MixedValue } from './controls/useNodeProps' export { useSceneComputed } from './internal/useSceneComputed' export { useSelectionState } from './selection/useSelectionState' export { useSelectionCapabilities } from './selection/useSelectionCapabilities' + +/** Command and menu composition helpers. */ export { useEditorCommands } from './commands/useEditorCommands' export type { EditorCommand, EditorCommandId } from './commands/useEditorCommands' export { useMenuModel } from './commands/useMenuModel' export type { MenuEntry } from './commands/useMenuModel' + +/** Miscellaneous editor-shell helpers. */ export { useViewportKind } from './viewport/useViewportKind' export { useLayerDrag } from './LayerTree/useLayerDrag' export { useInlineRename } from './shared/useInlineRename' @@ -30,25 +43,30 @@ export { useToolbarState } from './Toolbar/useToolbarState' export { useNodeFontStatus } from './shared/useFontStatus' export { usePropScrub } from './controls/usePropScrub' export { toolCursor } from './internal/toolCursor' + +/** Property-panel composables. */ export { usePosition } from './controls/usePosition' export { useLayout } from './controls/useLayout' export { useAppearance } from './controls/useAppearance' export { useTypography } from './controls/useTypography' export type { UseTypographyOptions } from './controls/useTypography' export { useExport } from './controls/useExport' +export { useFillControls } from './controls/useFillControls' +export { useFillVariableBinding } from './controls/useFillVariableBinding' +export { useEffectsControls } from './controls/useEffectsControls' +export { useStrokeControls } from './controls/useStrokeControls' + +/** Variables, page navigation, and picker helpers. */ export { useVariables } from './VariablesEditor/useVariables' export { useVariablesDialogState } from './VariablesEditor/useVariablesDialogState' export { useVariablesEditor } from './VariablesEditor/useVariablesEditor' export { useVariablesTable } from './VariablesEditor/useVariablesTable' -export { useFillControls } from './controls/useFillControls' -export { useFillPicker } from './FillPicker/useFillPicker' -export { useFillVariableBinding } from './controls/useFillVariableBinding' -export { useEffectsControls } from './controls/useEffectsControls' -export { useGradientStops } from './GradientEditor/useGradientStops' -export { useStrokeControls } from './controls/useStrokeControls' export { usePageList } from './PageList/usePageList' +export { useFillPicker } from './FillPicker/useFillPicker' +export { useGradientStops } from './GradientEditor/useGradientStops' export { useFontPicker } from './FontPicker/useFontPicker' +/** Headless structural primitives and their local contexts. */ export { CanvasRoot, CanvasSurface, useCanvasContext } from './Canvas' export type { CanvasContext } from './Canvas' export { ColorInputRoot, ColorPickerRoot } from './ColorPicker' diff --git a/packages/vue/src/internal/useSceneComputed.ts b/packages/vue/src/internal/useSceneComputed.ts index 977f99518..9a39ba552 100644 --- a/packages/vue/src/internal/useSceneComputed.ts +++ b/packages/vue/src/internal/useSceneComputed.ts @@ -1,5 +1,11 @@ import { computed, type ComputedRef } from 'vue' +/** + * Convenience wrapper for scene-derived computed state. + * + * Use this for values that should clearly read as editor/scene-backed derived + * state in higher-level composables. + */ export function useSceneComputed(fn: () => T): ComputedRef { return computed(fn) } diff --git a/packages/vue/src/selection/useSelectionCapabilities.ts b/packages/vue/src/selection/useSelectionCapabilities.ts index 17cdca199..fac4514aa 100644 --- a/packages/vue/src/selection/useSelectionCapabilities.ts +++ b/packages/vue/src/selection/useSelectionCapabilities.ts @@ -3,6 +3,13 @@ import { computed } from 'vue' import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed' import { useSelectionState } from '@open-pencil/vue/selection/useSelectionState' +/** + * Returns reactive booleans describing which selection-dependent actions are + * currently available. + * + * This is useful for menus, toolbars, shortcuts, and action buttons that need + * command-friendly capability checks. + */ export function useSelectionCapabilities() { const { editor, diff --git a/packages/vue/src/selection/useSelectionState.ts b/packages/vue/src/selection/useSelectionState.ts index be7987482..6ba6711a0 100644 --- a/packages/vue/src/selection/useSelectionState.ts +++ b/packages/vue/src/selection/useSelectionState.ts @@ -5,6 +5,12 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed' import type { SceneNode } from '@open-pencil/core' +/** + * Returns reactive selection-derived state for the current editor. + * + * Use this composable to drive UI from the current selection without manually + * reading graph state in every component. + */ export function useSelectionState() { const editor = useEditor() diff --git a/packages/vue/src/shared/useCanvas.ts b/packages/vue/src/shared/useCanvas.ts index 3a23cad78..28afa4e1e 100644 --- a/packages/vue/src/shared/useCanvas.ts +++ b/packages/vue/src/shared/useCanvas.ts @@ -39,12 +39,36 @@ async function initWebGPU(ck: CanvasKit): Promise { return { device, deviceContext } } +/** + * Options for {@link useCanvas}. + */ export interface UseCanvasOptions { + /** + * Forces ruler visibility on or off for this canvas. + * + * When omitted, the composable falls back to viewport and URL-param logic. + */ showRulers?: boolean + /** + * Keeps the drawing buffer after presenting frames. + * + * Useful for screenshot or pixel-readback workflows, but may increase memory + * usage depending on the browser and GPU backend. + */ preserveDrawingBuffer?: boolean + /** + * Called once the rendering surface is ready. + */ onReady?: () => void } +/** + * Connects an OpenPencil editor to a real canvas element using CanvasKit. + * + * This composable owns renderer creation, surface recreation on resize, + * render scheduling, and renderer-backed hit testing helpers used by higher- + * level canvas interaction code. + */ export function useCanvas( canvasRef: Ref, editor: Editor, diff --git a/packages/vue/src/shared/useFontStatus.ts b/packages/vue/src/shared/useFontStatus.ts index 5d4a58ef9..aea91eb5b 100644 --- a/packages/vue/src/shared/useFontStatus.ts +++ b/packages/vue/src/shared/useFontStatus.ts @@ -4,6 +4,12 @@ import { isFontLoaded, DEFAULT_FONT_FAMILY } from '@open-pencil/core' import type { SceneNode } from '@open-pencil/core' +/** + * Returns missing-font information for a text node getter. + * + * This is useful for typography panels and warnings that need to surface fonts + * that are referenced by a node but not yet loaded in the current runtime. + */ export function useNodeFontStatus(node: () => SceneNode | null | undefined) { const missingFonts = computed(() => { const n = node() diff --git a/packages/vue/src/viewport/useViewportKind.ts b/packages/vue/src/viewport/useViewportKind.ts index 68b9a1d65..96d31b541 100644 --- a/packages/vue/src/viewport/useViewportKind.ts +++ b/packages/vue/src/viewport/useViewportKind.ts @@ -3,6 +3,9 @@ import { computed } from 'vue' const breakpoints = useBreakpoints({ mobile: 768 }) +/** + * Returns coarse viewport kind flags used by responsive editor UI. + */ export function useViewportKind() { const isMobile = breakpoints.smaller('mobile') const isDesktop = computed(() => !isMobile.value)