Penpot 插件开发实战指南:运行官方示例插件与从零构建自定义插件
2026/9/8 18:11:54 网站建设 项目流程

Penpot 插件开发实战指南:运行官方示例插件与从零构建自定义插件

【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot

Penpot 的插件体系(Penpot Plugins)为开源设计平台提供了一个可扩展的运行时与一整套官方示例。本指南以仓库中 plugins/README.md 为核心,讲解如何定位plugins/插件工作区中的libs(运行库)与apps(示例应用)两大目录、如何在本机启动示例插件并在 Penpot 中通过 manifest 清单加载,以及从零创建自定义插件的完整路径。读完本文,你将能够独立完成 Penpot 插件开发环境的搭建、示例插件的运行验证,并为编写自己的第一个插件做好准备。

一、先认识plugins/:仓库中独立的插件子工程

在当前 Penpot 仓库根目录下的 plugins/ 中,维护着一套以插件为中心的独立 pnpm 工作区:它拥有自己的 package.json、angular.jsonpnpm-workspace.yaml,内部按用途划分为appslibsdocstools等子目录。这一阶段的产品形态是官方文档中所述的一个MVP(最小可行产品):一方面允许用户使用官方提供的既有插件,另一方面支持开发者编写自己的插件。

按照plugins/README.md的说明,有两个最重要的目录appslibs

plugins/ ├── apps/ # 可直接运行的示例插件、插件测试套件与样式 showcase ├── libs/ # 插件运行时的公共库:runtime、styles、plugin-types ├── docs/ # 从零创建插件、创建 Angular 插件、发布、e2e 等文档 ├── tools/ # 构建脚本(build-plugin.mjs)与发布脚本 ├── angular.json # 所有示例插件的 Angular 构建/开发服务器配置 └── package.json # 工作区级脚本入口

libs/:插件开发的三件套基座

libs/目录集中存放插件体系的公共代码,是理解插件如何运转的钥匙:

  • plugins-runtime:插件运行时的核心库。官方描述它是“负责生成 API 并加载 Penpot 插件”的代码,具体职责包括:初始化插件运行环境、建立沙箱(sandbox)、解析 manifest 清单,以及设置若干监听器以便获知 Penpot 的页面(page)、文件(file)、选区(selection)何时发生变化。它的入口在 src/index.ts。
  • plugins-styles:一份可独立发布的 Penpot 风格 CSS 基础库(npm 包@penpot/plugin-styles)。当你需要让自己的插件 UI 与 Penpot 主界面视觉一致时,可直接引入其样式。包内含按钮、复选框、图标、输入框、单选、下拉选择、开关等组件样式,以及字体、间距、色板等基础 token。
  • plugin-types@penpot/plugin-types提供 Penpot 插件 API 的 TypeScript 类型定义,配合typeRoots/types配置即可获得类型提示与 IDE 支持,是插件开发中最常用的一层。其类型声明见 plugins/libs/plugin-types/index.d.ts。

注意:虽然plugins/README.md只重点列举了 runtime 与 styles 两个库,但libs/plugin-types同样是该工作区的正式成员,且被 runtime 直接依赖(见 plugins-runtime/package.json 中的@penpot/plugin-types: workspace:^)。

apps/:示例插件与测试套件

apps/目录下是使用上述libs编写的具体示例,既可用于演示,也可作为新插件开发的起点。从仓库结构看,当前包含 9 个示例插件(如 contrast-plugin、icons-plugin、lorem-ipsum-plugin、create-palette-plugin、table-plugin、rename-layers-plugin、colors-to-tokens-plugin、poc-state-plugin、poc-tokens-plugin)、2 个插件测试套件(plugin-api-test-suite、composable-test-suite)、1 个样式 showcase(example-styles)以及端到端测试目录e2e。每个插件大致由 Angular 工程骨架、src/plugin.ts主逻辑与src/manifest.json清单三部分组成。

二、运行示例插件:从安装依赖到在 Penpot 中加载

前置条件:本机先有可用的 Penpot

