Penpot 插件 API 1.0 升级指南:Beta changelog 中的破坏性变更、废弃项与新增能力全解析
本篇文章以 Penpot 仓库中 docs/plugins/beta-changelog.md 为核心骨架,系统拆解 Penpot 插件 API 1.0 版本发布时的全部变更:包括类型命名去前缀、frame→board 等 UI 对齐重命名、事件监听从"传回调退订"改为"按 id 退订"、一批 getXxx 方法被只读属性取代,以及评论、导出、撤销块、标尺参考线、原型能力访问等新增特性。文中逐条结合 插件类型定义、插件运行时实现 与 插件 API 测试套件 等仓库源码给出佐证,帮助插件开发者据此编写一份可落地的迁移清单。
一、版本背景:为什么 1.0 是一个里程碑
Beta changelog 的开篇明确了这一版本的定位:1.0 是插件 API 进入稳定阶段的标志。项目承诺自该版本起将尽力不再引入新的破坏性变更,若必须废弃,也会尽量让废弃行为保持向后兼容——旧行为在下一个版本仍可用,但会在后续版本中彻底移除。因此,凡是运行在 1.0 之前 API 之上的存量插件,都值得对照本文进行一次系统性升级,避免在未来版本中失效。
与本文档配套的资源,在仓库中均有对应实体可供核对:
| 文档中提到的资源 | 本仓库对应位置 |
|---|---|
| 重新编写的 API 文档 | docs/plugins/api.md |
| 插件创建与开发教程 | docs/plugins/getting-started.md、docs/plugins/create-a-plugin.md、docs/plugins/deployment.md |
| 插件 API 类型定义(1.0 命名规范的最终形态) | plugins/libs/plugin-types/index.d.ts |
插件运行时(真实提供给插件的 penpot 全局对象实现) |
plugins/libs/plugins-runtime/src/lib/api/index.ts |
| API 测试套件与可用样例插件 | plugins/apps/plugin-api-test-suite、plugins/apps |
二、命名规范重构:去掉 Penpot 前缀
1.0 最宏观的调整是类型命名统一去掉 Penpot 前缀。此前形如 PenpotShape、PenpotFile、PenpotPage 的类型,现在直接叫 Shape、File、Page。
之所以能安全去前缀,是因为 1.0 起插件运行在独立的命名空间中,penpot 前缀(命名空间)与类型本身已经能形成足够清晰的区分,继续把 Penpot 粘在类型名上反而冗余。在当前仓库的 plugins/libs/plugin-types/index.d.ts 中可以确认这一命名结果:例如 Shape、File、Page、Board、Rectangle、Ellipse、Boolean、GuideX、Bounds 等都是去前缀后的顶层导出类型;文档中强调完整清单以 API 文档为准,本仓库内的 docs/plugins/api.md 即为其本地镜像。
对插件作者的影响集中在 import 语句与 JSDoc/TS 注解 的机械替换上。若使用 TypeScript,重命名后类型往往能直接从 penpot 全局环境自动推断,可进一步减少显式类型标注。
三、与 UI 对齐的类型/方法重命名
为了让 API 术语与 Penpot 界面保持一致,1.0 做了四组语义性重命名。注意:这四组不只是类型改名,还连带改了构造函数与字段名,属于名副其实的破坏性变更。
3.1 frame → board
Penpot 界面中"画板(Board)"概念取代了旧的"帧(Frame)"叫法,全部联动改名如下:
| 旧名称(1.0 前) | 新名称(1.0 起) |
|---|---|
PenpotFrame 类型 |
Board 类型 |
penpot.createFrame() |
penpot.createBoard() |
shape.frameX / shape.frameY |
shape.boardX / shape.boardY |
PenpotFrameGuideX 类型 |
GuideX 类型 |
源码佐证:
- 类型定义中
Board是核心形状接口(plugins/libs/plugin-types/index.d.ts),并派生出了VariantContainer extends Board(第 365 行)等子类型; - 运行时实现暴露
createBoard(): Board(plugins/libs/plugins-runtime/src/lib/api/index.ts); - 形状坐标字段已确认为
boardX(index.d.ts),沿用旧名frameX/frameY的代码会直接失去字段。
3.2 rect → rectangle
| 旧名称 | 新名称 |
|---|---|
PenpotRectangle 类型 |
Rectangle 类型 |
矩形类型 Rectangle 在 index.d.ts 中定义,属于基础形状(Shape 联合类型)的一员。
3.3 circle → ellipse
| 旧名称 | 新名称 |
|---|---|
PenpotCircle 类型 |
Ellipse 类型 |
penpot.createCircle() |
penpot.createEllipse() |
源码佐证:运行时实现 createEllipse(): Ellipse(api/index.ts),类型定义在 index.d.ts。
3.4 bool → boolean
| 旧名称 | 新名称 |
|---|---|
PenpotBool 类型 |
Boolean 类型 |
布尔形状接口 Boolean(index.d.ts)配合 BooleanType = 'union' | 'difference' | 'exclude' | 'intersection'(第 442 行)使用。这里把缩写词补全为完整单词,符合"去除模糊缩写"的整体趋势。
迁移建议:这四组改动建议用全局搜索 + 批量替换完成,搜索关键词包括 PenpotFrame、createFrame、frameX、frameY、PenpotRectangle、PenpotCircle、createCircle、PenpotBool,替换为上文表格中的新名称。注意 frameX/frameY 这类字段名也可能出现在序列化数据或自定义属性读写逻辑中,替换后应结合类型定义编译检查一遍。
四、事件系统重构:penpot.on / penpot.off 改为 id 句柄
1.0 之前,注册监听与移除监听的代码长这样:
// 1.0 之前:off 需要回传原始回调
penpot.on('pagechange', myListener); // 注册监听
penpot.off('pagechange', myListener); // 用同一回调注销
这种写法的问题在于:如果回调被包装、绑定或匿名化,调用方很难再拿到"同一个引用"去注销,容易造成监听泄漏。1.0 改为 on 返回一个事件 id,off 直接消费这个 id:
// 1.0 起:off 只需传 on 返回的 id
const id = penpot.on('pagechange', myListener); // 注册监听并拿到句柄
penpot.off(id); // 用 id 注销监听
文档同时给出明确承诺:旧行为已被标记为 deprecated——下一版本中依旧可用,但会在后续版本被移除,插件应尽快迁移。
源码佐证:类型定义中 off(listenerId: symbol): void 的签名(plugins/libs/plugin-types/index.d.ts)说明 id 句柄正是这一机制的数据类型;而注册监听的对象统一使用 penpot.on(...),且事件种类由一个 EventsMap(第 1558 行起)描述,其中可以看到与本文档所述新增事件直接对应的条目:
shapechange: Shape(第 1587 行):形状变化事件,文档指出使用前需在props中携带目标shapeId(参见第 103-107 行注释示例);contentsave: void(第 1590-1592 行):文件内容在后端保存完成后触发,回调不接收参数。
五、getXxx 方法移除:统一改为只读属性
1.0 移除了一批"查询类"方法,全部收敛为等价的属性访问,风格从"命令式取数"转向"声明式状态":
| 被移除的方法 | 替代写法(属性) |
|---|---|
getPage() |
currentPage |
getFile() |
currentFile |
getTheme() |
theme |
getSelected() |
selection |
getSelectedShapes() |
selection(与原 getSelectedShapes 语义一致) |
源码佐证(均可在 plugins/libs/plugin-types/index.d.ts 的 Context 接口中找到):
readonly currentFile: File | null(第 810 行),注释示例给出const fileData = context.currentFile;;readonly currentPage: Page | null(第 820 行);readonly theme: Theme(第 898 行),取值集合为'light' | 'dark'(主题类型定义见第 4458 行附近);selection: Shape[](第 914 行),即当前选中的形状数组。
注意 getSelected 与 getSelectedShapes 的语义差异:selection 属性对齐的是后者的语义(返回 Shape[])。另外文档提示 getSelected 原本返回的选中信息在 1.0 中统一由 selection 承担,插件代码中如需"选中的 id 列表",可通过 selection 中每个 Shape 的 id 字段推导。
迁移范例:
// 1.0 之前
const page = penpot.getPage();
const file = penpot.getFile();
const theme = penpot.getTheme();
const shapes = penpot.getSelectedShapes();
// 1.0 起
const page = penpot.currentPage;
const file = penpot.currentFile;
const theme = penpot.theme;
const shapes = penpot.selection;
六、1.0 新增能力逐项拆解
Beta changelog 列出了大量新增 API,下面结合类型定义与运行时实现逐类说明其用途与在源码中的位置。
6.1 注释(Comments)支持
新增对设计评论的读写能力。类型定义中有 Comment(index.d.ts,用于在设计与原型上提供反馈)、CommentThread(第 605 行,"一系列按创建时间排序的评论")等接口,CommentThread 通过 comments 字段(第 634 行附近)以数组形式列出归属于该线程的评论,并支持按所有者删除评论(第 595 行附近)。文档中在介绍评论能力时将其定位为"直接在设计稿上提供反馈",因此常与注释定位(position)相关字段配套使用。
6.2 文件导出(Export files)
File 类型(index.d.ts)新增了导出方法 export(...)(第 1659 行)。与其配套的 Export 接口(第 1596-1605 行)指明可导出文件格式为 png / jpeg / webp / svg / pdf,位图格式还可通过 scale 字段控制导出分辨率。典型场景是把画板批量导出为设计交付物或预览图。
6.3 撤销块(Undo blocks)
插件对画布做批量修改时,如果希望用户一次 Ctrl/Cmd+Z 就能回退整组操作,就需要"撤销块"。对应类型为 HistoryContext(index.d.ts),提供:
undoBlockBegin(): Symbol:开启一个撤销块并返回块 id(第 2380 行附近);undoBlockFinish(blockId: Symbol): void:结束该撤销块,块内所有操作将被整体撤销(第 2391 行附近)。
6.4 标尺参考线(Ruler guides)
新增对页面/画板标尺参考线的控制能力,类型定义中有 Guide(联合类型)与 GuideColumn / GuideRow / GuideSquare 及对应的参数类型 GuideColumnParams、GuideSquareParams 等(index.d.ts)。这解释了为何 1.0 会把 PenpotFrameGuideX 改名为 GuideX(见第三节):参考线体系从"仅帧内竖线"扩展成了列/行/方格三种形态的通用能力。
6.5 原型(Prototype)能力访问
插件开始可以访问/操作 Penpot 的原型系统。源码中 Interaction(第 2450 行附近)注释明确说明:"Penpot 允许你通过连接画板(可作为屏幕)来原型化交互",Flow(第 1852 行)描述原型流程,交互动作与触发器通过 Action(第 148 行)/触发器枚举与 addInteraction(第 4072 行附近)等方法暴露,典型如 shape.addInteraction('click', { type: 'navigate-to', destination: anotherBoard })(第 4075 行示例)。
6.6 新增事件:contentsave 与 shapechange
如第四节所述,EventsMap 中新增了 contentsave(文件保存后触发)与 shapechange(形状变更时触发)。前者可用来做"保存后同步/刷新外部数据",后者需要注意传入 shapeId 以指明监听哪个形状(见 index.d.ts)。
6.7 文件/页面模型增强:file.pages、createPage、openPage
File类型新增只读属性pages: Page[](index.d.ts),列出当前文件的所有页面;- 运行时实现新增
createPage(): Page(plugins/libs/plugins-runtime/src/lib/api/index.ts)与openPage(page, newWindow?)(同文件第 351 行),类型签名openPage(page: Page | string, newWindow?: boolean): Promise<void>(index.d.ts)。
官方在类型注释(第 1280 行附近)给出一个重要实操提醒:新建页面后需要先 await penpot.openPage(page) 使其成为活动页,再执行形状变更操作,否则修改会作用到错误的目标页。createPage 同样需要在打开后才向其中添加内容(第 1295 行附近注释)。
6.8 形状模型增强:shape.parent、page.root、shape.visible
ShapeBase新增只读字段parent: Shape | null与parentIndex(index.d.ts),形状可以沿着父链向上遍历层级树;Context新增readonly root: Shape | null(第 799 行),给出当前页面/文档的根形状引用;- 形状新增
visible: boolean属性(第 3736 行),用于读取或控制形状的显示/隐藏状态,这是此前插件必须通过复制属性等间接手段才能完成的操作。
6.9 几何工具:shape.bounds 与 shape.center
形状新增只读 bounds(index.d.ts),返回由 { x, y, width, height } 组成的矩形边界(Bounds 类型定义见第 454 行),即形状在不旋转/变形情况下的包围盒。同时插件工具对象上提供 geometry.center(shapes):center(shapes: Shape[]): { x: number; y: number } | null(第 1389 行),传入形状数组即可计算整体中心点;ContextUtils 下还有等价的 penpot.utils.geometry.center(第 1492 行示例)。配合自动布局与对齐类插件非常实用。
6.10 组件解绑:detach 方法
形状模型新增 detach(): void(index.d.ts),用于把形状从其所属的组件实例中解绑——解绑后该形状不再受组件主定义同步影响,可以自由编辑。这在"实例局部定制"类插件里是刚需。
6.11 视口控制:penpot.viewport.zoomToShapes
文档记录 1.0 新增 penpot.viewport.zoomToShapes(...),用于把视口缩放到让指定形状可见。从当前仓库 Viewport 接口 看,视口命名空间提供的同类能力包括可写的 center: Point 与 zoom: number、只读 bounds,以及 zoomReset()、zoomToFitAll()(缩放至页面所有形状)、zoomIntoView(shapes)(缩放至参数指定的形状集合)。zoomIntoView(shapes: Shape[]): void(第 5742 行)与 changelog 中"改变视口以查看这些形状"的描述语义一致,可以推断该能力在后续类型定义中进一步演化/细分——建议以仓库中当前的类型签名为准进行编码。
七、迁移速查清单
将 Beta changelog 的变更要点收敛为一份可直接对照执行的清单:
| 变更类别 | 旧写法 | 新写法/新名称 |
|---|---|---|
| 类型命名 | PenpotShape/PenpotFile 等 |
去掉 Penpot 前缀 |
| 画板概念 | PenpotFrame、penpot.createFrame、frameX/frameY |
Board、penpot.createBoard、boardX/boardY |
| 矩形 | PenpotRectangle |
Rectangle |
| 椭圆 | PenpotCircle、penpot.createCircle |
Ellipse、penpot.createEllipse |
| 布尔 | PenpotBool |
Boolean |
| 参考线 | PenpotFrameGuideX |
GuideX |
| 事件注销 | penpot.off('pagechange', listener) |
const id = penpot.on(...) 后 penpot.off(id) |
| 文件/页/主题/选中态 | getFile()/getPage()/getTheme()/getSelected()/getSelectedShapes() |
currentFile/currentPage/theme/selection(用 selection 统一表示) |
建议的升级步骤:
- 全局搜索第三节所列的旧方法名与旧类型名,按表格逐一替换;
- 将所有
penpot.off(event, callback)重写为"保存on返回值 → 用 id 注销"的形式,防止未来版本移除旧行为后失效; - 把
getXxx()调用改为属性访问,注意getSelected与getSelectedShapes合并为selection的语义变化; - 视项目需求采纳新增能力(撤销块、导出、参考线、几何工具等),新代码优先使用 1.0 命名;
- 若项目使用 TypeScript,可直接以 plugins/libs/plugin-types/index.d.ts 为类型源做编译校验;亦可参考仓库内 plugins/apps/plugin-api-test-suite 中各
*.test.ts(如pages.test.ts、shapes-types.test.ts、shapes-geometry.test.ts)观察新 API 的真实调用方式,以及plugins/apps/下各示例插件(如create-palette-plugin、table-plugin、rename-layers-plugin)在真实场景中的用法。
八、总结:面向未来兼容的升级
Penpot 插件 API 1.0 的 Beta changelog 传达了两个关键信号:
- 语义统一:去掉冗余前缀、补全缩写(
rect→rectangle、bool→boolean)、对齐产品 UI(frame→board)、把命令式getXxx收敛为只读属性,整体让 API 更接近"描述状态"而非"发起查询"; - 稳定承诺:1.0 之后项目将尽量不再引入破坏性变更,遗留行为也会以向后兼容的方式废弃。因此现在完成向 1.0 的迁移,等价于为插件的长期可用性上了一道保险。
需要对照最新 API 细节时,可随时查阅仓库内 docs/plugins/api.md(Beta changelog 中所指官方 API 文档的本地版本)与 docs/plugins/faq.md 等配套文档;涉及类型签名、事件表与工厂方法的最新状态,则以 plugins/libs/plugin-types/index.d.ts 与 plugins/libs/plugins-runtime/src/lib/api/index.ts 中的实际声明为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00