From 99e646aaa4d0deb7f18b2bf89e1e3167ae21c9a8 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Tue, 24 Mar 2026 23:24:08 +0300 Subject: [PATCH] Refine docs structure for SDK and automation --- AGENTS.md | 4 +- README.md | 3 +- bun.lock | 8 + packages/docs/.vitepress/config.ts | 174 ++++++++++++++++-- .../.vitepress/theme/components/SdkCard.vue | 51 +++++ .../theme/components/SdkCardGroup.vue | 13 ++ .../theme/components/SdkEventsTable.vue | 89 +++++++++ .../.vitepress/theme/components/SdkField.vue | 18 ++ .../theme/components/SdkFieldGroup.vue | 5 + .../theme/components/SdkPropsTable.vue | 98 ++++++++++ .../theme/components/SdkRelatedLinks.vue | 18 ++ .../theme/components/SdkSlotsTable.vue | 88 +++++++++ packages/docs/.vitepress/theme/index.ts | 19 ++ packages/docs/guide/getting-started.md | 2 + packages/docs/index.md | 7 +- packages/docs/package.json | 3 + packages/docs/programmable/index.md | 20 +- .../extract-image-files-from-clipboard.md | 15 ++ .../programmable/sdk/api/advanced/index.md | 39 ++++ .../sdk/api/advanced/tool-cursor.md | 15 ++ .../sdk/api/advanced/use-canvas-context.md | 15 ++ .../sdk/api/advanced/use-canvas-drop.md | 16 ++ .../sdk/api/advanced/use-fill-picker.md | 16 ++ .../api/advanced/use-fill-variable-binding.md | 15 ++ .../sdk/api/advanced/use-font-picker.md | 15 ++ .../sdk/api/advanced/use-gradient-stops.md | 16 ++ .../sdk/api/advanced/use-inline-rename.md | 15 ++ .../sdk/api/advanced/use-layer-drag.md | 16 ++ .../sdk/api/advanced/use-layer-tree.md | 15 ++ .../sdk/api/advanced/use-node-font-status.md | 15 ++ .../sdk/api/advanced/use-node-props.md | 17 ++ .../sdk/api/advanced/use-prop-scrub.md | 15 ++ .../sdk/api/advanced/use-property-list.md | 15 ++ .../sdk/api/advanced/use-scene-computed.md | 16 ++ .../sdk/api/advanced/use-scrub-input.md | 16 ++ .../sdk/api/advanced/use-toolbar-state.md | 16 ++ .../sdk/api/advanced/use-toolbar.md | 16 ++ .../components/appearance-controls-root.md | 15 ++ .../sdk/api/components/canvas-root.md | 16 ++ .../sdk/api/components/canvas-surface.md | 16 ++ .../sdk/api/components/color-input-root.md | 47 +++++ .../sdk/api/components/color-picker-root.md | 59 ++++++ .../sdk/api/components/fill-picker-root.md | 73 ++++++++ .../sdk/api/components/font-picker-root.md | 67 +++++++ .../sdk/api/components/gradient-editor-bar.md | 70 +++++++ .../api/components/gradient-editor-root.md | 75 ++++++++ .../api/components/gradient-editor-stop.md | 70 +++++++ .../programmable/sdk/api/components/index.md | 46 +++++ .../sdk/api/components/layer-tree-item.md | 16 ++ .../sdk/api/components/layer-tree-root.md | 14 ++ .../api/components/layout-controls-root.md | 15 ++ .../sdk/api/components/page-list-root.md | 40 ++++ .../api/components/position-controls-root.md | 15 ++ .../sdk/api/components/property-list-item.md | 16 ++ .../sdk/api/components/property-list-root.md | 36 ++++ .../sdk/api/components/scrub-input-display.md | 33 ++++ .../sdk/api/components/scrub-input-field.md | 33 ++++ .../sdk/api/components/scrub-input-root.md | 69 +++++++ .../sdk/api/components/toolbar-item.md | 16 ++ .../sdk/api/components/toolbar-root.md | 15 ++ .../components/typography-controls-root.md | 16 ++ .../programmable/sdk/api/composables/index.md | 39 ++++ .../sdk/api/composables/provide-editor.md | 46 +++++ .../sdk/api/composables/use-appearance.md | 56 ++++++ .../sdk/api/composables/use-canvas-input.md | 73 ++++++++ .../sdk/api/composables/use-canvas.md | 100 ++++++++++ .../api/composables/use-editor-commands.md | 90 +++++++++ .../sdk/api/composables/use-editor.md | 73 ++++++++ .../api/composables/use-effects-controls.md | 50 +++++ .../sdk/api/composables/use-export.md | 57 ++++++ .../sdk/api/composables/use-fill-controls.md | 38 ++++ .../sdk/api/composables/use-layout.md | 63 +++++++ .../sdk/api/composables/use-menu-model.md | 54 ++++++ .../sdk/api/composables/use-page-list.md | 50 +++++ .../sdk/api/composables/use-position.md | 64 +++++++ .../composables/use-selection-capabilities.md | 61 ++++++ .../api/composables/use-selection-state.md | 77 ++++++++ .../api/composables/use-stroke-controls.md | 47 +++++ .../sdk/api/composables/use-text-edit.md | 46 +++++ .../sdk/api/composables/use-typography.md | 64 +++++++ .../api/composables/use-variables-editor.md | 44 +++++ packages/docs/programmable/sdk/api/index.md | 20 ++ .../docs/programmable/sdk/architecture.md | 115 ++++++++++++ .../docs/programmable/sdk/getting-started.md | 124 +++++++++++++ .../sdk/guides/custom-editor-shell.md | 105 +++++++++++ .../sdk/guides/navigation-panels.md | 60 ++++++ .../sdk/guides/property-panels.md | 83 +++++++++ packages/docs/programmable/sdk/index.md | 59 ++++++ 88 files changed, 3573 insertions(+), 27 deletions(-) create mode 100644 packages/docs/.vitepress/theme/components/SdkCard.vue create mode 100644 packages/docs/.vitepress/theme/components/SdkCardGroup.vue create mode 100644 packages/docs/.vitepress/theme/components/SdkEventsTable.vue create mode 100644 packages/docs/.vitepress/theme/components/SdkField.vue create mode 100644 packages/docs/.vitepress/theme/components/SdkFieldGroup.vue create mode 100644 packages/docs/.vitepress/theme/components/SdkPropsTable.vue create mode 100644 packages/docs/.vitepress/theme/components/SdkRelatedLinks.vue create mode 100644 packages/docs/.vitepress/theme/components/SdkSlotsTable.vue create mode 100644 packages/docs/programmable/sdk/api/advanced/extract-image-files-from-clipboard.md create mode 100644 packages/docs/programmable/sdk/api/advanced/index.md create mode 100644 packages/docs/programmable/sdk/api/advanced/tool-cursor.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-canvas-context.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-canvas-drop.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-fill-picker.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-fill-variable-binding.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-font-picker.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-gradient-stops.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-inline-rename.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-layer-drag.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-layer-tree.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-node-font-status.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-node-props.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-prop-scrub.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-property-list.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-scene-computed.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-scrub-input.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-toolbar-state.md create mode 100644 packages/docs/programmable/sdk/api/advanced/use-toolbar.md create mode 100644 packages/docs/programmable/sdk/api/components/appearance-controls-root.md create mode 100644 packages/docs/programmable/sdk/api/components/canvas-root.md create mode 100644 packages/docs/programmable/sdk/api/components/canvas-surface.md create mode 100644 packages/docs/programmable/sdk/api/components/color-input-root.md create mode 100644 packages/docs/programmable/sdk/api/components/color-picker-root.md create mode 100644 packages/docs/programmable/sdk/api/components/fill-picker-root.md create mode 100644 packages/docs/programmable/sdk/api/components/font-picker-root.md create mode 100644 packages/docs/programmable/sdk/api/components/gradient-editor-bar.md create mode 100644 packages/docs/programmable/sdk/api/components/gradient-editor-root.md create mode 100644 packages/docs/programmable/sdk/api/components/gradient-editor-stop.md create mode 100644 packages/docs/programmable/sdk/api/components/index.md create mode 100644 packages/docs/programmable/sdk/api/components/layer-tree-item.md create mode 100644 packages/docs/programmable/sdk/api/components/layer-tree-root.md create mode 100644 packages/docs/programmable/sdk/api/components/layout-controls-root.md create mode 100644 packages/docs/programmable/sdk/api/components/page-list-root.md create mode 100644 packages/docs/programmable/sdk/api/components/position-controls-root.md create mode 100644 packages/docs/programmable/sdk/api/components/property-list-item.md create mode 100644 packages/docs/programmable/sdk/api/components/property-list-root.md create mode 100644 packages/docs/programmable/sdk/api/components/scrub-input-display.md create mode 100644 packages/docs/programmable/sdk/api/components/scrub-input-field.md create mode 100644 packages/docs/programmable/sdk/api/components/scrub-input-root.md create mode 100644 packages/docs/programmable/sdk/api/components/toolbar-item.md create mode 100644 packages/docs/programmable/sdk/api/components/toolbar-root.md create mode 100644 packages/docs/programmable/sdk/api/components/typography-controls-root.md create mode 100644 packages/docs/programmable/sdk/api/composables/index.md create mode 100644 packages/docs/programmable/sdk/api/composables/provide-editor.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-appearance.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-canvas-input.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-canvas.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-editor-commands.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-editor.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-effects-controls.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-export.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-fill-controls.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-layout.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-menu-model.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-page-list.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-position.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-selection-capabilities.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-selection-state.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-stroke-controls.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-text-edit.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-typography.md create mode 100644 packages/docs/programmable/sdk/api/composables/use-variables-editor.md create mode 100644 packages/docs/programmable/sdk/api/index.md create mode 100644 packages/docs/programmable/sdk/architecture.md create mode 100644 packages/docs/programmable/sdk/getting-started.md create mode 100644 packages/docs/programmable/sdk/guides/custom-editor-shell.md create mode 100644 packages/docs/programmable/sdk/guides/navigation-panels.md create mode 100644 packages/docs/programmable/sdk/guides/property-panels.md create mode 100644 packages/docs/programmable/sdk/index.md diff --git a/AGENTS.md b/AGENTS.md index 9bd867664..701f8a3a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ Bun workspace with three packages: - `packages/docs` — `@open-pencil/docs`: VitePress documentation site. Run with `cd packages/docs && bun run dev`. - `packages/mcp` — `@open-pencil/mcp`: MCP server for AI coding tools. Stdio + HTTP (Hono). Reuses `createServer()` factory with all core tools. -- `packages/vue` — `@open-pencil/vue`: headless Vue 3 SDK (Reka UI-style). Renderless components, composables. The app is a consumer. +- `packages/vue` — `@open-pencil/vue`: headless Vue 3 SDK (Reka UI-style) for building custom OpenPencil-powered editor shells and embedded editing surfaces. Renderless components and composables. The app is one consumer of the SDK. The root app (`src/`) is the Tauri/Vite desktop editor. Its `src/engine/` files are thin re-export shims from `@open-pencil/core`. `src/composables/use-canvas.ts` re-exports from `@open-pencil/vue`. @@ -127,7 +127,7 @@ bun run test # Playwright E2E - `CHANGELOG.md` — all user-facing changes, grouped by version. "Unreleased" section at top for in-progress work. - `README.md` — user-facing: features, getting started, CLI, project structure. No implementation details. - `AGENTS.md` (this file) — contributor/agent reference: architecture, conventions, how to release. -- `packages/docs/` — VitePress site deployed at `openpencil.dev`. User guide, reference, development docs. +- `packages/docs/` — VitePress site deployed at `openpencil.dev`. User guide, SDK, automation, reference, and development docs. When adding features, update `CHANGELOG.md` (Unreleased section) and `README.md` (if user-facing). Update `AGENTS.md` when architecture or conventions change. diff --git a/README.md b/README.md index fef390246..b8866327f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OpenPencil -Open-source design editor. Opens Figma files, built-in AI, fully programmable. +Open-source design editor. Opens Figma files, built-in AI, fully programmable, and designed as a toolkit for building custom editors. > **Status:** Active development. Not ready for production use. @@ -23,6 +23,7 @@ Or download from the [releases page](https://github.com/open-pencil/open-pencil/ - **Opens .fig files** — read and write native Figma files, copy & paste nodes between apps - **AI builds designs** — describe what you want in chat, 90+ tools create and modify nodes. Connect Anthropic, OpenAI, Google AI, OpenRouter, or any compatible endpoint - **Fully programmable** — headless CLI, Figma Plugin API via `eval`, MCP server for AI agents +- **Toolkit for custom editors** — Vue SDK primitives and composables for embedding OpenPencil into other apps or building workflow-specific editing surfaces - **Real-time collaboration** — P2P via WebRTC, no server, no account. Cursors, presence, follow mode - **Auto layout & CSS Grid** — flex and grid layout via Yoga WASM, with gap, padding, alignment, track sizing - **Tailwind CSS export** — export any selection as HTML with Tailwind v4 utility classes diff --git a/bun.lock b/bun.lock index f7480ea53..5f50da6e8 100644 --- a/bun.lock +++ b/bun.lock @@ -123,6 +123,9 @@ "packages/docs": { "name": "@open-pencil/docs", "version": "0.1.0", + "dependencies": { + "@iconify/vue": "^5.0.0", + }, "devDependencies": { "vitepress": "^1.6.4", }, @@ -157,6 +160,9 @@ "canvaskit-wasm": ">=0.39.0", "vue": "^3.4.0", }, + "optionalPeers": [ + "canvaskit-wasm", + ], }, }, "packages": { @@ -596,6 +602,8 @@ "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], + "@iconify/vue": ["@iconify/vue@5.0.0", "", { "dependencies": { "@iconify/types": "^2.0.0" }, "peerDependencies": { "vue": ">=3" } }, "sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg=="], + "@internationalized/date": ["@internationalized/date@3.11.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q=="], "@internationalized/number": ["@internationalized/number@3.6.5", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g=="], diff --git a/packages/docs/.vitepress/config.ts b/packages/docs/.vitepress/config.ts index e947d27cc..a211d8268 100644 --- a/packages/docs/.vitepress/config.ts +++ b/packages/docs/.vitepress/config.ts @@ -26,6 +26,7 @@ interface SidebarLabels { } interface ProgrammableLabels { + overview: string cli: string inspecting: string exporting: string @@ -38,13 +39,85 @@ interface ProgrammableLabels { } interface NavLabels { + guide: string userGuide: string programmable: string + sdk: string reference: string development: string openApp: string } +const SDK_COMPONENT_PAGES = [ + { text: 'CanvasRoot', slug: 'canvas-root' }, + { text: 'CanvasSurface', slug: 'canvas-surface' }, + { text: 'LayerTreeRoot', slug: 'layer-tree-root' }, + { text: 'LayerTreeItem', slug: 'layer-tree-item' }, + { text: 'ToolbarRoot', slug: 'toolbar-root' }, + { text: 'ToolbarItem', slug: 'toolbar-item' }, + { text: 'PageListRoot', slug: 'page-list-root' }, + { text: 'PropertyListRoot', slug: 'property-list-root' }, + { text: 'PropertyListItem', slug: 'property-list-item' }, + { text: 'ColorPickerRoot', slug: 'color-picker-root' }, + { text: 'ColorInputRoot', slug: 'color-input-root' }, + { text: 'FillPickerRoot', slug: 'fill-picker-root' }, + { text: 'FontPickerRoot', slug: 'font-picker-root' }, + { text: 'GradientEditorRoot', slug: 'gradient-editor-root' }, + { text: 'GradientEditorBar', slug: 'gradient-editor-bar' }, + { text: 'GradientEditorStop', slug: 'gradient-editor-stop' }, + { text: 'ScrubInputRoot', slug: 'scrub-input-root' }, + { text: 'ScrubInputField', slug: 'scrub-input-field' }, + { text: 'ScrubInputDisplay', slug: 'scrub-input-display' }, + { text: 'LayoutControlsRoot', slug: 'layout-controls-root' }, + { text: 'AppearanceControlsRoot', slug: 'appearance-controls-root' }, + { text: 'PositionControlsRoot', slug: 'position-controls-root' }, + { text: 'TypographyControlsRoot', slug: 'typography-controls-root' }, +] as const + +const SDK_COMPOSABLE_PAGES = [ + { text: 'provideEditor', slug: 'provide-editor' }, + { text: 'useEditor', slug: 'use-editor' }, + { text: 'useCanvas', slug: 'use-canvas' }, + { text: 'useCanvasInput', slug: 'use-canvas-input' }, + { text: 'useTextEdit', slug: 'use-text-edit' }, + { text: 'useSelectionState', slug: 'use-selection-state' }, + { text: 'useSelectionCapabilities', slug: 'use-selection-capabilities' }, + { text: 'useEditorCommands', slug: 'use-editor-commands' }, + { text: 'useMenuModel', slug: 'use-menu-model' }, + { text: 'usePosition', slug: 'use-position' }, + { text: 'useLayout', slug: 'use-layout' }, + { text: 'useAppearance', slug: 'use-appearance' }, + { text: 'useTypography', slug: 'use-typography' }, + { text: 'useExport', slug: 'use-export' }, + { text: 'useFillControls', slug: 'use-fill-controls' }, + { text: 'useStrokeControls', slug: 'use-stroke-controls' }, + { text: 'useEffectsControls', slug: 'use-effects-controls' }, + { text: 'useVariablesEditor', slug: 'use-variables-editor' }, + { text: 'usePageList', slug: 'use-page-list' }, +] as const + +const SDK_ADVANCED_PAGES = [ + { text: 'useNodeProps', slug: 'use-node-props' }, + { text: 'useSceneComputed', slug: 'use-scene-computed' }, + { text: 'useFillVariableBinding', slug: 'use-fill-variable-binding' }, + { text: 'useFillPicker', slug: 'use-fill-picker' }, + { text: 'useGradientStops', slug: 'use-gradient-stops' }, + { text: 'useFontPicker', slug: 'use-font-picker' }, + { text: 'usePropScrub', slug: 'use-prop-scrub' }, + { text: 'useLayerDrag', slug: 'use-layer-drag' }, + { text: 'useInlineRename', slug: 'use-inline-rename' }, + { text: 'useToolbarState', slug: 'use-toolbar-state' }, + { text: 'useNodeFontStatus', slug: 'use-node-font-status' }, + { text: 'useCanvasDrop', slug: 'use-canvas-drop' }, + { text: 'extractImageFilesFromClipboard', slug: 'extract-image-files-from-clipboard' }, + { text: 'toolCursor', slug: 'tool-cursor' }, + { text: 'useCanvasContext', slug: 'use-canvas-context' }, + { text: 'useLayerTree', slug: 'use-layer-tree' }, + { text: 'useToolbar', slug: 'use-toolbar' }, + { text: 'usePropertyList', slug: 'use-property-list' }, + { text: 'useScrubInput', slug: 'use-scrub-input' }, +] as const + const userGuideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.SidebarItem[] => [ { text: l.gettingAround, @@ -78,7 +151,14 @@ const userGuideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.Sideba }, ] -const programmableSidebar = (prefix: string, p: ProgrammableLabels): DefaultTheme.SidebarItem[] => [ +const programmableSidebar = ( + prefix: string, + p: ProgrammableLabels, +): DefaultTheme.SidebarItem[] => [ + { + text: p.overview, + link: `${prefix}/programmable/`, + }, { text: p.aiChat, link: `${prefix}/programmable/ai-chat`, @@ -106,6 +186,57 @@ const programmableSidebar = (prefix: string, p: ProgrammableLabels): DefaultThem }, ] + +const sdkSidebar = (prefix: string): DefaultTheme.SidebarItem[] => [ + { + text: 'Vue SDK', + items: [ + { text: 'Overview', link: `${prefix}/programmable/sdk/` }, + { text: 'Getting Started', link: `${prefix}/programmable/sdk/getting-started` }, + { text: 'Architecture', link: `${prefix}/programmable/sdk/architecture` }, + { text: 'Custom Shell', link: `${prefix}/programmable/sdk/guides/custom-editor-shell` }, + { text: 'Property Panels', link: `${prefix}/programmable/sdk/guides/property-panels` }, + { text: 'Navigation Panels', link: `${prefix}/programmable/sdk/guides/navigation-panels` }, + { + text: 'API Reference', + items: [ + { text: 'Overview', link: `${prefix}/programmable/sdk/api/` }, + { + text: 'Components', + items: [ + { text: 'Overview', link: `${prefix}/programmable/sdk/api/components/` }, + ...SDK_COMPONENT_PAGES.map((page) => ({ + text: page.text, + link: `${prefix}/programmable/sdk/api/components/${page.slug}`, + })), + ], + }, + { + text: 'Composables', + items: [ + { text: 'Overview', link: `${prefix}/programmable/sdk/api/composables/` }, + ...SDK_COMPOSABLE_PAGES.map((page) => ({ + text: page.text, + link: `${prefix}/programmable/sdk/api/composables/${page.slug}`, + })), + ], + }, + { + text: 'Advanced', + items: [ + { text: 'Overview', link: `${prefix}/programmable/sdk/api/advanced/` }, + ...SDK_ADVANCED_PAGES.map((page) => ({ + text: page.text, + link: `${prefix}/programmable/sdk/api/advanced/${page.slug}`, + })), + ], + }, + ], + }, + ], + }, +] + const guideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.SidebarItem[] => [ { text: l.guide, @@ -120,12 +251,11 @@ const guideSidebar = (prefix: string, l: SidebarLabels): DefaultTheme.SidebarIte }, ] -const referenceSidebar = (prefix: string, label: string, l: SidebarLabels): DefaultTheme.SidebarItem[] => [ +const referenceSidebar = (prefix: string, label: string): DefaultTheme.SidebarItem[] => [ { text: label, items: [ { text: 'Keyboard Shortcuts', link: `${prefix}/reference/keyboard-shortcuts` }, - { text: l.contextMenu, link: `${prefix}/user-guide/context-menu` }, { text: 'CLI', link: `${prefix}/reference/cli` }, { text: 'Node Types', link: `${prefix}/reference/node-types` }, { text: 'Scene Graph', link: `${prefix}/reference/scene-graph` }, @@ -146,13 +276,13 @@ const developmentSidebar = (prefix: string, label: string): DefaultTheme.Sidebar }, ] -const EN_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspecting Files', exporting: 'Exporting', analyzing: 'Analyzing Designs', scripting: 'Scripting', jsxRenderer: 'JSX Renderer', mcpServer: 'MCP Server', aiChat: 'AI Chat', collaboration: 'Collaboration' } -const DE_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Dateien inspizieren', exporting: 'Exportieren', analyzing: 'Designs analysieren', scripting: 'Skripte', jsxRenderer: 'JSX-Renderer', mcpServer: 'MCP-Server', aiChat: 'KI-Chat', collaboration: 'Zusammenarbeit' } -const IT_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Ispezione file', exporting: 'Esportazione', analyzing: 'Analisi design', scripting: 'Scripting', jsxRenderer: 'Renderer JSX', mcpServer: 'Server MCP', aiChat: 'Chat IA', collaboration: 'Collaborazione' } -const FR_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspecter les fichiers', exporting: 'Exporter', analyzing: 'Analyser les designs', scripting: 'Scripts', jsxRenderer: 'Moteur JSX', mcpServer: 'Serveur MCP', aiChat: 'Chat IA', collaboration: 'Collaboration' } -const ES_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspeccionar archivos', exporting: 'Exportar', analyzing: 'Analizar diseños', scripting: 'Scripts', jsxRenderer: 'Renderizador JSX', mcpServer: 'Servidor MCP', aiChat: 'Chat IA', collaboration: 'Colaboración' } -const PL_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Inspekcja plików', exporting: 'Eksportowanie', analyzing: 'Analiza projektów', scripting: 'Skrypty', jsxRenderer: 'Renderer JSX', mcpServer: 'Serwer MCP', aiChat: 'Czat AI', collaboration: 'Współpraca' } -const RU_PROG: ProgrammableLabels = { cli: 'CLI', inspecting: 'Инспекция файлов', exporting: 'Экспорт', analyzing: 'Анализ дизайна', scripting: 'Скрипты', jsxRenderer: 'JSX-рендерер', mcpServer: 'MCP-сервер', aiChat: 'ИИ-чат', collaboration: 'Совместная работа' } +const EN_PROG: ProgrammableLabels = { overview: 'Overview', cli: 'CLI', inspecting: 'Inspecting Files', exporting: 'Exporting', analyzing: 'Analyzing Designs', scripting: 'Scripting', jsxRenderer: 'JSX Renderer', mcpServer: 'MCP Server', aiChat: 'AI Chat', collaboration: 'Collaboration' } +const DE_PROG: ProgrammableLabels = { overview: 'Überblick', cli: 'CLI', inspecting: 'Dateien inspizieren', exporting: 'Exportieren', analyzing: 'Designs analysieren', scripting: 'Skripte', jsxRenderer: 'JSX-Renderer', mcpServer: 'MCP-Server', aiChat: 'KI-Chat', collaboration: 'Zusammenarbeit' } +const IT_PROG: ProgrammableLabels = { overview: 'Panoramica', cli: 'CLI', inspecting: 'Ispezione file', exporting: 'Esportazione', analyzing: 'Analisi design', scripting: 'Scripting', jsxRenderer: 'Renderer JSX', mcpServer: 'Server MCP', aiChat: 'Chat IA', collaboration: 'Collaborazione' } +const FR_PROG: ProgrammableLabels = { overview: 'Vue d’ensemble', cli: 'CLI', inspecting: 'Inspecter les fichiers', exporting: 'Exporter', analyzing: 'Analyser les designs', scripting: 'Scripts', jsxRenderer: 'Moteur JSX', mcpServer: 'Serveur MCP', aiChat: 'Chat IA', collaboration: 'Collaboration' } +const ES_PROG: ProgrammableLabels = { overview: 'Resumen', cli: 'CLI', inspecting: 'Inspeccionar archivos', exporting: 'Exportar', analyzing: 'Analizar diseños', scripting: 'Scripts', jsxRenderer: 'Renderizador JSX', mcpServer: 'Servidor MCP', aiChat: 'Chat IA', collaboration: 'Colaboración' } +const PL_PROG: ProgrammableLabels = { overview: 'Przegląd', cli: 'CLI', inspecting: 'Inspekcja plików', exporting: 'Eksportowanie', analyzing: 'Analiza projektów', scripting: 'Skrypty', jsxRenderer: 'Renderer JSX', mcpServer: 'Serwer MCP', aiChat: 'Czat AI', collaboration: 'Współpraca' } +const RU_PROG: ProgrammableLabels = { overview: 'Обзор', cli: 'CLI', inspecting: 'Инспекция файлов', exporting: 'Экспорт', analyzing: 'Анализ дизайна', scripting: 'Скрипты', jsxRenderer: 'JSX-рендерер', mcpServer: 'MCP-сервер', aiChat: 'ИИ-чат', collaboration: 'Совместная работа' } const localeThemeConfig = ( prefix: string, @@ -161,16 +291,19 @@ const localeThemeConfig = ( prog: ProgrammableLabels, ): DefaultTheme.Config => ({ nav: [ + { text: nav.guide, link: `${prefix}/guide/getting-started` }, { text: nav.userGuide, link: `${prefix}/user-guide/` }, { text: nav.programmable, link: `${prefix}/programmable/` }, + { text: nav.sdk, link: `${prefix}/programmable/sdk/` }, { text: nav.reference, link: `${prefix}/reference/keyboard-shortcuts` }, { text: nav.development, link: `${prefix}/development/contributing` }, { text: nav.openApp, link: 'https://app.openpencil.dev' }, ], sidebar: { [`${prefix}/user-guide/`]: userGuideSidebar(prefix, sidebar), + [`${prefix}/programmable/sdk/`]: sdkSidebar(prefix), [`${prefix}/programmable/`]: programmableSidebar(prefix, prog), - [`${prefix}/reference/`]: referenceSidebar(prefix, nav.reference, sidebar), + [`${prefix}/reference/`]: referenceSidebar(prefix, nav.reference), [`${prefix}/`]: [ ...guideSidebar(prefix, sidebar), ...developmentSidebar(prefix, nav.development), @@ -302,37 +435,37 @@ export default defineConfig({ label: 'Deutsch', lang: 'de', description: 'Open-Source, KI-nativer Design-Editor. Figma-Alternative.', - themeConfig: localeThemeConfig('/de', { userGuide: 'Benutzerhandbuch', programmable: 'KI & Automatisierung', reference: 'Referenz', development: 'Entwicklung', openApp: 'App öffnen' }, DE, DE_PROG), + themeConfig: localeThemeConfig('/de', { guide: 'Guide', userGuide: 'Benutzerhandbuch', programmable: 'Automation', sdk: 'SDK', reference: 'Referenz', development: 'Entwicklung', openApp: 'App öffnen' }, DE, DE_PROG), }, it: { label: 'Italiano', lang: 'it', description: 'Editor di design open-source, IA-nativo. Alternativa a Figma.', - themeConfig: localeThemeConfig('/it', { userGuide: 'Guida utente', programmable: 'IA & Automazione', reference: 'Riferimento', development: 'Sviluppo', openApp: 'Apri app' }, IT, IT_PROG), + themeConfig: localeThemeConfig('/it', { guide: 'Guida', userGuide: 'Guida utente', programmable: 'Automation', sdk: 'SDK', reference: 'Riferimento', development: 'Sviluppo', openApp: 'Apri app' }, IT, IT_PROG), }, fr: { label: 'Français', lang: 'fr', description: 'Éditeur de design open-source, IA-natif. Alternative à Figma.', - themeConfig: localeThemeConfig('/fr', { userGuide: 'Guide utilisateur', programmable: 'IA & Automatisation', reference: 'Référence', development: 'Développement', openApp: "Ouvrir l'app" }, FR, FR_PROG), + themeConfig: localeThemeConfig('/fr', { guide: 'Guide', userGuide: 'Guide utilisateur', programmable: 'Automation', sdk: 'SDK', reference: 'Référence', development: 'Développement', openApp: "Ouvrir l'app" }, FR, FR_PROG), }, es: { label: 'Español', lang: 'es', description: 'Editor de diseño open-source, IA-nativo. Alternativa a Figma.', - themeConfig: localeThemeConfig('/es', { userGuide: 'Guía del usuario', programmable: 'IA & Automatización', reference: 'Referencia', development: 'Desarrollo', openApp: 'Abrir app' }, ES, ES_PROG), + themeConfig: localeThemeConfig('/es', { guide: 'Guía', userGuide: 'Guía del usuario', programmable: 'Automation', sdk: 'SDK', reference: 'Referencia', development: 'Desarrollo', openApp: 'Abrir app' }, ES, ES_PROG), }, pl: { label: 'Polski', lang: 'pl', description: "Open-source'owy edytor graficzny z natywnym AI. Alternatywa dla Figmy.", - themeConfig: localeThemeConfig('/pl', { userGuide: 'Podręcznik', programmable: 'AI i automatyzacja', reference: 'Referencja', development: 'Rozwój', openApp: 'Otwórz app' }, PL, PL_PROG), + themeConfig: localeThemeConfig('/pl', { guide: 'Przewodnik', userGuide: 'Podręcznik', programmable: 'Automation', sdk: 'SDK', reference: 'Referencja', development: 'Rozwój', openApp: 'Otwórz app' }, PL, PL_PROG), }, ru: { label: 'Русский', lang: 'ru', description: 'Дизайн-редактор с открытым исходным кодом. Альтернатива Figma с встроенным ИИ.', - themeConfig: localeThemeConfig('/ru', { userGuide: 'Руководство', programmable: 'ИИ и автоматизация', reference: 'Справочник', development: 'Разработка', openApp: 'Открыть приложение' }, RU, RU_PROG), + themeConfig: localeThemeConfig('/ru', { guide: 'Руководство', userGuide: 'Руководство', programmable: 'Automation', sdk: 'SDK', reference: 'Справочник', development: 'Разработка', openApp: 'Открыть приложение' }, RU, RU_PROG), }, }, @@ -340,8 +473,10 @@ export default defineConfig({ search: { provider: 'local' }, nav: [ + { text: 'Guide', link: '/guide/getting-started' }, { text: 'User Guide', link: '/user-guide/' }, - { text: 'AI & Automation', link: '/programmable/' }, + { text: 'Automation', link: '/programmable/' }, + { text: 'SDK', link: '/programmable/sdk/' }, { text: 'Reference', link: '/reference/keyboard-shortcuts' }, { text: 'Development', link: '/development/contributing' }, { text: 'Open App', link: 'https://app.openpencil.dev' }, @@ -349,8 +484,9 @@ export default defineConfig({ sidebar: { '/user-guide/': userGuideSidebar('', EN), + '/programmable/sdk/': sdkSidebar(''), '/programmable/': programmableSidebar('', EN_PROG), - '/reference/': referenceSidebar('', 'Reference', EN), + '/reference/': referenceSidebar('', 'Reference'), '/': [ ...guideSidebar('', EN), { diff --git a/packages/docs/.vitepress/theme/components/SdkCard.vue b/packages/docs/.vitepress/theme/components/SdkCard.vue new file mode 100644 index 000000000..04cad4207 --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkCard.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/packages/docs/.vitepress/theme/components/SdkCardGroup.vue b/packages/docs/.vitepress/theme/components/SdkCardGroup.vue new file mode 100644 index 000000000..2d2a1ff28 --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkCardGroup.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/docs/.vitepress/theme/components/SdkEventsTable.vue b/packages/docs/.vitepress/theme/components/SdkEventsTable.vue new file mode 100644 index 000000000..0a31370ef --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkEventsTable.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/packages/docs/.vitepress/theme/components/SdkField.vue b/packages/docs/.vitepress/theme/components/SdkField.vue new file mode 100644 index 000000000..50699ebef --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkField.vue @@ -0,0 +1,18 @@ + + + diff --git a/packages/docs/.vitepress/theme/components/SdkFieldGroup.vue b/packages/docs/.vitepress/theme/components/SdkFieldGroup.vue new file mode 100644 index 000000000..bb12b3abb --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkFieldGroup.vue @@ -0,0 +1,5 @@ + diff --git a/packages/docs/.vitepress/theme/components/SdkPropsTable.vue b/packages/docs/.vitepress/theme/components/SdkPropsTable.vue new file mode 100644 index 000000000..eaae6032e --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkPropsTable.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/packages/docs/.vitepress/theme/components/SdkRelatedLinks.vue b/packages/docs/.vitepress/theme/components/SdkRelatedLinks.vue new file mode 100644 index 000000000..e1e0de541 --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkRelatedLinks.vue @@ -0,0 +1,18 @@ + + + diff --git a/packages/docs/.vitepress/theme/components/SdkSlotsTable.vue b/packages/docs/.vitepress/theme/components/SdkSlotsTable.vue new file mode 100644 index 000000000..51ee2c0da --- /dev/null +++ b/packages/docs/.vitepress/theme/components/SdkSlotsTable.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/packages/docs/.vitepress/theme/index.ts b/packages/docs/.vitepress/theme/index.ts index 6cf910272..4fa466cbc 100644 --- a/packages/docs/.vitepress/theme/index.ts +++ b/packages/docs/.vitepress/theme/index.ts @@ -1,7 +1,26 @@ import DefaultTheme from 'vitepress/theme' + import HomeLayout from './HomeLayout.vue' +import SdkCard from './components/SdkCard.vue' +import SdkCardGroup from './components/SdkCardGroup.vue' +import SdkEventsTable from './components/SdkEventsTable.vue' +import SdkField from './components/SdkField.vue' +import SdkFieldGroup from './components/SdkFieldGroup.vue' +import SdkPropsTable from './components/SdkPropsTable.vue' +import SdkRelatedLinks from './components/SdkRelatedLinks.vue' +import SdkSlotsTable from './components/SdkSlotsTable.vue' export default { extends: DefaultTheme, Layout: HomeLayout, + enhanceApp({ app }) { + app.component('SdkCard', SdkCard) + app.component('SdkCardGroup', SdkCardGroup) + app.component('SdkEventsTable', SdkEventsTable) + app.component('SdkField', SdkField) + app.component('SdkFieldGroup', SdkFieldGroup) + app.component('SdkPropsTable', SdkPropsTable) + app.component('SdkRelatedLinks', SdkRelatedLinks) + app.component('SdkSlotsTable', SdkSlotsTable) + }, } diff --git a/packages/docs/guide/getting-started.md b/packages/docs/guide/getting-started.md index 27ac95f7d..dbee507ad 100644 --- a/packages/docs/guide/getting-started.md +++ b/packages/docs/guide/getting-started.md @@ -4,6 +4,8 @@ OpenPencil runs in the browser — no installation required. Open [app.openpencil.dev](https://app.openpencil.dev) to start designing. +If you want to build on top of it instead of only using the default app, see the [Programmable](/programmable/) section and the [Vue SDK](/programmable/sdk/). + ## Download Desktop App Pre-built binaries for macOS, Windows, and Linux are available on the [releases page](https://github.com/open-pencil/open-pencil/releases/latest). diff --git a/packages/docs/index.md b/packages/docs/index.md index b4e55b800..8fd8fef22 100644 --- a/packages/docs/index.md +++ b/packages/docs/index.md @@ -6,7 +6,7 @@ description: Open-source Figma alternative. Opens .fig files, built-in AI, fully hero: name: OpenPencil text: Open-Source Design Editor - tagline: Opens Figma files. Built-in AI. Fully programmable. Free forever. + tagline: Opens Figma files. Built-in AI. Fully programmable. Also a toolkit for building custom editors. actions: - theme: brand text: Try Online @@ -24,7 +24,10 @@ features: details: Opens .fig files natively. Copy & paste between Figma and OpenPencil. Kiwi binary codec with round-trip fidelity. - icon: ⚡ title: Programmable - details: Headless CLI to inspect, export, and analyze .fig files. Figma Plugin API via eval. Tailwind CSS export. JSON output for CI. + details: Headless CLI to inspect, export, and analyze .fig files. Figma Plugin API via eval. Tailwind CSS export. JSON output for CI and automation. + - icon: 🧩 + title: Toolkit, not just app + details: Build custom editing surfaces with the Vue SDK, embed OpenPencil into other products, or assemble workflow-specific editors around the same core. - icon: 🤖 title: AI-Native details: Built-in chat with 90 tools — create shapes, set styles, manage layout, analyze tokens. MCP server for Claude Code, Cursor, Windsurf. diff --git a/packages/docs/package.json b/packages/docs/package.json index 9892af164..bc46bcea4 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -11,5 +11,8 @@ }, "devDependencies": { "vitepress": "^1.6.4" + }, + "dependencies": { + "@iconify/vue": "^5.0.0" } } diff --git a/packages/docs/programmable/index.md b/packages/docs/programmable/index.md index 497182146..5570c2d0d 100644 --- a/packages/docs/programmable/index.md +++ b/packages/docs/programmable/index.md @@ -1,15 +1,23 @@ --- layout: doc -title: AI & Automation -description: Every operation in OpenPencil is scriptable — AI chat, CLI, JSX renderer, MCP server, real-time collaboration. +title: Automation +description: AI chat, CLI, JSX renderer, MCP server, and other automation surfaces built on the OpenPencil editor engine. --- -# AI & Automation +# Automation OpenPencil treats design files as data. Every operation available in the editor — creating shapes, setting fills, managing auto-layout, exporting assets — is also available from the terminal, from AI agents, and from code. No plugins to install, no API keys, no waiting list. The editor UI and the automation interfaces use the same engine. If you can do it by clicking, you can do it by scripting. +## The bigger idea + +OpenPencil is not just meant to be a design app. + +It is also meant to be a toolkit: something you can embed into other products, wrap with your own UI, and use to build editing workflows that fit your own domain. + +That is why the automation surface matters. The app, the CLI, the AI tools, the JSX renderer, the MCP server, and the SDK all build on the same underlying editor engine. + ## AI Chat The built-in assistant has access to 87 tools that cover the full surface of the editor. Describe what you want in natural language — "add a 16px drop shadow to all buttons", "create a card component with dark mode variant", "export every frame on this page at 2×". @@ -22,6 +30,12 @@ Real-time multiplayer editing over peer-to-peer WebRTC. No server, no account. S [Collaboration →](./collaboration) +## Vue SDK + +Build OpenPencil-powered editors with the same Vue SDK the app uses internally. The SDK exposes editor context, canvas wiring, selection state, command models, property-panel composables, and headless primitives. + +[Vue SDK →](./sdk/) + ## JSX Renderer Describe UI as JSX — the same syntax LLMs already know from React. A single call can create an entire component tree with frames, text, auto-layout, fills, and strokes. Compact, declarative, and diffable. diff --git a/packages/docs/programmable/sdk/api/advanced/extract-image-files-from-clipboard.md b/packages/docs/programmable/sdk/api/advanced/extract-image-files-from-clipboard.md new file mode 100644 index 000000000..b0acee062 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/extract-image-files-from-clipboard.md @@ -0,0 +1,15 @@ +--- +title: extractImageFilesFromClipboard +description: Clipboard utility for extracting accepted image files. +--- + +# extractImageFilesFromClipboard + +`extractImageFilesFromClipboard(event)` filters clipboard files down to accepted image types used by the canvas drop flow. + +Use it when implementing paste-from-clipboard image workflows. + +## Related APIs + +- [useCanvasDrop](./use-canvas-drop) +- [useCanvas](../composables/use-canvas) diff --git a/packages/docs/programmable/sdk/api/advanced/index.md b/packages/docs/programmable/sdk/api/advanced/index.md new file mode 100644 index 000000000..79888b81f --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/index.md @@ -0,0 +1,39 @@ +--- +title: Advanced +description: Lower-level and specialized APIs in @open-pencil/vue. +--- + +# Advanced + +These APIs are public, but they are more specialized than the main component and composable surface. + +## Selection and scene helpers + +- [useNodeProps](./use-node-props) +- [useSceneComputed](./use-scene-computed) +- [usePropScrub](./use-prop-scrub) + +## Picker and editor internals + +- [useFillVariableBinding](./use-fill-variable-binding) +- [useFillPicker](./use-fill-picker) +- [useGradientStops](./use-gradient-stops) +- [useFontPicker](./use-font-picker) +- [useToolbarState](./use-toolbar-state) +- [useNodeFontStatus](./use-node-font-status) + +## Editor-shell utilities + +- [useLayerDrag](./use-layer-drag) +- [useInlineRename](./use-inline-rename) +- [useCanvasDrop](./use-canvas-drop) +- [extractImageFilesFromClipboard](./extract-image-files-from-clipboard) +- [toolCursor](./tool-cursor) + +## Primitive context helpers + +- [useCanvasContext](./use-canvas-context) +- [useLayerTree](./use-layer-tree) +- [useToolbar](./use-toolbar) +- [usePropertyList](./use-property-list) +- [useScrubInput](./use-scrub-input) diff --git a/packages/docs/programmable/sdk/api/advanced/tool-cursor.md b/packages/docs/programmable/sdk/api/advanced/tool-cursor.md new file mode 100644 index 000000000..412ac9f16 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/tool-cursor.md @@ -0,0 +1,15 @@ +--- +title: toolCursor +description: Helper that resolves the cursor string for an editor tool. +--- + +# toolCursor + +`toolCursor(tool, override?)` maps an editor tool to the cursor the SDK should use, while still allowing an explicit override. + +Use it when building custom canvas shells or tool UIs that need consistent cursor behavior. + +## Related APIs + +- [useCanvas](../composables/use-canvas) +- [useEditorCommands](../composables/use-editor-commands) diff --git a/packages/docs/programmable/sdk/api/advanced/use-canvas-context.md b/packages/docs/programmable/sdk/api/advanced/use-canvas-context.md new file mode 100644 index 000000000..7f146199b --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-canvas-context.md @@ -0,0 +1,15 @@ +--- +title: useCanvasContext +description: Primitive context helper for CanvasRoot descendants. +--- + +# useCanvasContext + +`useCanvasContext()` reads the local canvas context provided by `CanvasRoot`. + +Use it inside descendants like `CanvasSurface` or your own custom canvas children. + +## Related APIs + +- [CanvasRoot](../components/canvas-root) +- [CanvasSurface](../components/canvas-surface) diff --git a/packages/docs/programmable/sdk/api/advanced/use-canvas-drop.md b/packages/docs/programmable/sdk/api/advanced/use-canvas-drop.md new file mode 100644 index 000000000..191b73fae --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-canvas-drop.md @@ -0,0 +1,16 @@ +--- +title: useCanvasDrop +description: Image file drag-and-drop helper for canvas surfaces. +--- + +# useCanvasDrop + +`useCanvasDrop(canvasRef, editor)` wires dragenter, dragover, dragleave, and drop handling so image files dropped on the canvas are placed into the scene. + +Use it when building custom canvas surfaces that accept image drops. + +## Related APIs + +- [CanvasRoot](../components/canvas-root) +- [CanvasSurface](../components/canvas-surface) +- [extractImageFilesFromClipboard](./extract-image-files-from-clipboard) diff --git a/packages/docs/programmable/sdk/api/advanced/use-fill-picker.md b/packages/docs/programmable/sdk/api/advanced/use-fill-picker.md new file mode 100644 index 000000000..0bb55e0da --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-fill-picker.md @@ -0,0 +1,16 @@ +--- +title: useFillPicker +description: Low-level fill-category and conversion helper. +--- + +# useFillPicker + +`useFillPicker(fill, onUpdate)` returns derived fill category state plus conversion helpers for switching between solid, gradient, and image fills. + +Use it when building custom fill pickers beyond `FillPickerRoot`. + +## Related APIs + +- [FillPickerRoot](../components/fill-picker-root) +- [useFillControls](../composables/use-fill-controls) +- [useGradientStops](./use-gradient-stops) diff --git a/packages/docs/programmable/sdk/api/advanced/use-fill-variable-binding.md b/packages/docs/programmable/sdk/api/advanced/use-fill-variable-binding.md new file mode 100644 index 000000000..52cca1660 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-fill-variable-binding.md @@ -0,0 +1,15 @@ +--- +title: useFillVariableBinding +description: Variable-binding helper for fill color editors. +--- + +# useFillVariableBinding + +`useFillVariableBinding()` exposes search, binding, and unbinding helpers for color variables used by fill editors. + +Use it when building fill UIs that need to connect solid fills to design variables. + +## Related APIs + +- [useFillControls](../composables/use-fill-controls) +- [FillPickerRoot](../components/fill-picker-root) diff --git a/packages/docs/programmable/sdk/api/advanced/use-font-picker.md b/packages/docs/programmable/sdk/api/advanced/use-font-picker.md new file mode 100644 index 000000000..496166237 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-font-picker.md @@ -0,0 +1,15 @@ +--- +title: useFontPicker +description: Searchable font-picker state and selection helper. +--- + +# useFontPicker + +`useFontPicker(options)` manages available font families, search state, open state, and family selection for font-picker UIs. + +Use it when building custom font-picking UIs beyond `FontPickerRoot`. + +## Related APIs + +- [FontPickerRoot](../components/font-picker-root) +- [useTypography](../composables/use-typography) diff --git a/packages/docs/programmable/sdk/api/advanced/use-gradient-stops.md b/packages/docs/programmable/sdk/api/advanced/use-gradient-stops.md new file mode 100644 index 000000000..50b659577 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-gradient-stops.md @@ -0,0 +1,16 @@ +--- +title: useGradientStops +description: Gradient-stop state and mutation helper for fill editors. +--- + +# useGradientStops + +`useGradientStops(fill, onUpdate)` manages active-stop state, subtype switching, stop dragging, and color or opacity updates for gradient fills. + +Use it when building custom gradient editors beyond the packaged primitives. + +## Related APIs + +- [GradientEditorRoot](../components/gradient-editor-root) +- [GradientEditorBar](../components/gradient-editor-bar) +- [GradientEditorStop](../components/gradient-editor-stop) diff --git a/packages/docs/programmable/sdk/api/advanced/use-inline-rename.md b/packages/docs/programmable/sdk/api/advanced/use-inline-rename.md new file mode 100644 index 000000000..1e96134b4 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-inline-rename.md @@ -0,0 +1,15 @@ +--- +title: useInlineRename +description: Inline rename state and focus-management helper. +--- + +# useInlineRename + +`useInlineRename(onCommit)` manages editing state, focus, outside-click handling, and keyboard behavior for inline rename flows. + +Use it in custom page lists, layer trees, or similar rename-in-place UIs. + +## Related APIs + +- [PageListRoot](../components/page-list-root) +- [LayerTreeItem](../components/layer-tree-item) diff --git a/packages/docs/programmable/sdk/api/advanced/use-layer-drag.md b/packages/docs/programmable/sdk/api/advanced/use-layer-drag.md new file mode 100644 index 000000000..a0e3f6310 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-layer-drag.md @@ -0,0 +1,16 @@ +--- +title: useLayerDrag +description: Drag-and-drop wiring helper for layer tree reordering. +--- + +# useLayerDrag + +`useLayerDrag(editor, indentPerLevel?)` wires pragmatic-drag-and-drop behavior for layer tree rows and maps drop instructions to editor reorder operations. + +Use it when extending or replacing the default layer-tree drag UI. + +## Related APIs + +- [LayerTreeRoot](../components/layer-tree-root) +- [LayerTreeItem](../components/layer-tree-item) +- [useLayerTree](./use-layer-tree) diff --git a/packages/docs/programmable/sdk/api/advanced/use-layer-tree.md b/packages/docs/programmable/sdk/api/advanced/use-layer-tree.md new file mode 100644 index 000000000..ef686fb32 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-layer-tree.md @@ -0,0 +1,15 @@ +--- +title: useLayerTree +description: Primitive context helper for LayerTreeRoot descendants. +--- + +# useLayerTree + +`useLayerTree()` reads the local layer-tree context provided by `LayerTreeRoot`. + +Use it inside custom layer-tree descendants that need access to tree items, selection state, expansion state, or row actions. + +## Related APIs + +- [LayerTreeRoot](../components/layer-tree-root) +- [LayerTreeItem](../components/layer-tree-item) diff --git a/packages/docs/programmable/sdk/api/advanced/use-node-font-status.md b/packages/docs/programmable/sdk/api/advanced/use-node-font-status.md new file mode 100644 index 000000000..d9e6ea1d3 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-node-font-status.md @@ -0,0 +1,15 @@ +--- +title: useNodeFontStatus +description: Missing-font status helper for text nodes. +--- + +# useNodeFontStatus + +`useNodeFontStatus(node)` returns missing-font information for a text-node getter. + +Use it in typography panels and warnings that need to surface unavailable font families. + +## Related APIs + +- [useTypography](../composables/use-typography) +- [TypographyControlsRoot](../components/typography-controls-root) diff --git a/packages/docs/programmable/sdk/api/advanced/use-node-props.md b/packages/docs/programmable/sdk/api/advanced/use-node-props.md new file mode 100644 index 000000000..77b6353fa --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-node-props.md @@ -0,0 +1,17 @@ +--- +title: useNodeProps +description: Low-level selection and mixed-value helper for property panels. +--- + +# useNodeProps + +`useNodeProps()` is the low-level property-panel helper behind higher-level composables like `useAppearance`, `useLayout`, and `useTypography`. + +Use it when you need mixed-value detection, multi-selection updates, array-item editing, or undo-aware property commits. + +## Related APIs + +- [useAppearance](../composables/use-appearance) +- [useLayout](../composables/use-layout) +- [useTypography](../composables/use-typography) +- [PropertyListRoot](../components/property-list-root) diff --git a/packages/docs/programmable/sdk/api/advanced/use-prop-scrub.md b/packages/docs/programmable/sdk/api/advanced/use-prop-scrub.md new file mode 100644 index 000000000..7ff5e63f2 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-prop-scrub.md @@ -0,0 +1,15 @@ +--- +title: usePropScrub +description: Low-level helper for drag-to-scrub property updates with commit support. +--- + +# usePropScrub + +`usePropScrub(editor)` coordinates live property updates during scrubbing and commits undo-aware changes when the interaction finishes. + +Use it when building numeric controls that scrub selected node properties directly. + +## Related APIs + +- [ScrubInputRoot](../components/scrub-input-root) +- [useNodeProps](./use-node-props) diff --git a/packages/docs/programmable/sdk/api/advanced/use-property-list.md b/packages/docs/programmable/sdk/api/advanced/use-property-list.md new file mode 100644 index 000000000..0922f5a71 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-property-list.md @@ -0,0 +1,15 @@ +--- +title: usePropertyList +description: Primitive context helper for PropertyListRoot descendants. +--- + +# usePropertyList + +`usePropertyList()` reads the local property-list context provided by `PropertyListRoot`. + +Use it inside descendants that need current items, mixed-state info, or row-level handlers for fills, strokes, or effects. + +## Related APIs + +- [PropertyListRoot](../components/property-list-root) +- [PropertyListItem](../components/property-list-item) diff --git a/packages/docs/programmable/sdk/api/advanced/use-scene-computed.md b/packages/docs/programmable/sdk/api/advanced/use-scene-computed.md new file mode 100644 index 000000000..472596a4e --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-scene-computed.md @@ -0,0 +1,16 @@ +--- +title: useSceneComputed +description: Convenience wrapper for scene-derived computed state. +--- + +# useSceneComputed + +`useSceneComputed(fn)` is a thin computed wrapper used to make scene-backed derived state explicit in higher-level composables. + +Use it when you want intent-revealing computed state that clearly depends on editor scene data. + +## Related APIs + +- [useSelectionState](../composables/use-selection-state) +- [useSelectionCapabilities](../composables/use-selection-capabilities) +- [useNodeProps](./use-node-props) diff --git a/packages/docs/programmable/sdk/api/advanced/use-scrub-input.md b/packages/docs/programmable/sdk/api/advanced/use-scrub-input.md new file mode 100644 index 000000000..addbafec0 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-scrub-input.md @@ -0,0 +1,16 @@ +--- +title: useScrubInput +description: Primitive context helper for ScrubInputRoot descendants. +--- + +# useScrubInput + +`useScrubInput()` reads the local scrub-input context provided by `ScrubInputRoot`. + +Use it inside `ScrubInputField`, `ScrubInputDisplay`, or custom descendants that need editing and scrubbing state. + +## Related APIs + +- [ScrubInputRoot](../components/scrub-input-root) +- [ScrubInputField](../components/scrub-input-field) +- [ScrubInputDisplay](../components/scrub-input-display) diff --git a/packages/docs/programmable/sdk/api/advanced/use-toolbar-state.md b/packages/docs/programmable/sdk/api/advanced/use-toolbar-state.md new file mode 100644 index 000000000..627933012 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-toolbar-state.md @@ -0,0 +1,16 @@ +--- +title: useToolbarState +description: Presentation-oriented toolbar state helper for mobile category paging. +--- + +# useToolbarState + +`useToolbarState()` returns mobile-category paging state and helpers like `goPrev()` and `goNext()` for responsive toolbar shells. + +Use it when building toolbar layouts on top of `ToolbarRoot`. + +## Related APIs + +- [ToolbarRoot](../components/toolbar-root) +- [ToolbarItem](../components/toolbar-item) +- [useToolbar](./use-toolbar) diff --git a/packages/docs/programmable/sdk/api/advanced/use-toolbar.md b/packages/docs/programmable/sdk/api/advanced/use-toolbar.md new file mode 100644 index 000000000..34bdf2de8 --- /dev/null +++ b/packages/docs/programmable/sdk/api/advanced/use-toolbar.md @@ -0,0 +1,16 @@ +--- +title: useToolbar +description: Primitive context helper for ToolbarRoot descendants. +--- + +# useToolbar + +`useToolbar()` reads the local toolbar context provided by `ToolbarRoot`. + +Use it inside toolbar descendants that need access to tools, active state, or tool selection helpers. + +## Related APIs + +- [ToolbarRoot](../components/toolbar-root) +- [ToolbarItem](../components/toolbar-item) +- [useToolbarState](./use-toolbar-state) diff --git a/packages/docs/programmable/sdk/api/components/appearance-controls-root.md b/packages/docs/programmable/sdk/api/components/appearance-controls-root.md new file mode 100644 index 000000000..205807c1c --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/appearance-controls-root.md @@ -0,0 +1,15 @@ +--- +title: AppearanceControlsRoot +description: Headless root primitive for opacity, visibility, and corner-radius controls. +--- + +# AppearanceControlsRoot + +`AppearanceControlsRoot` exposes the slot contract returned by `useAppearance()` as a structural primitive. + +Use it when you want reusable appearance controls with custom presentation. + +## Related APIs + +- [useAppearance](../composables/use-appearance) +- [Property Panels guide](../../guides/property-panels) diff --git a/packages/docs/programmable/sdk/api/components/canvas-root.md b/packages/docs/programmable/sdk/api/components/canvas-root.md new file mode 100644 index 000000000..6810125ea --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/canvas-root.md @@ -0,0 +1,16 @@ +--- +title: CanvasRoot +description: Headless canvas primitive for OpenPencil rendering surfaces. +--- + +# CanvasRoot + +`CanvasRoot` is the structural canvas primitive in `@open-pencil/vue`. + +Use it when you want SDK-provided canvas structure and context with app-owned layout and styling. + +## Related APIs + +- [useCanvas](../composables/use-canvas) +- [useCanvasInput](../composables/use-canvas-input) +- [useTextEdit](../composables/use-text-edit) diff --git a/packages/docs/programmable/sdk/api/components/canvas-surface.md b/packages/docs/programmable/sdk/api/components/canvas-surface.md new file mode 100644 index 000000000..0de1922bd --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/canvas-surface.md @@ -0,0 +1,16 @@ +--- +title: CanvasSurface +description: Canvas element primitive that binds to the nearest CanvasRoot context. +--- + +# CanvasSurface + +`CanvasSurface` renders the actual `` element used by the SDK canvas stack. + +Use it inside `CanvasRoot` when you want SDK-managed canvas refs and rendering integration, but app-owned layout and styling. + +## Related APIs + +- [CanvasRoot](./canvas-root) +- [useCanvasContext](../advanced/use-canvas-context) +- [useCanvas](../composables/use-canvas) diff --git a/packages/docs/programmable/sdk/api/components/color-input-root.md b/packages/docs/programmable/sdk/api/components/color-input-root.md new file mode 100644 index 000000000..5cc93165d --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/color-input-root.md @@ -0,0 +1,47 @@ +--- +title: ColorInputRoot +description: Headless color input helper with hex parsing and update helpers. +--- + +# ColorInputRoot + +`ColorInputRoot` is a headless helper for color input UIs. + +It derives a hex value from a color and exposes update helpers for hex and full-color changes. + +## Props + + + +## Events + + + +## Slots + + + +## Example + +```vue + + + +``` + +## Related APIs + +- [ColorPickerRoot](./color-picker-root) diff --git a/packages/docs/programmable/sdk/api/components/color-picker-root.md b/packages/docs/programmable/sdk/api/components/color-picker-root.md new file mode 100644 index 000000000..ab3bf1ce0 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/color-picker-root.md @@ -0,0 +1,59 @@ +--- +title: ColorPickerRoot +description: Headless popover-based color picker primitive. +--- + +# ColorPickerRoot + +`ColorPickerRoot` is a headless popover-based color picker primitive. + +It provides: + +- a trigger slot with swatch background styling +- a default trigger fallback +- a content slot with `color` and `update()` + +## Props + + + +## Events + + + +## Slots + + + +## Example + +```vue + + + + + +``` + +## Related APIs + +- [ColorInputRoot](./color-input-root) diff --git a/packages/docs/programmable/sdk/api/components/fill-picker-root.md b/packages/docs/programmable/sdk/api/components/fill-picker-root.md new file mode 100644 index 000000000..95422cc16 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/fill-picker-root.md @@ -0,0 +1,73 @@ +--- +title: FillPickerRoot +description: Headless popover-based fill picker primitive. +--- + +# FillPickerRoot + +`FillPickerRoot` is a headless popover-based fill picker for solid, gradient, and image fills. + +## Props + + + +## Events + + + +## Slots + + + +### Trigger slot props + +```ts +{ + style: Record +} +``` + +### Default slot props + +```ts +{ + fill: Fill + category: 'SOLID' | 'GRADIENT' | 'IMAGE' + toSolid: () => void + toGradient: () => void + toImage: () => void + update: (fill: Fill) => void +} +``` + +## Example + +```vue + + + +``` + +## Related APIs + +- [GradientEditorRoot](./gradient-editor-root) diff --git a/packages/docs/programmable/sdk/api/components/font-picker-root.md b/packages/docs/programmable/sdk/api/components/font-picker-root.md new file mode 100644 index 000000000..9a21465e7 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/font-picker-root.md @@ -0,0 +1,67 @@ +--- +title: FontPickerRoot +description: Headless searchable font picker built on Reka Combobox. +--- + +# FontPickerRoot + +`FontPickerRoot` is a headless searchable font picker built on Reka UI Combobox primitives. + +## Props + + + +## Model + + + +## Events + + + +## Slots + + + +## Example + +```vue + + + +``` + +## Related APIs + +- [useTypography](../composables/use-typography) diff --git a/packages/docs/programmable/sdk/api/components/gradient-editor-bar.md b/packages/docs/programmable/sdk/api/components/gradient-editor-bar.md new file mode 100644 index 000000000..bbdad0ce3 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/gradient-editor-bar.md @@ -0,0 +1,70 @@ +--- +title: GradientEditorBar +description: Headless draggable bar primitive for gradient stops. +--- + +# GradientEditorBar + +`GradientEditorBar` is the draggable bar primitive used inside gradient editors. + +## Props + + + +## Events + + + +## Slots + + + +### Default slot props + +```ts +{ + stops: GradientStop[] + activeStopIndex: number + barBackground: string + barRef: (el: unknown) => void + onStopPointerDown: (index: number, event: PointerEvent) => void + onPointerMove: (event: PointerEvent) => void + onPointerUp: () => void + draggingIndex: number | null +} +``` + +## Example + +```vue + + + +``` + +## Related APIs + +- [GradientEditorRoot](./gradient-editor-root) +- [GradientEditorStop](./gradient-editor-stop) diff --git a/packages/docs/programmable/sdk/api/components/gradient-editor-root.md b/packages/docs/programmable/sdk/api/components/gradient-editor-root.md new file mode 100644 index 000000000..cc4936b2d --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/gradient-editor-root.md @@ -0,0 +1,75 @@ +--- +title: GradientEditorRoot +description: Headless root primitive for gradient stop editing. +--- + +# GradientEditorRoot + +`GradientEditorRoot` is a headless root primitive for gradient editing. + +It owns: + +- active stop state +- subtype switching +- stop add/remove/update logic +- active color editing +- derived bar background + +## Props + + + +## Events + + + +## Slots + + + +### Default slot props + +```ts +{ + stops: GradientStop[] + subtype: GradientSubtype + subtypes: Array<{ value: GradientSubtype; label: string }> + activeStopIndex: number + activeColor: Color + barBackground: string + setSubtype: (type: GradientSubtype) => void + selectStop: (index: number) => void + addStop: () => void + removeStop: (index: number) => void + updateStopPosition: (index: number, position: number) => void + updateStopColor: (index: number, hex: string) => void + updateStopOpacity: (index: number, opacity: number) => void + updateActiveColor: (color: Color) => void + dragStop: (index: number, position: number) => void +} +``` + +## Example + +```vue + + + +``` + +## Related APIs + +- [GradientEditorBar](./gradient-editor-bar) +- [GradientEditorStop](./gradient-editor-stop) diff --git a/packages/docs/programmable/sdk/api/components/gradient-editor-stop.md b/packages/docs/programmable/sdk/api/components/gradient-editor-stop.md new file mode 100644 index 000000000..2840244af --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/gradient-editor-stop.md @@ -0,0 +1,70 @@ +--- +title: GradientEditorStop +description: Headless slot primitive for a single gradient stop row. +--- + +# GradientEditorStop + +`GradientEditorStop` is a headless primitive for rendering and editing a single gradient stop. + +## Props + + + +## Events + + + +## Slots + + + +### Default slot props + +```ts +{ + stop: GradientStop + index: number + active: boolean + positionPercent: number + opacityPercent: number + hex: string + css: string + select: () => void + updatePosition: (position: number) => void + updateColor: (hex: string) => void + updateOpacity: (opacity: number) => void + remove: () => void +} +``` + +## Example + +```vue + + + +``` + +## Related APIs + +- [GradientEditorRoot](./gradient-editor-root) +- [GradientEditorBar](./gradient-editor-bar) diff --git a/packages/docs/programmable/sdk/api/components/index.md b/packages/docs/programmable/sdk/api/components/index.md new file mode 100644 index 000000000..be6e15907 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/index.md @@ -0,0 +1,46 @@ +--- +title: Components +description: Component reference for headless Vue primitives in @open-pencil/vue. +--- + +# Components + +`@open-pencil/vue` exposes headless structural primitives for canvas wiring, navigation UI, property panels, and focused input controls. + +## Core editor primitives + + + + + + + + + + + +## Property panel primitives + + + + + + + + + + +## Pickers and inputs + + + + + + + + + + + + + diff --git a/packages/docs/programmable/sdk/api/components/layer-tree-item.md b/packages/docs/programmable/sdk/api/components/layer-tree-item.md new file mode 100644 index 000000000..ecd3ea0ee --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/layer-tree-item.md @@ -0,0 +1,16 @@ +--- +title: LayerTreeItem +description: Headless row primitive for a single layer tree node. +--- + +# LayerTreeItem + +`LayerTreeItem` renders one layer-tree row and exposes selection, expand, visibility, lock, and rename handlers through its default slot. + +Use it when you want app-owned row markup with SDK-provided layer-tree behavior. + +## Related APIs + +- [LayerTreeRoot](./layer-tree-root) +- [useLayerTree](../advanced/use-layer-tree) +- [useLayerDrag](../advanced/use-layer-drag) diff --git a/packages/docs/programmable/sdk/api/components/layer-tree-root.md b/packages/docs/programmable/sdk/api/components/layer-tree-root.md new file mode 100644 index 000000000..a38d2f80a --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/layer-tree-root.md @@ -0,0 +1,14 @@ +--- +title: LayerTreeRoot +description: Headless structural primitive for layer tree interfaces. +--- + +# LayerTreeRoot + +`LayerTreeRoot` is the SDK primitive for rendering a layer tree with app-owned markup and styling. + +Use it when you want reusable tree structure and interaction wiring without built-in presentation. + +## Related APIs + +- [useSelectionState](../composables/use-selection-state) diff --git a/packages/docs/programmable/sdk/api/components/layout-controls-root.md b/packages/docs/programmable/sdk/api/components/layout-controls-root.md new file mode 100644 index 000000000..5e35fa000 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/layout-controls-root.md @@ -0,0 +1,15 @@ +--- +title: LayoutControlsRoot +description: Headless root primitive for auto-layout and sizing controls. +--- + +# LayoutControlsRoot + +`LayoutControlsRoot` exposes the slot contract returned by `useLayout()` as a structural primitive. + +Use it when you want a reusable layout-controls shell with app-owned markup. + +## Related APIs + +- [useLayout](../composables/use-layout) +- [Property Panels guide](../../guides/property-panels) diff --git a/packages/docs/programmable/sdk/api/components/page-list-root.md b/packages/docs/programmable/sdk/api/components/page-list-root.md new file mode 100644 index 000000000..de5b1e292 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/page-list-root.md @@ -0,0 +1,40 @@ +--- +title: PageListRoot +description: Headless structural primitive for page list UIs. +--- + +# PageListRoot + +`PageListRoot` is a headless structural primitive for page list interfaces. + +It provides slot props for: + +- pages +- current page id +- divider detection +- page actions like add, switch, rename, and delete + +## Usage + +Use it when you want SDK-provided page-list structure with app-specific rendering and styling. + +## Basic example + +```vue + +
    +
  • + +
  • +