启动任何 Penpot 插件前,都需要一个正在运行的 Penpot 实例(插件要挂在 Penpot 的页面/文件上下文上执行)。本机开发环境的搭建不在本仓库插件子工程范围内,可参照仓库根目录下的开发环境(devenv)指南 以及 docker/devenv 目录下的编排配置完成。

第一步:安装工作区依赖

在终端进入plugins/工作区目录,使用 pnpm 递归安装全部子包依赖:

pnpm -r install

该命令会同时解析apps/*libs/*等所有 filter 子包的依赖。从根级 package.json 可看到本工作区固定使用pnpm@11.20.0packageManager字段),建议使用与之匹配的 pnpm 版本。

第二步:启动插件运行时(Runtime)

继续执行:

pnpm run start

start实际指向start:app:runtime,其实现为一条concurrently命令,同时执行两部分工作(见 plugins/package.json):

  • pnpm --filter @penpot/plugins-runtime run build:watch:以 watch 模式持续构建 runtime 库;
  • pnpm --filter @penpot/plugins-runtime run preview:预览构建产物,开发服务器固定监听4200端口(见 vite.config.ts 中的preview.port: 4200)。

启动后运行时子包会处于“随时可被 Penpot 前端调用”的构建/预览状态。

第三步:启动所选示例插件

保持上一步的进程运行,另开一个新的终端标签页,执行该插件的启动脚本。以 README 中的 Contrast(对比度检测)插件为例:

pnpm run start:plugin:contrast

该脚本指向pnpm --filter contrast-plugin run init,实际由concurrently同时拉起“watch 构建插件产物”与ng serve contrast-plugin两个进程(见 apps/contrast-plugin/package.json)。由于 Angular 在 watch 模式下会把src/manifest.json等静态资源同步到构建产物中,因此插件本体与清单会同时可用。

第四步:在浏览器/Penpot 中加载插件

示例插件自身的界面运行在 Angular 开发服务器上,随后在 Penpot 界面中通过插件管理器输入该插件的Manifest URL完成安装与加载:

  1. 打开浏览器访问插件的开发服务器地址;
  2. 在 Penpot 内按快捷键Ctrl + Alt + P唤起插件管理器弹窗(也可通过菜单进入);
  3. 粘贴该插件的 manifest 地址(例如http://localhost:4202/assets/manifest.json)完成安装;
  4. 安装成功后即可随时从插件入口打开它。

端口说明:README 正文示例中 Contrast 插件写的是http://localhost:4302,但就当前仓库实际配置而言,所有示例插件的 Angular 开发服务器端口在根级 angular.json 中均被设置为4202,表格中的 Manifest URL(http://localhost:4202/assets/manifest.json)才是当前唯一准确的口径。由于全部插件共用 4202 端口,同一时刻请只运行一个示例插件

各插件的具体启动方式见下节表格

README 中给出了完整的“插件→启动命令→端口→Manifest URL”对照表,下文将原样保留并补充注解。

三、示例插件与 Web 应用清单

示例插件(Sample plugins)

Plugin描述PORT启动命令Manifest URL
poc-state-plugin用于测试新插件 API 功能的沙箱插件4202pnpm run start:plugin:poc-statehttp://localhost:4202/assets/manifest.json
contrast-plugin提供颜色对比度信息的示例插件4202pnpm run start:plugin:contrasthttp://localhost:4202/assets/manifest.json
icons-plugin从 Feather 图标库添加图标的工具4202pnpm run start:plugin:iconshttp://localhost:4202/assets/manifest.json
lorem-ipsum-plugin生成 Lorem ipsum 占位文本4202pnpm run start:plugin:loremipsumhttp://localhost:4202/assets/manifest.json
create-palette-plugin创建包含全部色板颜色的画板4202pnpm run start:plugin:palettehttp://localhost:4202/assets/manifest.json
table-plugin创建或导入表格4202pnpm run start:table-pluginhttp://localhost:4202/assets/manifest.json
rename-layers-plugin批量重命名图层4202pnpm run start:plugin:renamelayershttp://localhost:4202/assets/manifest.json
colors-to-tokens-plugin生成设计 Token 的 JSON 文件4202pnpm run start:plugin:colors-to-tokenshttp://localhost:4202/assets/manifest.json
poc-tokens-plugin用于测试 Token 相关功能的沙箱插件4202pnpm run start:plugin:poc-tokenshttp://localhost:4202/assets/manifest.json

启动任意一行命令后,插件界面都通过4202端口对外服务,manifest 统一从/assets/manifest.json暴露——这是因为根级 angular.json 将各apps/*-plugin/src/manifest.json声明为静态资源,构建后落入assets目录。

Web 应用(Web Apps)

App描述启动命令
plugins-runtime插件子系统运行时pnpm run start:app:runtime(即pnpm run start
example-styles可应用于插件的 Penpot 样式 showcasepnpm run start:app:styles-example

两点与 README 相关的口径勘误,均以仓库代码为准:

  • example-styles 的启动脚本应使用pnpm run start:app:styles-example(指向pnpm --filter example-styles dev)。README 正文中出现的旧命令pnpm run start:styles-example在当前根级 package.json 中并不存在。
  • 其开发服务器实际监听端口是4202(见 apps/example-styles/vite.config.ts 中server.port: 4202),README 中“Web Apps 表”标注的 4201 与正文中的地址以源码配置为准即可。它展示的正是plugins-styles库中的各组件样式(其页面入口见 apps/example-styles/src/main.ts)。

示例插件的代码长什么样?

以 Contrast 插件为例,其主逻辑位于 apps/contrast-plugin/src/plugin.ts,演示了 Penpot 插件 API 的几种典型用法:

penpot.ui.open('CONTRAST PLUGIN', `?theme=${penpot.theme}`, { width: 285, height: 525, }); penpot.on('selectionchange', () => { /* 读取 penpot.selection 并通知 UI */ }); penpot.on('themechange', () => { /* 主题切换同步 */ });

