前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载本文围绕 OpenPencil Vue SDK 的无头headless导航原语展开讲解如何用PageListRoot/usePageList()搭建页面列表、用LayerTreeRoot搭建图层树并结合useSelectionState驱动选择状态。读完本文你将掌握侧边栏导航面板的完整构建思路SDK 负责树结构与交互逻辑应用自由决定样式与行组件并可参考官方应用的 PagesPanel.vue 与 LayerTree 获得可落地的实现细节。导航面板页面导航与图层导航的组合在设计工具中侧边栏sidebar通常承担两类职责页面导航page navigation列出文档内的多个页面支持切换、新建、重命名、删除与排序图层导航layer navigation以树形结构展示当前页面中的节点层级支持选中、展开/折叠、可见性/锁定切换与重命名。OpenPencil 的 Vue SDKopen-pencil/vue为这两类职责分别提供了无头原语headless primitivesPageListRoot与LayerTreeRoot。所谓无头是指这些组件不携带任何预设外观——它们只负责维护数据、状态与交互行为把pages、items、selectedIds等状态通过作用域插槽v-slot暴露出来由应用自行决定 Markup 与样式。这正是 LayerTreeRoot.vue 中 SDK-managed tree structure but app-owned presentation 的设计意图。页面导航PageListRoot 与 usePageList()PageListRoot 基础用法原文档给出的最小示例清晰展示了PageListRoot的用法——通过v-slot解构出pages、currentPageId、switchPage、addPagePageListRoot v-slot{ pages, currentPageId, switchPage, addPage } div button v-forpage in pages :keypage.id clickswitchPage(page.id) {{ page.name }} /button button clickaddPage()New page/button /div /PageListRootPageListRoot本身不产出任何 DOM 结构v-for与按钮完全由应用编写。页面数据来自当前编辑器场景图scene graph因此页面的增删、切换都会立即反映到插槽数据中。插槽暴露的完整数据与动作结合 PageListRoot.vue 的插槽实现PageListRoot实际暴露插槽属性类型说明pages页面数组当前文档的全部页面id、name、childIds等currentPageIdstring当前激活页面 id可用于高亮当前页isDivider(page) boolean判断某页是否为分割线页无子节点且名称匹配分隔符模式actions对象页面操作的统一集合add、switch、rename、delete、moveactions与组件原生emit一一对应add、switch、rename、delete、move。也就是说即使不直接调用 composable也能在插槽内通过actions.add()、actions.switch(pageId)等完成全部页面管理操作这比原文档示例中的switchPage/addPage覆盖面更完整。分割线divider识别机制PageListRoot支持一个可选 propdividerPattern?: RegExp用于识别作为视觉分隔符使用的页面。其默认模式为/^[-–—*\s]$/即名称仅由连字符、短破折号、长破折号、星号与空白组成的页面被视为分割线同时要求该页childIds.length 0没有子节点。应用可用isDivider在插槽内决定是否渲染分隔线样式——官方 PagesPanel.vue 中就用isDivider(pg)渲染一条细线并支持双击重命名。usePageList()页面管理的 composable 形态当不需要组件封装、只想在逻辑层直接操作页面时可以使用usePageList()。其实现位于 packages/vue/src/primitives/PageList/usePageList.ts返回const { editor, // Editor 实例 pages, // 响应式页面数组useSceneComputed(editor.graph.getPages()) currentPageId, // computed来自 editor.state.currentPageId switchPage, // editor.switchPage addPage, // editor.addPage deletePage, // editor.deletePage movePage, // editor.movePage renamePage // editor.renamePage } usePageList()从源码可以看出pages通过useSceneComputed包装editor.graph.getPages()因而对场景图变更保持响应式而switchPage、addPage等动作直接透传自 Editor 实例。也就是说PageListRoot与usePageList()共享同一套底层实现前者只是多了插槽与 emit 的声明式包装。官方应用在 PagesPanel.vue 中直接消费插槽内的actionsactions.add()、actions.switch(pg.id)、actions.rename、actions.delete、actions.move并结合useInlineRename实现双击行内重命名、useFlatReorderDrag实现拖拽排序。图层导航LayerTreeRoot基础用法当希望 SDK 管理树的构建与行为、应用负责渲染与样式时使用LayerTreeRootLayerTreeRoot v-slot{ items, selectedIds, select, toggleExpand, getKey, getChildren } TreeView :itemsitems :selected-idsselectedIds :get-keygetKey :get-childrengetChildren selectselect toggle-expandtoggleExpand / /LayerTreeRoot示例中的TreeView是应用自绘的树组件items提供扁平节点数组getKey/getChildren用于恢复层级select与toggleExpand则回传用户交互。整个过程中 SDK 负责树从哪来、选中怎么算、展开怎么存。插槽暴露的完整状态与动作根据 LayerTreeRoot.vue插槽实际提供插槽属性说明itemsLayerNode[]树节点数组flattenItems由底层 reka-uiTreeRoot提供的扁平化条目visibleRows展开状态下可见的行{ node, level, hasChildren }配合虚拟滚动expanded当前展开的节点 id 数组v-model 语义treeVersion树重建版本号用于强制刷新缓存selectedIds当前选中的节点 id 集合Setstringfocused树是否聚焦draggingId/instruction/instructionTargetId拖拽行、拖拽指令与目标 idactionsselect、toggleExpand、setFocused、setVirtualizer树节点LayerNode的结构定义在 packages/vue/src/primitives/LayerTree/context.tsid、name、type、layoutMode、visible、locked以及可选的children——恰好覆盖图层树行渲染所需的全部信息。树模型构建与增量更新LayerTreeRoot内部通过buildLayerTreeModel(editor.graph, editor.state.currentPageId)从当前页面构建树模型并订阅了以下编辑器事件以保持同步见 LayerTreeRoot.vuegraph:replaced、page:changed→ 整体重建树node:created、node:deleted、node:reparented、node:reordered→ 调度微任务重建node:updated→ 只对name、type、layoutMode、visible、locked等可修补字段做局部 patch避免整树重建selection:changed→ 自动展开选中节点的祖先并将选中项滚动到可见区域。这种全量重建 增量 patch的组合保证了图层树在大型文档下仍能保持流畅也为虚拟滚动提供了稳定的行数据源。选择行为additive 与 rangeLayerTreeRoot的select动作接收(id, selection)其中selection既可以是布尔值也可以是{ additive, range }模式对象。官方应用 LayerTree.vue 中把鼠标事件转换为选择模式按住 Meta/Ctrl 点击为增量选择additive按住 Shift 点击为范围选择range。底层还会通过layerSelectionForTarget依据可见行、当前选中集合与选择锚点计算最终选中集并在非增量模式下同步画布容器作用域syncCanvasScope。行渲染、虚拟滚动与拖拽行组件SDK 还导出配套的LayerTreeItem行原语以及useLayerTree()组合式函数用于在子组件中读取树上下文在LayerTreeRoot之外调用useLayerTree()会抛出[open-pencil] useLayerTree() called outside LayerTreeRoot错误见 context.ts。虚拟滚动LayerTreeRoot接受indentPerLevel默认16每层缩进像素prop并通过setVirtualizer让应用把 reka-uiTreeVirtualizer注册进来配合visibleRows实现scrollToIndex定位选中自动滚动即依赖此机制。拖拽拖拽能力由useLayerDrag提供指令类型包括reorder-above、reorder-below、make-child应用可据此渲染拖拽指示器并实现图层重排/嵌套。官方应用对照官方 LayerTree/LayerTree.vue 是LayerTreeRoot的生产级用法范例它组合 reka-ui 的TreeItem、TreeVirtualizer与ContextMenu用LayerTreeNodeRow.vue/LayerTreeRenameRow.vue作为行组件并在右键时先选中目标节点再弹出上下文菜单。整个 LayersPanel.vue 再通过垂直 Splitter 把PagesPanel页面列表与图层树上下分区构成了页面上、图层下的典型侧边栏。用 useSelectionState 驱动面板状态图层树与属性面板都依赖当前选了什么这一派生状态。useSelectionState()从当前编辑器读取选择并计算出一组响应式派生值实现位于 packages/vue/src/editor/selection-state/use.ts返回字段说明selectedIds选中节点 id 集合SetstringhasSelection是否至少选中一个节点selectedNode主选中节点首选节点selectedCount选中节点数量selectedNodeType主选中节点的类型INSTANCE/COMPONENT/GROUP等isInstance是否为实例类型INSTANCEisComponent是否为组件类型COMPONENTisGroup是否为编组类型GROUPcanCreateComponentSet选中项 ≥ 2 且全部为COMPONENT时可创建组件集基础用法示例script setup langts import { useSelectionState } from open-pencil/vue const { hasSelection, selectedCount, isInstance } useSelectionState() /script template div classtext-xs text-muted span v-if!hasSelectionNo selection/span span v-else {{ selectedCount }} selected span v-ifisInstance· instance/span /span /div /template从源码看selectedIds同样经useSceneComputed包装editor.state.selectedIdscanCreateComponentSet的判定逻辑是遍历选中集合、要求每个节点类型均为COMPONENT且数量不少于 2。这些派生值可以直接用于属性面板的启用/禁用状态、图层树右键菜单项显隐以及与 useSelectionCapabilities、useEditorCommands 联动实现命令级操作。典型布局页面在上、图层在下综合官方实践与 SDK 设计一个完整的导航侧边栏通常遵循如下排布页面列表位于侧边栏顶部用PageListRoot渲染页面按钮/行高亮currentPageId提供新建、重命名、删除与拖拽排序图层树位于其下用LayerTreeRoot渲染节点层级行内显示可见性、锁定图标支持多选、展开折叠与拖拽重排详情与行内重命名嵌入行组件详情信息如类型、布局模式与重命名输入框都放在行组件内部官方应用通过useInlineRename实现双击进入编辑态参见 PagesPanel.vue 与 LayerTree.vue。这一模式的核心收益在于结构、状态与行为由 SDK 保证一致性而视觉与交互细节完全由应用掌控——无论是官方编辑器皮肤还是完全自定义的编辑器外壳custom editor shell都可以复用同一套无头原语。相关 API 与延伸阅读usePageList页面管理 composable 的完整文档PageListRoot页面列表无头组件文档LayerTreeRoot图层树无头组件文档useSelectionState选择派生状态 composable 文档Property Panels 指南与导航面板配套的属性面板构建思路Custom Editor Shell 指南自定义编辑器外壳的组装方式Vue SDK 入口PageListRoot、LayerTreeRoot、LayerTreeItem、usePageList等公共导出的统一出口Getting StartedSDK 初始化与编辑器上下文注入赞分享前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载相关推荐用 PageListRoot 构建无样式的页面导航面板OpenPencil Vue SDK 页面列表演示原语用 PageListRoot 构建无样式的页面导航面板OpenPencil Vue SDK 页面列表演示原语 PageListRoot 是 OpenPenci前端桌面应用AI 应用MCP 服务OpenPencil 图层与页面完全指南层级树、多页面管理与属性面板实战OpenPencil 图层与页面完全指南层级树、多页面管理与属性面板实战 本文是 OpenPencil开源、可编程、AI 原生的设计编辑器用户指南系列的一前端桌面应用AI 应用MCP 服务OpenPencil 图层与页面管理实战图层树、多页面切换与属性面板全解析OpenPencil 图层与页面管理实战图层树、多页面切换与属性面板全解析 导读 本文以 OpenPencil 官方用户指南 packages/docs/e前端桌面应用AI 应用MCP 服务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
