OpenPencil 导航面板开发指南:用 PageListRoot、LayerTreeRoot 构建页面与图层面板
前端桌面应用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),仅供参考

相关新闻

MA_SRUKF-PHD-SLAM:时变特征下激光SLAM的滤波新思路

MA_SRUKF-PHD-SLAM:时变特征下激光SLAM的滤波新思路

简介:这份资源面向机器人、自动驾驶及多目标跟踪方向的学习者与研究者,聚焦平方根无迹卡尔曼滤波(SRUKF)在概率假设密度(PHD)SLAM中的实现,帮助理解非线性系统状态估计与多目标环境建图的结合方…

2026/10/28 1:10:04 阅读更多 →
智慧校园Android客户端毕设源码解析:从跑通到会改

智慧校园Android客户端毕设源码解析:从跑通到会改

简介:一套面向高校学生群体的智慧校园Android客户端及管理系统,覆盖校园资讯浏览与互动、生活学习记录、任务提醒与进度管理、团队建设与任务协作等场景,适合计算机相关专业学生用于毕业设计、课程设计或项目初期演示。资源包含配套文档说明&…

2026/10/28 1:12:10 阅读更多 →
深入解析 Mach-O 的 __RODATA 段:布局、加载与崩溃排查实战

深入解析 Mach-O 的 __RODATA 段:布局、加载与崩溃排查实战

排查一个诡异的崩溃时,我头一回认真盯着Mach-O的__RODATA段看了半天。崩溃堆栈里符号化出来的一行地址,干干净净落在只读数据区,既不像是执行了代码,也不像是访问了__DATA,一度让我怀疑是链接器在搞鬼。后来才意识到&a…

2026/10/25 10:53:19 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/27 17:33:32 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/27 4:06:49 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/27 7:21:53 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/27 18:20:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/27 6:20:47 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/27 4:06:50 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 13:55:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 7:21:50 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 19:19:37 阅读更多 →