而它的清单 apps/contrast-plugin/src/manifest.json 则声明了自己需要的最小权限:

{ "name": "Contrast", "description": "Measure contrast plugin", "version": 2, "code": "assets/plugin.js", "icon": "assets/icon.png", "permissions": ["content:read"] }

四、库源码速览:插件运行时的加载与沙箱机制

如果你想知道“插件究竟如何被加载、如何拿到权限”,答案都在libs/plugins-runtime的源码中。这一节以源码为据,把 README 中“runtime 会初始化插件并注册若干监听器”这句描述展开讲清楚。

Manifest 清单的强约束

运行时使用 zod,逐字段如下:

字段类型说明
pluginIdstring插件的唯一标识
namestring插件名称
hoststring(url)插件宿主来源地址
codestring插件入口脚本地址
iconstring(可选)图标地址
versionnumber(可选)清单版本
descriptionstring(可选,≤200)插件描述,最长 200 字符
permissions枚举数组逐项授予的权限

其中permissions是一组白名单枚举:content:readcontent:writelibrary:readlibrary:writeuser:readcomment:readcomment:writeallow:downloadsallow:localstorageclipboard:readclipboard:write。插件申请的权限直接决定它能调用 API 的哪些能力——例如 plugin-types 的 index.d.ts 中多处标注“Requires thecontent:readpermission”,监听事件同样要求先具备content:read

从 Manifest URL 到插件实例的调用链

运行时对外暴露的核心入口见 src/index.ts:initPluginsRuntime(contextBuilder)会把构造插件上下文(Context)的回调、加载函数挂到全局,随后由 Penpot 前端按需调用。加载过程的核心实现位于 lib/load-plugin.ts,关键链路为:

  1. ɵloadPluginByUrl(manifestUrl):先通过loadManifest拉取并解析 manifest;
  2. loadPlugin(manifest):通过contextBuilder构造该插件专属的Context(含当前文件、页面、库、字体、用户等信息),并将其放入SES(Secure ECMAScript)沙箱harden加固后再传给插件代码;
  3. createPlugin:真正实例化插件,注册消息监听,并在插件关闭时从已加载列表移除;
  4. 卸载ɵunloadPlugin(id)pluginId查找并关闭对应插件。