+
+``` + +## Related APIs + +- [usePageList](../composables/use-page-list) diff --git a/packages/docs/programmable/sdk/api/components/position-controls-root.md b/packages/docs/programmable/sdk/api/components/position-controls-root.md new file mode 100644 index 000000000..08f61532f --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/position-controls-root.md @@ -0,0 +1,15 @@ +--- +title: PositionControlsRoot +description: Headless root primitive for position, size, alignment, and transform controls. +--- + +# PositionControlsRoot + +`PositionControlsRoot` exposes position, size, rotation, align, flip, and rotate handlers for the current selection. + +Use it when you want custom position controls without reimplementing editor wiring. + +## Related APIs + +- [usePosition](../composables/use-position) +- [Property Panels guide](../../guides/property-panels) diff --git a/packages/docs/programmable/sdk/api/components/property-list-item.md b/packages/docs/programmable/sdk/api/components/property-list-item.md new file mode 100644 index 000000000..ce63e3015 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/property-list-item.md @@ -0,0 +1,16 @@ +--- +title: PropertyListItem +description: Headless item primitive for a single fills, strokes, or effects row. +--- + +# PropertyListItem + +`PropertyListItem` exposes update, patch, remove, and visibility handlers for one array item inside `PropertyListRoot`. + +Use it when building custom list-row UIs for fills, strokes, or effects. + +## Related APIs + +- [PropertyListRoot](./property-list-root) +- [usePropertyList](../advanced/use-property-list) +- [useNodeProps](../advanced/use-node-props) diff --git a/packages/docs/programmable/sdk/api/components/property-list-root.md b/packages/docs/programmable/sdk/api/components/property-list-root.md new file mode 100644 index 000000000..7611d12fc --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/property-list-root.md @@ -0,0 +1,36 @@ +--- +title: PropertyListRoot +description: Headless structural primitive for fills, strokes, and effects list UIs. +--- + +# PropertyListRoot + +`PropertyListRoot` is a headless structural primitive for array-based property editors. + +It is intended for property UIs like: + +- fills +- strokes +- effects + +It provides slot props for: + +- current items +- mixed-state detection +- add/remove/update/patch operations +- visibility toggling per item + +## Usage + +```vue + +
+ +
+ +
+``` + +## Related APIs + +- [SDK API Overview](../) diff --git a/packages/docs/programmable/sdk/api/components/scrub-input-display.md b/packages/docs/programmable/sdk/api/components/scrub-input-display.md new file mode 100644 index 000000000..5fb005fd1 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/scrub-input-display.md @@ -0,0 +1,33 @@ +--- +title: ScrubInputDisplay +description: Read-only display primitive for ScrubInputRoot non-editing mode. +--- + +# ScrubInputDisplay + +`ScrubInputDisplay` renders the non-editing display for `ScrubInputRoot`. + +It only renders while the scrub input is not in editing mode. + +## Usage + +Use it inside a `ScrubInputRoot` subtree. + +## Props and attrs + + + Passed through to the rendered span element. + + +## Example + +```vue + + + +``` + +## Related APIs + +- [ScrubInputRoot](./scrub-input-root) +- [ScrubInputField](./scrub-input-field) diff --git a/packages/docs/programmable/sdk/api/components/scrub-input-field.md b/packages/docs/programmable/sdk/api/components/scrub-input-field.md new file mode 100644 index 000000000..6ef2d4c0d --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/scrub-input-field.md @@ -0,0 +1,33 @@ +--- +title: ScrubInputField +description: Input element primitive for ScrubInputRoot editing mode. +--- + +# ScrubInputField + +`ScrubInputField` renders the editable input element for `ScrubInputRoot`. + +It only renders while the scrub input is in editing mode. + +## Usage + +Use it inside a `ScrubInputRoot` subtree. + +## Props and attrs + + + Passed through to the rendered input element. + + +## Example + +```vue + + + +``` + +## Related APIs + +- [ScrubInputRoot](./scrub-input-root) +- [ScrubInputDisplay](./scrub-input-display) diff --git a/packages/docs/programmable/sdk/api/components/scrub-input-root.md b/packages/docs/programmable/sdk/api/components/scrub-input-root.md new file mode 100644 index 000000000..856ffb1cc --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/scrub-input-root.md @@ -0,0 +1,69 @@ +--- +title: ScrubInputRoot +description: Headless root primitive for drag-to-scrub numeric input. +--- + +# ScrubInputRoot + +`ScrubInputRoot` is the headless root primitive for drag-to-scrub numeric input. + +It manages: + +- mixed-value display +- editing vs scrubbing state +- pointer-driven numeric scrubbing +- commit semantics for finished edits + +## Props + + + +## Model + + + +## Events + + + +## Slots + + + +## Example + +```vue + +
+ + +
+
+``` + +## Related APIs + +- [ScrubInputField](./scrub-input-field) +- [ScrubInputDisplay](./scrub-input-display) diff --git a/packages/docs/programmable/sdk/api/components/toolbar-item.md b/packages/docs/programmable/sdk/api/components/toolbar-item.md new file mode 100644 index 000000000..977fdd36d --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/toolbar-item.md @@ -0,0 +1,16 @@ +--- +title: ToolbarItem +description: Headless toolbar item primitive for a single editor tool. +--- + +# ToolbarItem + +`ToolbarItem` exposes active-state and selection behavior for one toolbar tool. + +Use it inside `ToolbarRoot` when you want custom button markup but shared tool-selection wiring. + +## Related APIs + +- [ToolbarRoot](./toolbar-root) +- [useToolbar](../advanced/use-toolbar) +- [useToolbarState](../advanced/use-toolbar-state) diff --git a/packages/docs/programmable/sdk/api/components/toolbar-root.md b/packages/docs/programmable/sdk/api/components/toolbar-root.md new file mode 100644 index 000000000..b701e1687 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/toolbar-root.md @@ -0,0 +1,15 @@ +--- +title: ToolbarRoot +description: Headless structural primitive for editor toolbar UIs. +--- + +# ToolbarRoot + +`ToolbarRoot` is the headless toolbar primitive from `@open-pencil/vue`. + +Use it when you want reusable toolbar structure and context with your own buttons, styling, and layout. + +## Related APIs + +- [useEditorCommands](../composables/use-editor-commands) +- [useSelectionCapabilities](../composables/use-selection-capabilities) diff --git a/packages/docs/programmable/sdk/api/components/typography-controls-root.md b/packages/docs/programmable/sdk/api/components/typography-controls-root.md new file mode 100644 index 000000000..7ca21d751 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/typography-controls-root.md @@ -0,0 +1,16 @@ +--- +title: TypographyControlsRoot +description: Headless root primitive for font, alignment, and formatting controls. +--- + +# TypographyControlsRoot + +`TypographyControlsRoot` exposes typography state and handlers from `useTypography()` as a structural primitive. + +Use it when you want custom typography controls with SDK-managed font and formatting behavior. + +## Related APIs + +- [useTypography](../composables/use-typography) +- [FontPickerRoot](./font-picker-root) +- [Property Panels guide](../../guides/property-panels) diff --git a/packages/docs/programmable/sdk/api/composables/index.md b/packages/docs/programmable/sdk/api/composables/index.md new file mode 100644 index 000000000..da405055c --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/index.md @@ -0,0 +1,39 @@ +--- +title: Composables +description: Core composable APIs in @open-pencil/vue. +--- + +# Composables + +These are the main composables most `@open-pencil/vue` consumers will use. + +## Context and canvas + +- [provideEditor](./provide-editor) +- [useEditor](./use-editor) +- [useCanvas](./use-canvas) +- [useCanvasInput](./use-canvas-input) +- [useTextEdit](./use-text-edit) + +## Selection and commands + +- [useSelectionState](./use-selection-state) +- [useSelectionCapabilities](./use-selection-capabilities) +- [useEditorCommands](./use-editor-commands) +- [useMenuModel](./use-menu-model) + +## Property panels + +- [usePosition](./use-position) +- [useLayout](./use-layout) +- [useAppearance](./use-appearance) +- [useTypography](./use-typography) +- [useExport](./use-export) +- [useFillControls](./use-fill-controls) +- [useStrokeControls](./use-stroke-controls) +- [useEffectsControls](./use-effects-controls) + +## Variables and navigation + +- [useVariablesEditor](./use-variables-editor) +- [usePageList](./use-page-list) diff --git a/packages/docs/programmable/sdk/api/composables/provide-editor.md b/packages/docs/programmable/sdk/api/composables/provide-editor.md new file mode 100644 index 000000000..03fc6ad2e --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/provide-editor.md @@ -0,0 +1,46 @@ +--- +title: provideEditor +description: Provide an OpenPencil editor instance to a Vue subtree using injection. +--- + +# provideEditor + +`provideEditor(editor)` makes an OpenPencil editor available to descendant composables and headless primitives through Vue injection. + +This is the foundation for `useEditor()`. + +## Usage + +```ts +import { provideEditor } from '@open-pencil/vue' + +provideEditor(editor) +``` + +## Basic example + +```vue + + + +``` + +## Notes + +The current SDK uses `provideEditor()` and `useEditor()` directly. Some older examples and error messages still refer to an `OpenPencilProvider` component, but the injection model is the real API surface to prefer in docs and app code. + +## Related APIs + +- [useEditor](./use-editor) diff --git a/packages/docs/programmable/sdk/api/composables/use-appearance.md b/packages/docs/programmable/sdk/api/composables/use-appearance.md new file mode 100644 index 000000000..974d20f8d --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-appearance.md @@ -0,0 +1,56 @@ +--- +title: useAppearance +description: Control visibility, opacity, and corner radius state for the current selection. +--- + +# useAppearance + +`useAppearance()` is the appearance-focused control composable for property panels. + +It exposes selection-derived UI state for: + +- visibility +- opacity +- corner radius +- independent corner radii + +## Usage + +```ts +import { useAppearance } from '@open-pencil/vue' + +const appearance = useAppearance() +``` + +## Basic example + +```ts +const { + visibilityState, + opacityPercent, + cornerRadiusValue, + toggleVisibility, + toggleIndependentCorners, +} = useAppearance() +``` + +## Practical examples + +### Toggle selection visibility + +```ts +appearance.toggleVisibility() +``` + +### Edit per-corner radii + +```ts +appearance.updateCornerProp('topLeftRadius', 12) +appearance.commitCornerProp('topLeftRadius', 12, 8) +``` + +## Related APIs + +- [SDK API Overview](../) +- [useLayout](./use-layout) +- [useTypography](./use-typography) diff --git a/packages/docs/programmable/sdk/api/composables/use-canvas-input.md b/packages/docs/programmable/sdk/api/composables/use-canvas-input.md new file mode 100644 index 000000000..07ae046b4 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-canvas-input.md @@ -0,0 +1,73 @@ +--- +title: useCanvasInput +description: Wire canvas pointer input, dragging, selection, resize, rotation, and tool behavior. +--- + +# useCanvasInput + +`useCanvasInput()` connects pointer and mouse interaction to the editor canvas. + +It handles interaction concerns like: + +- selection +- dragging +- resize +- rotation +- panning +- pen/draw flows +- text editing interaction +- scope-aware hit testing + +## Usage + +This composable is typically paired with `useCanvas()` and hit-test helpers from the renderer. + +```ts +useCanvasInput( + canvasRef, + editor, + hitTestSectionTitle, + hitTestComponentLabel, + hitTestFrameTitle, +) +``` + +## Basic example + +```ts +const canvas = useCanvas(canvasRef, editor) + +useCanvasInput( + canvasRef, + editor, + canvas.hitTestSectionTitle, + canvas.hitTestComponentLabel, + canvas.hitTestFrameTitle, +) +``` + +## Practical examples + +### Track cursor movement in canvas space + +```ts +useCanvasInput( + canvasRef, + editor, + hitTestSectionTitle, + hitTestComponentLabel, + hitTestFrameTitle, + (cx, cy) => { + console.log(cx, cy) + }, +) +``` + +## Notes + +This composable is lower-level than most panel logic. It is best suited for editor shells and canvas containers. + +## Related APIs + +- [useCanvas](./use-canvas) +- [useEditor](./use-editor) diff --git a/packages/docs/programmable/sdk/api/composables/use-canvas.md b/packages/docs/programmable/sdk/api/composables/use-canvas.md new file mode 100644 index 000000000..74435dbf0 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-canvas.md @@ -0,0 +1,100 @@ +--- +title: useCanvas +description: Attach CanvasKit-backed rendering to a canvas element for an OpenPencil editor. +--- + +# useCanvas + +`useCanvas()` connects an editor to a real `` element. + +It handles: + +- CanvasKit initialization +- surface creation +- render scheduling +- resize handling +- optional ruler visibility +- renderer readiness callback + +## Usage + +```ts +import { ref } from 'vue' + +import { useCanvas, useEditor } from '@open-pencil/vue' + +const canvasRef = ref(null) +const editor = useEditor() + +useCanvas(canvasRef, editor) +``` + +## Basic example + +```vue + + + +``` + +## Practical examples + +### Disable rulers for an embedded preview + +```ts +useCanvas(canvasRef, editor, { + showRulers: false, +}) +``` + +### Keep drawing buffer for screenshots + +```ts +useCanvas(canvasRef, editor, { + preserveDrawingBuffer: true, +}) +``` + +## Notes + +- `useCanvas()` is renderer-facing and browser-only in practice +- it is responsible for the live canvas pipeline, not app-level file flows +- it should usually be paired with `useCanvasInput()` for interaction handling + +## Related APIs + +- [useEditor](./use-editor) +- [useCanvasInput](./use-canvas-input) +- [useTextEdit](./use-text-edit) + +## Type + +```ts +interface UseCanvasOptions { + showRulers?: boolean + preserveDrawingBuffer?: boolean + onReady?: () => void +} + +function useCanvas( + canvasRef: Ref, + editor: Editor, + options?: UseCanvasOptions, +): void +``` diff --git a/packages/docs/programmable/sdk/api/composables/use-editor-commands.md b/packages/docs/programmable/sdk/api/composables/use-editor-commands.md new file mode 100644 index 000000000..f58b2cc6c --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-editor-commands.md @@ -0,0 +1,90 @@ +--- +title: useEditorCommands +description: Build menus, actions, and command-driven UI on top of the editor. +--- + +# useEditorCommands + +`useEditorCommands()` exposes a command-oriented layer over editor actions. + +It is useful when building: + +- app menus +- context menus +- toolbars +- keyboard-command adapters +- page-move submenus + +## Usage + +```ts +import { useEditorCommands } from '@open-pencil/vue' + +const { commands, menuItem, runCommand, moveSelectionToPage, otherPages } = useEditorCommands() +``` + +## Basic example + +```ts +const { menuItem } = useEditorCommands() + +const editMenu = [ + menuItem('edit.undo', '⌘Z'), + menuItem('edit.redo', '⇧⌘Z'), + { separator: true }, + menuItem('selection.delete'), +] +``` + +## Practical examples + +### Run a command directly + +```ts +const { runCommand } = useEditorCommands() +runCommand('selection.duplicate') +``` + +### Build a “move to page” submenu + +```ts +const { otherPages, moveSelectionToPage } = useEditorCommands() + +const items = otherPages.value.map(page => ({ + label: page.name, + action: () => moveSelectionToPage(page.id), +})) +``` + +## Related APIs + +- [useMenuModel](./use-menu-model) +- [useSelectionState](./use-selection-state) +- [useEditor](./use-editor) + +## Main types + +```ts +type EditorCommandId = + | 'edit.undo' + | 'edit.redo' + | 'selection.selectAll' + | 'selection.duplicate' + | 'selection.delete' + | 'selection.group' + | 'selection.ungroup' + | 'selection.createComponent' + | 'selection.createComponentSet' + | 'selection.createInstance' + | 'selection.detachInstance' + | 'selection.goToMainComponent' + | 'selection.wrapInAutoLayout' + | 'selection.bringToFront' + | 'selection.sendToBack' + | 'selection.toggleVisibility' + | 'selection.toggleLock' + | 'selection.moveToPage' + | 'view.zoom100' + | 'view.zoomFit' + | 'view.zoomSelection' +``` diff --git a/packages/docs/programmable/sdk/api/composables/use-editor.md b/packages/docs/programmable/sdk/api/composables/use-editor.md new file mode 100644 index 000000000..114ac782c --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-editor.md @@ -0,0 +1,73 @@ +--- +title: useEditor +description: Access the current injected OpenPencil editor instance. +--- + +# useEditor + +`useEditor()` returns the current injected OpenPencil editor. + +It is the main entry point for SDK composables and headless primitives that need editor access. + +## Usage + +`useEditor()` must be called inside a subtree where `provideEditor(editor)` has already been called. + +```ts +import { useEditor } from '@open-pencil/vue' + +const editor = useEditor() +``` + +## Basic example + +```vue + + + +``` + +## Practical examples + +### Read selected nodes + +```ts +const editor = useEditor() +const selected = editor.getSelectedNodes() +``` + +### Trigger commands + +```ts +const editor = useEditor() +editor.zoomToFit() +editor.undoAction() +``` + +## Error behavior + +If called outside an editor provider tree, `useEditor()` throws with a helpful message. + +That is intentional — this API should fail loudly when the editor context is missing. + +## Related APIs + +- [provideEditor](./provide-editor) +- [useCanvas](./use-canvas) +- [useSelectionState](./use-selection-state) +- [useEditorCommands](./use-editor-commands) + +## Type + +```ts +function useEditor(): Editor +``` diff --git a/packages/docs/programmable/sdk/api/composables/use-effects-controls.md b/packages/docs/programmable/sdk/api/composables/use-effects-controls.md new file mode 100644 index 000000000..0ae4d7f2f --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-effects-controls.md @@ -0,0 +1,50 @@ +--- +title: useEffectsControls +description: Effects-panel helpers for shadows, blurs, expansion state, and scrub/commit flows. +--- + +# useEffectsControls + +`useEffectsControls()` is the effects-property composable used by effects panels. + +It provides helpers for: + +- default effects +- shadow vs blur logic +- expanded item state +- scrub-preview editing +- commit-on-finish updates +- effect type and color changes + +## Usage + +```ts +import { useEffectsControls } from '@open-pencil/vue' + +const effects = useEffectsControls() +``` + +## Basic example + +```ts +const { effectOptions, createDefaultEffect, toggleExpand, scrubEffect, commitEffect } = useEffectsControls() +``` + +## Practical examples + +### Add a default effect + +```ts +const effect = effects.createDefaultEffect() +``` + +### Preview scrub changes, then commit + +```ts +effects.scrubEffect(node, index, { radius: 12 }) +effects.commitEffect(node, index, { radius: 12 }) +``` + +## Related APIs + +- [PropertyListRoot](../components/property-list-root) diff --git a/packages/docs/programmable/sdk/api/composables/use-export.md b/packages/docs/programmable/sdk/api/composables/use-export.md new file mode 100644 index 000000000..71ded50d5 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-export.md @@ -0,0 +1,57 @@ +--- +title: useExport +description: Manage export settings like scale and format for the current selection. +--- + +# useExport + +`useExport()` is the export-panel composable for selected nodes. + +It manages: + +- export settings rows +- selected node ids +- export name labeling +- supported scales and formats + +## Usage + +```ts +import { useExport } from '@open-pencil/vue' + +const exportState = useExport() +``` + +## Basic example + +```ts +const { + settings, + nodeName, + scales, + formats, + addSetting, + updateScale, + updateFormat, +} = useExport() +``` + +## Practical examples + +### Add another export preset + +```ts +exportState.addSetting() +``` + +### Change the first export to 2x WEBP + +```ts +exportState.updateScale(0, 2) +exportState.updateFormat(0, 'WEBP') +``` + +## Related APIs + +- [useSelectionState](./use-selection-state) +- [useEditor](./use-editor) diff --git a/packages/docs/programmable/sdk/api/composables/use-fill-controls.md b/packages/docs/programmable/sdk/api/composables/use-fill-controls.md new file mode 100644 index 000000000..add37617c --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-fill-controls.md @@ -0,0 +1,38 @@ +--- +title: useFillControls +description: Fill-panel composable with default fill behavior and variable binding support. +--- + +# useFillControls + +`useFillControls()` is the fill-property composable used by fill editing UIs. + +It builds on variable-binding support and adds a reusable default fill value. + +## Usage + +```ts +import { useFillControls } from '@open-pencil/vue' + +const fills = useFillControls() +``` + +## What it gives you + +Along with fill binding behavior, it exposes: + +- `defaultFill` +- fill variable-binding helpers +- selection-aware fill editing state + +## Practical examples + +### Add a new fill row + +```ts +propertyList.add(fills.defaultFill) +``` + +## Related APIs + +- [PropertyListRoot](../components/property-list-root) diff --git a/packages/docs/programmable/sdk/api/composables/use-layout.md b/packages/docs/programmable/sdk/api/composables/use-layout.md new file mode 100644 index 000000000..218cfa205 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-layout.md @@ -0,0 +1,63 @@ +--- +title: useLayout +description: Work with auto-layout, sizing, padding, alignment, and grid tracks. +--- + +# useLayout + +`useLayout()` is the main control composable for layout-related panels. + +It exposes state and actions for: + +- flex vs grid mode +- width/height sizing +- padding +- alignment +- grid template track editing + +## Usage + +```ts +import { useLayout } from '@open-pencil/vue' + +const layout = useLayout() +``` + +## Basic example + +```ts +const { + isGrid, + isFlex, + widthSizing, + heightSizing, + setWidthSizing, + setHeightSizing, +} = useLayout() +``` + +## Practical examples + +### Toggle between uniform and individual padding UI + +```ts +layout.toggleIndividualPadding() +``` + +### Update grid tracks + +```ts +layout.updateGridTrack('gridTemplateColumns', 0, { sizing: 'FIXED', value: 240 }) +layout.addTrack('gridTemplateRows') +``` + +### Change alignment + +```ts +layout.setAlignment('CENTER', 'MAX') +``` + +## Related APIs + +- [usePosition](./use-position) +- [useEditor](./use-editor) diff --git a/packages/docs/programmable/sdk/api/composables/use-menu-model.md b/packages/docs/programmable/sdk/api/composables/use-menu-model.md new file mode 100644 index 000000000..23453dea6 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-menu-model.md @@ -0,0 +1,54 @@ +--- +title: useMenuModel +description: Build app and canvas menu models from the current editor state. +--- + +# useMenuModel + +`useMenuModel()` builds higher-level menu structures on top of editor commands and selection state. + +It is useful when you want ready-to-render menu groups instead of composing commands manually. + +## Usage + +```ts +import { useMenuModel } from '@open-pencil/vue' + +const { appMenu, canvasMenu, selectionLabelMenu } = useMenuModel() +``` + +## Basic example + +```ts +const { canvasMenu } = useMenuModel() +``` + +Render `canvasMenu.value` into your context menu component. + +## Practical examples + +### App-style top menu + +`appMenu` groups entries into: + +- Edit +- View +- Object +- Arrange + +### Context menu with page moves + +`canvasMenu` includes dynamic items like “Move to page” based on current selection and available pages. + +### Selection labels + +`selectionLabelMenu` exposes context-sensitive labels like: + +- `Hide` / `Show` +- `Lock` / `Unlock` + +## Related APIs + +- [useEditorCommands](./use-editor-commands) +- [useSelectionState](./use-selection-state) +- [useSelectionCapabilities](./use-selection-capabilities) diff --git a/packages/docs/programmable/sdk/api/composables/use-page-list.md b/packages/docs/programmable/sdk/api/composables/use-page-list.md new file mode 100644 index 000000000..75fe389e7 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-page-list.md @@ -0,0 +1,50 @@ +--- +title: usePageList +description: Read pages and drive page switching, creation, deletion, and renaming. +--- + +# usePageList + +`usePageList()` is the page-management composable behind page list UIs. + +It exposes: + +- `pages` +- `currentPageId` +- `switchPage` +- `addPage` +- `deletePage` +- `renamePage` + +## Usage + +```ts +import { usePageList } from '@open-pencil/vue' + +const pageList = usePageList() +``` + +## Basic example + +```ts +const { pages, currentPageId, switchPage, addPage } = usePageList() +``` + +## Practical examples + +### Switch pages + +```ts +switchPage(pageId) +``` + +### Create a new page + +```ts +addPage() +``` + +## Related APIs + +- [PageListRoot](../components/page-list-root) +- [useMenuModel](./use-menu-model) diff --git a/packages/docs/programmable/sdk/api/composables/use-position.md b/packages/docs/programmable/sdk/api/composables/use-position.md new file mode 100644 index 000000000..5269ce975 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-position.md @@ -0,0 +1,64 @@ +--- +title: usePosition +description: Read and update selected node position, size, rotation, alignment, and flipping. +--- + +# usePosition + +`usePosition()` is a control composable for position-related UI. + +It exposes selected-node values like: + +- `x` +- `y` +- `width` +- `height` +- `rotation` + +and actions like: + +- align +- flip +- rotate +- scrub/update numeric properties + +## Usage + +```ts +import { usePosition } from '@open-pencil/vue' + +const position = usePosition() +``` + +## Basic example + +```ts +const { x, y, width, height, rotation, updateProp, commitProp } = usePosition() +``` + +## Practical examples + +### Align selected nodes + +```ts +position.align('horizontal', 'center') +position.align('vertical', 'min') +``` + +### Flip selection + +```ts +position.flip('horizontal') +position.flip('vertical') +``` + +### Rotate selection + +```ts +position.rotate(90) +``` + +## Related APIs + +- [useLayout](./use-layout) +- [useAppearance](./use-appearance) diff --git a/packages/docs/programmable/sdk/api/composables/use-selection-capabilities.md b/packages/docs/programmable/sdk/api/composables/use-selection-capabilities.md new file mode 100644 index 000000000..5645455e5 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-selection-capabilities.md @@ -0,0 +1,61 @@ +--- +title: useSelectionCapabilities +description: Derive command-friendly booleans for selection-driven UI and actions. +--- + +# useSelectionCapabilities + +`useSelectionCapabilities()` exposes reactive booleans for whether common editor actions are currently allowed. + +Use it when building: + +- menus +- toolbars +- keyboard shortcuts +- action buttons +- contextual panels + +## Usage + +```ts +import { useSelectionCapabilities } from '@open-pencil/vue' + +const caps = useSelectionCapabilities() +``` + +## Basic example + +```vue + + + +``` + +## Practical examples + +### Gate menu entries + +```ts +const { canMoveToPage, canGoToMainComponent } = useSelectionCapabilities() +``` + +### Enable zoom commands only when useful + +```ts +const { canZoomToSelection } = useSelectionCapabilities() +``` + +## Related APIs + +- [useSelectionState](./use-selection-state) +- [useEditorCommands](./use-editor-commands) diff --git a/packages/docs/programmable/sdk/api/composables/use-selection-state.md b/packages/docs/programmable/sdk/api/composables/use-selection-state.md new file mode 100644 index 000000000..7361c32e7 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-selection-state.md @@ -0,0 +1,77 @@ +--- +title: useSelectionState +description: Reactive selection-derived editor state for current node, count, and selection type. +--- + +# useSelectionState + +`useSelectionState()` exposes reactive selection-derived state from the current editor. + +Use it when you need to render UI based on: + +- whether anything is selected +- how many nodes are selected +- the primary selected node +- whether the current selection is an instance, component, or group + +## Usage + +```ts +import { useSelectionState } from '@open-pencil/vue' + +const selection = useSelectionState() +``` + +## Basic example + +```vue + + + +``` + +## What it returns + +Useful values include: + +- `selectedIds` +- `hasSelection` +- `selectedNode` +- `selectedCount` +- `selectedNodeType` +- `isInstance` +- `isComponent` +- `isGroup` +- `canCreateComponentSet` + +## Practical examples + +### Show instance-only actions + +```ts +const { isInstance } = useSelectionState() +``` + +### Enable component-set creation UI + +```ts +const { canCreateComponentSet } = useSelectionState() +``` + +## Related APIs + +- [useSelectionCapabilities](./use-selection-capabilities) +- [useEditorCommands](./use-editor-commands) +- [useEditor](./use-editor) diff --git a/packages/docs/programmable/sdk/api/composables/use-stroke-controls.md b/packages/docs/programmable/sdk/api/composables/use-stroke-controls.md new file mode 100644 index 000000000..d98c89fc7 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-stroke-controls.md @@ -0,0 +1,47 @@ +--- +title: useStrokeControls +description: Stroke-panel helpers for alignment, side selection, and per-side stroke weights. +--- + +# useStrokeControls + +`useStrokeControls()` is the stroke-property composable used by stroke editing panels. + +It provides: + +- stroke align options +- side presets like all, top, bottom, left, right, custom +- default stroke data +- helpers for per-side border weights + +## Usage + +```ts +import { useStrokeControls } from '@open-pencil/vue' + +const strokes = useStrokeControls() +``` + +## Basic example + +```ts +const { alignOptions, sideOptions, currentAlign, currentSides, selectSide } = useStrokeControls() +``` + +## Practical examples + +### Set stroke alignment + +```ts +strokes.updateAlign('INSIDE', activeNode) +``` + +### Limit a stroke to one side + +```ts +strokes.selectSide('TOP', activeNode) +``` + +## Related APIs + +- [PropertyListRoot](../components/property-list-root) diff --git a/packages/docs/programmable/sdk/api/composables/use-text-edit.md b/packages/docs/programmable/sdk/api/composables/use-text-edit.md new file mode 100644 index 000000000..cf5082b0b --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-text-edit.md @@ -0,0 +1,46 @@ +--- +title: useTextEdit +description: Manage DOM text editing, composition, formatting, and syncing for canvas text nodes. +--- + +# useTextEdit + +`useTextEdit()` bridges DOM text input and the editor’s canvas text editing model. + +It coordinates: + +- textarea-backed text input +- IME composition +- caret blinking +- delete/backspace behavior +- formatting commands like bold/italic/underline +- syncing text changes back into the graph + +## Usage + +```ts +useTextEdit(canvasRef, editor) +``` + +## Basic example + +Use this in the canvas owner component together with `useCanvas()` and `useCanvasInput()`. + +## Practical examples + +### Support formatting shortcuts + +`useTextEdit()` already handles keyboard formatting actions like bold, italic, and underline while text editing is active. + +### Keep canvas and text editor in sync + +It updates graph text and style runs as the user types or edits formatted ranges. + +## Notes + +This is a canvas/editor integration composable, not a generic text-field composable. + +## Related APIs + +- [useCanvas](./use-canvas) +- [useCanvasInput](./use-canvas-input) diff --git a/packages/docs/programmable/sdk/api/composables/use-typography.md b/packages/docs/programmable/sdk/api/composables/use-typography.md new file mode 100644 index 000000000..195ab74b3 --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-typography.md @@ -0,0 +1,64 @@ +--- +title: useTypography +description: Read and update font family, weight, size, alignment, and formatting for text nodes. +--- + +# useTypography + +`useTypography()` is the text-property control composable for text editing panels. + +It exposes: + +- font family +- font weight +- font size +- formatting state +- missing-font status +- helpers for changing family, weight, alignment, and decorations + +## Usage + +```ts +import { useTypography } from '@open-pencil/vue' + +const typography = useTypography() +``` + +## Basic example + +```ts +const { + fontFamily, + fontWeight, + fontSize, + activeFormatting, + setFamily, + setWeight, + setAlign, +} = useTypography() +``` + +## Practical examples + +### Load and switch a font family + +```ts +const typography = useTypography({ + loadFont: async (family, style) => { + await myFontLoader(family, style) + }, +}) +``` + +### Toggle formatting + +```ts +typography.toggleBold() +typography.toggleItalic() +typography.toggleDecoration('UNDERLINE') +``` + +## Related APIs + +- [useTextEdit](./use-text-edit) +- [useSelectionState](./use-selection-state) diff --git a/packages/docs/programmable/sdk/api/composables/use-variables-editor.md b/packages/docs/programmable/sdk/api/composables/use-variables-editor.md new file mode 100644 index 000000000..b8f98677b --- /dev/null +++ b/packages/docs/programmable/sdk/api/composables/use-variables-editor.md @@ -0,0 +1,44 @@ +--- +title: useVariablesEditor +description: Compose the variables dialog state, table columns, and TanStack table wiring. +--- + +# useVariablesEditor + +`useVariablesEditor()` is a higher-level variables-domain composable for building a variables dialog or editor screen. + +It combines: + +- variables dialog state +- variables table columns +- TanStack Vue Table wiring +- collection/mode helpers + +## Usage + +```ts +const variables = useVariablesEditor({ + colorInput: ColorInput, + icons, + fallbackIcon, + deleteIcon, +}) +``` + +## What it returns + +It includes the lower-level dialog/table state plus: + +- `columns` +- `table` +- `hasCollections` + +## Practical examples + +### Build a variables dialog + +Use `useVariablesEditor()` when you want one composable that already wires the table and action handlers together. + +## Related APIs + +- [SDK API Overview](../) diff --git a/packages/docs/programmable/sdk/api/index.md b/packages/docs/programmable/sdk/api/index.md new file mode 100644 index 000000000..8859cb675 --- /dev/null +++ b/packages/docs/programmable/sdk/api/index.md @@ -0,0 +1,20 @@ +--- +title: API Reference +description: Reference documentation for @open-pencil/vue components, composables, and advanced APIs. +--- + +# API Reference + +The Vue SDK reference is organized into three sections. + + + + + + + +## Suggested path + +- Start with **Components** if you are building reusable editor UI primitives. +- Start with **Composables** if you are wiring editor state and actions. +- Use **Advanced** only when you need lower-level helpers or primitive contexts. diff --git a/packages/docs/programmable/sdk/architecture.md b/packages/docs/programmable/sdk/architecture.md new file mode 100644 index 000000000..608b6817a --- /dev/null +++ b/packages/docs/programmable/sdk/architecture.md @@ -0,0 +1,115 @@ +--- +title: SDK Architecture +description: Folder structure, public API boundaries, and composition patterns in @open-pencil/vue. +--- + +# SDK Architecture + +`@open-pencil/vue` is the Vue-facing layer over `@open-pencil/core`. + +It does not own the editor model itself. It adapts the core editor into: + +- Vue injection +- reactive composables +- headless structural primitives +- canvas and input wiring + +## Folder structure + +This package is organized by domain. + +### Component families + +- `Canvas/` +- `ColorPicker/` +- `FillPicker/` +- `FontPicker/` +- `GradientEditor/` +- `LayerTree/` +- `PageList/` +- `PropertyList/` +- `ScrubInput/` +- `Toolbar/` + +These contain structural/headless primitives and local helpers. + +### Controls + +`controls/` contains property-panel and editor control composables: + +- `usePosition` +- `useLayout` +- `useAppearance` +- `useTypography` +- `useExport` +- `useFillControls` +- `useStrokeControls` +- `useEffectsControls` +- `useNodeProps` +- `usePropScrub` + +### Variables + +`VariablesEditor/` contains variables-domain composables and state wiring. + +### Selection + +`selection/` contains selection-derived editor state and capabilities. + +### Context + +`context/` contains editor injection helpers: + +- `EDITOR_KEY` +- `provideEditor` +- `useEditor` + +### Internal + +`internal/` contains cross-cutting utilities not intended as primary headless primitives. + +## Public API philosophy + +### Prefer composables + +If the problem is mostly control logic, state derivation, or editor actions, expose a composable. + +### Keep headless primitives for meaningful structure + +Use component roots when they coordinate structure, children, slots, or context. + +Examples: + +- `PageListRoot` +- `PropertyListRoot` +- `ToolbarRoot` + +### Avoid broad context-dump slots + +Prefer focused slot props or direct composable usage over giant `v-slot="ctx"` payloads. + +## App vs SDK responsibility + +### SDK owns + +- editor integration +- reusable headless logic +- reusable UI structure without styling assumptions +- canvas rendering integration + +### App owns + +- styling +- layout shells +- routing +- product file flows +- toasts, menus, and app-specific UX + +## Practical rule of thumb + +If a piece of logic could be reused in a different OpenPencil-based app without bringing app styling with it, it probably belongs in `@open-pencil/vue`. + +## Related pages + +- [SDK Getting Started](./getting-started) +- [API Reference](./api/) diff --git a/packages/docs/programmable/sdk/getting-started.md b/packages/docs/programmable/sdk/getting-started.md new file mode 100644 index 000000000..3a57d6e3b --- /dev/null +++ b/packages/docs/programmable/sdk/getting-started.md @@ -0,0 +1,124 @@ +--- +title: SDK Getting Started +description: Set up @open-pencil/vue with createEditor, provideEditor, and a canvas. +--- + +# SDK Getting Started + +## Installation + +The SDK lives in the monorepo today and is consumed by the app itself. + +```ts +import { createEditor } from '@open-pencil/core/editor' +import { provideEditor, useCanvas } from '@open-pencil/vue' +``` + +## Mental model + +There are three layers: + +1. `@open-pencil/core` — framework-agnostic editor engine +2. `@open-pencil/vue` — Vue composables and headless primitives +3. your app — styling, routing, file flows, product-specific UI + +## Minimal setup + +### 1. Create an editor + +```ts +import { createEditor } from '@open-pencil/core/editor' + +const editor = createEditor({ + width: 1200, + height: 800, +}) +``` + +### 2. Provide it to Vue + +```vue + + + +``` + +You can think of this as the provider layer for the editor tree. The docs prefer `provideEditor()` directly because that is the current real API surface. + +### 3. Attach a canvas + +```vue + + + +``` + +## Using composables + +Once the editor is provided, child components can read selection and issue commands: + +```ts +import { useEditorCommands, useSelectionState } from '@open-pencil/vue' + +const selection = useSelectionState() +const commands = useEditorCommands() +``` + +## Basic example + +```vue + + + +``` + +## Next steps + +- [Architecture](./architecture) +- [API Reference](./api/) +- [useEditor](./api/composables/use-editor) +- [useCanvas](./api/composables/use-canvas) diff --git a/packages/docs/programmable/sdk/guides/custom-editor-shell.md b/packages/docs/programmable/sdk/guides/custom-editor-shell.md new file mode 100644 index 000000000..632a3b6d4 --- /dev/null +++ b/packages/docs/programmable/sdk/guides/custom-editor-shell.md @@ -0,0 +1,105 @@ +--- +title: Custom Editor Shell +description: Build your own editor shell with provideEditor, CanvasRoot, menus, panels, and toolbars. +--- + +# Custom Editor Shell + +A typical OpenPencil Vue app has three layers: + +1. `@open-pencil/core` creates the editor +2. `@open-pencil/vue` adapts it into Vue composables and headless primitives +3. your app renders the shell, styling, and product UX + +## Why this matters + +The built-in OpenPencil app is only one possible shell. + +You can build a very different one for a focused workflow: an embedded editor inside another product, an internal asset tool, a template editor, an annotation UI, or an AI-assisted editing surface with custom controls. + +That is the main reason the SDK exists. + +## Recommended composition + +A practical shell often looks like this: + +- provider at the top with `provideEditor()` +- canvas in the center +- page/layer navigation on one side +- properties on the other side +- menus and toolbars driven by composables + +## Example + +```vue + + + +``` + +## Why this split works + +- the SDK owns editor integration and reusable headless logic +- your app owns layout, styling, and product-specific actions +- composables can power menus and panels without extra wrapper components + +## Related APIs + +- [provideEditor](../api/composables/provide-editor) +- [useCanvas](../api/composables/use-canvas) +- [ToolbarRoot](../api/components/toolbar-root) +- [PageListRoot](../api/components/page-list-root) +- [LayerTreeRoot](../api/components/layer-tree-root) diff --git a/packages/docs/programmable/sdk/guides/navigation-panels.md b/packages/docs/programmable/sdk/guides/navigation-panels.md new file mode 100644 index 000000000..b43fd22c0 --- /dev/null +++ b/packages/docs/programmable/sdk/guides/navigation-panels.md @@ -0,0 +1,60 @@ +--- +title: Navigation Panels +description: Build page and layer sidebars with PageListRoot, LayerTreeRoot, and selection state. +--- + +# Navigation Panels + +OpenPencil sidebars usually combine two concerns: + +- page navigation +- layer navigation + +The Vue SDK provides headless primitives for both. + +## Page navigation + +Use `PageListRoot` or `usePageList()`. + +```vue + +
+ + +
+
+``` + +## Layer navigation + +Use `LayerTreeRoot` when you want SDK-managed tree structure but app-owned presentation. + +```vue + + + +``` + +## Practical pattern + +A common layout is: + +- pages at the top of the sidebar +- layers below +- details or inline rename controls embedded in your row components + +## Related APIs + +- [usePageList](../api/composables/use-page-list) +- [PageListRoot](../api/components/page-list-root) +- [LayerTreeRoot](../api/components/layer-tree-root) +- [useSelectionState](../api/composables/use-selection-state) diff --git a/packages/docs/programmable/sdk/guides/property-panels.md b/packages/docs/programmable/sdk/guides/property-panels.md new file mode 100644 index 000000000..0e3933cae --- /dev/null +++ b/packages/docs/programmable/sdk/guides/property-panels.md @@ -0,0 +1,83 @@ +--- +title: Property Panels +description: Build property panels with control composables and headless list primitives. +--- + +# Property Panels + +Property panels in `@open-pencil/vue` are intentionally composable-first. + +If a panel mostly needs selection-derived values and update actions, prefer composables. +If a panel needs reusable array/list structure, use a headless primitive like `PropertyListRoot`. + +## Common control composables + +For standard property sections, start with: + +- `usePosition()` +- `useLayout()` +- `useAppearance()` +- `useTypography()` +- `useExport()` + +For list-style panels, use: + +- `useFillControls()` +- `useStrokeControls()` +- `useEffectsControls()` + +## Example: position panel + +```vue + + + +``` + +## Example: fills panel + +```vue + + + +``` + +## Rule of thumb + +- use composables for direct control logic +- use structural primitives when repeated list/tree/slot coordination is the hard part + +## Related APIs + +- [usePosition](../api/composables/use-position) +- [useLayout](../api/composables/use-layout) +- [useAppearance](../api/composables/use-appearance) +- [useTypography](../api/composables/use-typography) +- [useFillControls](../api/composables/use-fill-controls) +- [useStrokeControls](../api/composables/use-stroke-controls) +- [useEffectsControls](../api/composables/use-effects-controls) +- [PropertyListRoot](../api/components/property-list-root) diff --git a/packages/docs/programmable/sdk/index.md b/packages/docs/programmable/sdk/index.md new file mode 100644 index 000000000..7ebae84e5 --- /dev/null +++ b/packages/docs/programmable/sdk/index.md @@ -0,0 +1,59 @@ +--- +title: Vue SDK +description: Build OpenPencil-powered editors with headless Vue composables and primitives. +--- + +# Vue SDK + +`@open-pencil/vue` exists so OpenPencil can be more than a standalone design app. + +The goal is to make OpenPencil a toolkit you can embed into other products, internal tools, and workflow-specific editors — not just a single default UI. + +The OpenPencil app is one composition of that toolkit. The SDK is how you build a different one. + +It gives you: + +- injected editor context +- CanvasKit-backed canvas rendering +- selection, commands, menu, and property-panel composables +- headless structural primitives like `PageListRoot`, `PropertyListRoot`, and `ToolbarRoot` + +## Start here + + + + + + + + +## Why the SDK exists + +Different products and teams need different editing surfaces. + +Sometimes you want a full design editor. Sometimes you want a focused canvas inside another app. Sometimes you want an internal workflow tool, a template editor, or an AI-assisted editing surface built around a narrow use case. + +The SDK is the layer that makes those possible. + +## Design principles + +- **Headless first**: logic and structure, not app styling +- **Composable over wrapper**: use composables when there is no meaningful structural coordination +- **Intentional public API**: stable exports from `packages/vue/src/index.ts` +- **Framework-aware**: Vue integration over `@open-pencil/core` + +## How to think about the package + +The SDK has two main layers: + +1. **Composables** for editor state and actions +2. **Primitives** for meaningful UI structure + +If you only need editor state and actions, start with composables. +If you are building reusable editor UI building blocks, start with primitives. + +## API sections + +- [Components](/programmable/sdk/api/components/) +- [Composables](/programmable/sdk/api/composables/) +- [Advanced](/programmable/sdk/api/advanced/)