值得注意的一个设计是:运行时在加载新插件时会先closeAllPlugins()关闭全部已加载插件(见 load-plugin.ts 中closeAllPlugins实现),即同一时刻只允许一个插件处于活动状态。

页面/文件/选区变化的监听机制

README 提到 runtime “sets a few listeners to know when the penpot page/file/selection changes”。这套能力最终以类型化事件的形式暴露给插件作者——即 plugin.model.ts 中定义的RegisterListenerpenpot.on(type, handler, props?)返回一个symbol句柄,配合penpot.off(listener)取消订阅。可订阅的事件(如selectionchangeshapechangethemechange)由@penpot/plugin-typesEventsMap统一定义,示例插件已经展示了它们的实际用法。

五、从零创建插件:官方推荐的下一个学习路径

运行完示例后,README 明确指引开发者阅读 插件创建指南(create-plugin.md)。该指南描述了在penpot-plugins工作区内部新建插件的完整步骤:

  1. 初始化目录结构:新建apps/<name>/srcapps/<name>/public,编写最小package.json
  2. 编写清单:在 public 目录创建manifest.json,声明namehostcodeicon与所需permissions(完整示例见该文档);
  3. 配置构建:在vite.config.ts中指定src/plugin.ts为入口,产出plugin.js
  4. 接入类型:在tsconfig的 include 中加入../../libs/plugin-types/index.d.ts(即仓库内实际的插件类型路径);
  5. 本地预览pnpm --filter <plugin-name> devbuild && preview
  6. 在 Penpot 中加载Ctrl + Alt + P打开插件管理器,粘贴 manifest URL 完成安装。

除上述“通用型”创建方式外,仓库还提供Angular 技术栈的等价指南 create-angular-plugin.md,以及围绕 API 的进阶资料:插件 API 文档、API 文档生成说明、端到端测试指南(test-e2e) 与发布插件包说明(publish-package)。工作区还为测试套件提供了专门脚本,例如根级package.json中的pnpm run test:e2e--filter e2e test)用于运行插件端到端测试。

六、常见问题与实用提示

  • 端口冲突:runtime 预览固定占4200,全部示例插件与 example-styles 共用4202。若浏览器访问不到,先确认是否有其他进程占用,且示例插件一次只启动一个。
  • 依赖缺失报错:首次使用请务必先执行pnpm -r install,并保证 pnpm 版本与仓库packageManager声明(pnpm@11.x)兼容。
  • 修改插件后如何生效:每个插件的init脚本都包含 watch 模式(Angular serve + 插件产物 watch 构建),保存源码后浏览器刷新即可看到变化,无需重启整个流程。
  • 权限申请原则:manifest 中的permissions是插件 API 能力的“门禁”,请只声明真正需要的权限——这与运行时在 manifest.schema.ts 中对权限做白名单校验的机制直接相关。
  • manifest 位于assets:Angular 系插件的清单由构建过程从src/manifest.json拷贝为assets/manifest.json,因此安装地址统一形如http://localhost:4202/assets/manifest.json

七、许可证

plugins/子工程与 Penpot 主项目一致,采用Mozilla Public License 2.0(MPL-2.0),版权归 KALEIDOS SUBSIDIARY SL 所有(完整声明见 plugins/LICENSE)。这意味着你可以自由使用、修改与再分发本工作区代码,但需遵循 MPL-2.0 的源码公开义务,并在分发时保留版权与许可声明。

相关文档与源码索引

  • 工作区总览:plugins/README.md
  • 运行时库说明与实现:plugins-runtime README · 运行入口 src/index.ts · 加载器 load-plugin.ts
  • 样式库:plugins-styles README · CSS 入口 styles.css
  • 类型定义:plugin-types README · index.d.ts
  • 示例插件:Contrast manifest · plugin.ts
  • 插件开发文档:create-plugin.md · create-angular-plugin.md · api-docs.md
  • Penpot 本地开发环境:devenv 指南

【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询