☰
OpenPencil 技术栈深度解析:从 CanvasKit 渲染到 Rust 桌面壳的全栈选型指南
2026/9/28 20:19:51 网站建设 项目流程
  • 前端
  • 桌面应用
  • AI 应用
  • MCP 服务

【免费下载链接】open-pencil

AI-native design editor. Open-source Figma alternative.

项目地址:https://gitcode.com/gh_mirrors/op/open-pencil
点击查看免费下载

OpenPencil 是一款开源的 AI-native 设计编辑器(Figma 的开源替代品),本文以其官方开发文档 tech-stack.md 为核心,结合仓库源码逐层拆解其渲染、UI、布局、文件格式、协作、AI/MCP、桌面端与工程化工具链的完整技术选型。读完本文,你将掌握 OpenPencil 每个核心技术层的选型理由、版本约束与真实代码落地位置,并能据此评估如何在自己的设计工具类项目中复用这套技术组合。

一、技术栈总览:一张表看懂全栈架构

OpenPencil 的整个前端与桌面体系围绕"浏览器可运行、原生性能可达、Figma 文件兼容"三个目标搭建。官方文档给出了如下核心技术分层:

层技术选型理由
渲染Skia CanvasKit WASM与 Figma 同引擎,久经验证的性能、GPU 加速、像素级精确
UI 框架Vue 3 + VueUse响应式组合式 API,TypeScript 支持出色
组件Reka UIHeadless、可访问的 UI 原语(tree、slider 等)
样式Tailwind CSS 4Utility-first、快速迭代、暗色主题
布局Yoga WASMMeta 出品的 CSS flexbox/grid 引擎,经 React Native 亿级设备验证
文件格式Kiwi 二进制 + ZstdFigma 自家格式,紧凑、解析快、兼容 .fig
协作Trystero + Yjs基于 MQTT 信令的 P2P WebRTC、CRDT 同步、y-indexeddb 持久化
颜色culori色彩空间转换(HSV、RGB、hex)
AI/MCPMCP SDK + Hono面向 AI 编码工具的 90+ 工具,支持 stdio 与 HTTP 两种传输
JSX 转换Sucrase轻量(201 KB)的 JSX→JS 转换,同步执行,浏览器可兼容
事件nanoevents108 字节的类型化事件发射器,服务于 SceneGraph 变更
桌面Tauri v2约 5MB 原生应用(对比 Electron 约 100MB),Rust 后端
构建Vite 7快速 HMR、原生 ES 模块
测试Playwright + bun:test视觉回归(E2E)+ 快速单元测试
LintingoxlintRust 实现,比 ESLint 快数个数量级
格式化oxfmtRust 实现的格式化器
类型检查typescript-go (tsgo)TypeScript 类型检查器的原生 Go 实现

二、核心依赖清单:版本即契约

文档列出的关键依赖在仓库根目录 package.json 中全部得到印证(bun.lock锁定具体版本),其中几个特殊条目值得注意:

{ "canvaskit-wasm": "^0.41.1", "vue": "^3.5.41", "yoga-layout": "npm:@open-pencil/yoga-layout@3.3.0-grid.3", "nanoevents": "^9.1.0", "sucrase": "^3.35.1", "reka-ui": "^2.10.3", "tailwindcss": "^4.3.3", "culori": "^4.0.2", "fzstd": "^0.1.1", "fflate": "^0.8.3", "trystero": "^0.22.0", "yjs": "^13.6.32", "y-indexeddb": "^9.0.12" }
  • yoga-layout的依赖被重定向到npm:@open-pencil/yoga-layout@3.3.0-grid.3,这是一个带 grid 支持的 Yoga fork 版本,正是"附加技术"章节提到的 CSS Grid in Yoga 的落地方式;
  • fzstd(浏览器兼容的 Zstd 解压)与fflate(压缩/解压)并列出现,分别服务于 Kiwi 容器解压与其他文件 IO 场景;
  • 仓库实际运行在Vite 8.1.4(devDependencies中"vite": "8.1.4")与bun@1.4.2(packageManager字段)之上,技术栈文档写作时的 Vite 7 是当时基线,当前仓库以 Bun 作为包管理器与脚本运行时。

三、渲染层:CanvasKit WASM 为什么是唯一正解

3.1 与 Figma 同引擎

官方文档明确说明选择 Skia CanvasKit WASM 的核心原因:Figma 桌面端与 Web 端使用的就是同一套 Skia 渲染引擎,选择 CanvasKit 意味着在渲染正确性、GPU 加速能力与像素级精确输出上直接对齐业界标杆,而不是从零验证一套新渲染路径。

仓库中 CanvasKit 的实际加载封装位于 packages/core/src/canvaskit.ts:它维护一个模块级单例instance,通过getCanvasKit()惰性初始化并复用,避免重复加载 WASM;locateFile逻辑区分浏览器与非浏览器环境——浏览器内以import.meta.env.BASE_URL为前缀定位.wasm文件,Node/Bun 环境则通过import.meta.resolve('canvaskit-wasm')解析包内路径。这意味着同样的渲染核心既可在浏览器运行,也可在服务端/CLI 场景无头运行。

3.2 为什么不用 SVG 渲染

文档专门解释了放弃 SVG 方案的工程理由:SVG 每个节点都是一个 DOM 元素,一份 10,000 节点的复杂文档意味着 10,000 个 DOM 节点,随之而来的是布局、绘制与合成三层开销;而 CanvasKit 把一切绘制到单一 GPU 表面,节点数量不再直接放大 DOM 压力。文档同时给出参照系:Penpot 默认仍走 SVG 渲染,其 Rust/Skia WASM 渲染器尚在开发中、为 opt-in 状态——这从侧面印证了 CanvasKit 路线的成熟度优势。

四、UI 与样式层:Vue 3 组合式响应式 + Reka UI + Tailwind 4

4.1 Vue 3 是从 React 迁移而来的结果

"为什么不用 React(原始计划)"一节披露了一个重要历史决策:项目在早期开发阶段从 React 迁移到了 Vue 3,理由是 Vue 的响应式系统与 VueUse 组合式函数在编辑器状态管理场景下更顺手。仓库中@vueuse/core(^14.4.0)与 Vue 3.5 系列(vue: ^3.5.41、@vue/compiler-core: ^3.5.41)都作为正式依赖出现;vue-router(^5.2.0)、vue-tsc类型检查、@vitejs/plugin-vue构建插件构成完整的 Vue 工程链。

4.2 组件与样式体系

  • Reka UI(^2.10.3):headless 无障碍 UI 原语,为树形控件(tree)、滑块(slider)等编辑器高频组件提供无样式逻辑层,UI 视觉完全由项目自身控制;
  • Tailwind CSS 4(^4.3.3):utility-first 快速迭代,配合@tailwindcss/vite插件在构建期注入;tailwind-merge(^3.6.0)与tailwind-variants(^3.3.1)用于类名合并与变体管理,tw-animate-css提供动效类;
  • 主题体系集中在 src/theme 目录,按 binding、button、chat、code、collapsible、dialog、feedback、home、input、list、mobile、motion、paint、panel、select、settings、tabs、toggle 等模块拆分,形成统一的设计令牌结构。

五、布局层:Yoga WASM 与 CSS Grid fork

5.1 Yoga 的底气:React Native 级别的实战验证

"为什么不用自研布局引擎"一节给出结论:Yoga 由 Meta 维护,在 React Native 的数十亿设备上久经考验,完整实现 CSS flexbox 规范;自研引擎要追平其正确性需要数月工作量。文档还指出本项目使用的 Yoga 版本在 fork 上额外合入了 grid 布局支持——这正是"附加技术"表中"CSS Grid in Yoga:基于 Grid 的自动布局,随 Yoga fork 发布"的完整含义。

5.2 源码中的实际用法

在 packages/core/src/layout 目录可以看到完整实现:

  • yoga-helpers.ts:Yoga.Config.create()创建共享配置,createYogaNode()生成节点,并提供configureAbsoluteChild()与applyMinMaxConstraints()辅助函数;
  • apply.ts:applyYogaLayout()将 Yoga 计算出的布局结果(frame 尺寸、子节点位置)回写到 SceneGraph 节点,实现"场景图 → Yoga → 回写"的完整布局回路;
  • grid.ts:从yoga-layout导入Direction、Display、Gutter、Edge等枚举,createGridChildNode()/mapGridTrack()完成 grid 轨道到 Yoga 节点的映射——即 grid 自动布局的前端实现。

5.3 自定义布局引擎方案早已在代码库中被否决

packages/core/src/layout.ts(核心布局入口)与上述 layout 子目录共同证明:项目没有重复造轮子,而是把 flexbox/grid 语义委托给 Yoga WASM,自己只负责 SceneGraph 与 Yoga 之间的映射与约束应用。

六、文件格式:Kiwi 二进制 + Zstd,原生 .fig 兼容

6.1 格式选型逻辑

文档将文件格式定为Kiwi 二进制 + Zstd 压缩:Kiwi 是 Figma 自有的二进制场景编码,体积紧凑、解析快速,天然兼容.fig文件。Zstd 提供高压缩比与高速解压,二者组合使大文档的保存与加载路径足够高效。

6.2 仓库中的实现证据

仓库内 kiwi 相关能力被拆成多个工作区包协同:

  • packages/kiwi:纯 Kiwi schema 解析、Figma Kiwi schema 数据、底层 Figma 消息编解码、fig-kiwi容器辅助与 GUID 格式化。其 README 明确:"完整的.fig归档解析位于@open-pencil/fig;SceneGraph 集成在包外";
  • packages/kiwi/src/fig/codec.ts:注释直接写明 "fzstd: Browser-compatible Zstd decompression",并通过zstdDecompress(data)完成 Kiwi 负载解压;
  • packages/kiwi/src/fig/container.ts:容器层解压优先使用 Bun 原生zstdDecompressSync(非浏览器环境),否则回退 fzstd——体现"浏览器可运行"与"服务端性能"的双轨设计;
  • packages/fig:负责完整的.fig归档解析(含图片资源)与 Kiwi 编解码的更高层封装,测试覆盖见 packages/fig/tests/export.test.ts 等;
  • packages/core/src/kiwi 与 packages/core/src/index.ts 导出isZstdCompressed等判定逻辑,用于在解析前识别是否 Zstd 压缩负载;
  • 桌面端 Rust 侧同样使用zstd = "0.13"(见 desktop/Cargo.toml),与前端 fzstd 形成压缩/解压能力的全栈覆盖。

七、协作层:Trystero P2P + Yjs CRDT + IndexedDB 持久化

7.1 架构与选型理由

文档定义的协作栈是Trystero + Yjs:Trystero 通过 MQTT 信令建立P2P WebRTC连接(无需中心化数据服务器),Yjs 提供CRDT 同步,y-indexeddb负责本地持久化。三者组合让协作既不依赖自建同步后端,又能获得离线优先、增量同步的能力。

7.2 源码落地

  • 传输层:src/app/collab/transport/trystero.ts 直接import { joinRoom as joinTrysteroRoom } from 'trystero/mqtt',证明使用 MQTT 信令子模块;src/app/collab/transport/index.ts 统一导出;
  • CRDT 编码:src/app/collab/node-codec.ts 提供encodeNodeForYjs()/decodeNodeFromYjs(),完成 SceneGraph 节点与 Yjs 文档结构之间的双向转换;
  • 房间与同步:src/app/collab/room.ts 通过makeAction('yjs-update')建立 Yjs 更新与 awareness 的传输通道;src/app/collab/session.ts 引入IndexeddbPersistence(来自y-indexeddb)做本地持久化;
  • 状态与上下文:src/app/collab/context.ts、src/app/collab/awareness.ts、src/app/collab/yjs-sync.ts 构成完整协作运行时;对应测试位于 tests/engine/collab。

八、AI/MCP:MCP SDK + Hono 的 90+ 工具矩阵

8.1 文档定义

文档将 AI/MCP 层定义为MCP SDK + Hono:为 AI 编码工具暴露 90+ 个设计工具,同时支持stdio 与 HTTP 两种传输。这使 Claude、Cursor 等 AI 客户端既能通过子进程 stdio 直连,也能走 HTTP 远程调用。

8.2 实现位置

MCP 服务端位于 packages/mcp:

  • packages/mcp/src/server/lifecycle.ts:getRequestListener来自@hono/node-server,Hono类型直接来自hono包——HTTP 传输正是基于 Hono 构建;
  • packages/mcp/src/stdio.ts 与packages/mcp/src/transport目录覆盖 stdio 与 HTTP 双传输;
  • packages/mcp/src/tool 目录承载具体工具实现,测试见 packages/mcp/tests/root.test.ts;
  • 前端侧 src/app/automation/mcp 与 src/app/automation/bridge 负责将 MCP 工具接入编辑器运行时;docs/programmable/mcp-server.md 提供了面向使用者的 MCP 服务说明。

8.3 浏览器内 JSX 执行:Sucrase

MCP/自动化能力中"把 JSX 设计代码跑起来"依赖Sucrase完成 JSX→JS 转换。文档强调其 201 KB 轻量体积、同步执行、浏览器兼容三大特性。源码证据:packages/core/src/design-jsx/render.ts 通过transform从 JSX 字符串构建组件函数(输出在运行时eval,见注释 "sucrase output must be evaluated at runtime");packages/core/src/design-jsx/transform.ts 同样使用transform。这构成"设计稿即代码(Design JSX)"的可编程能力底座。

九、事件与颜色等基础层:小而精的专项依赖

9.1 nanoevents:SceneGraph 变更事件

文档标注 nanoevents 仅 108 字节、类型化事件发射器。源码证据:packages/scene-graph/src/events.tsimport type { Emitter } from 'nanoevents';packages/scene-graph/src/index.ts 中 SceneGraph 实例字段readonly emitter: Emitter<SceneGraphEvents> = createNanoEvents()。整个场景图对外只暴露一个极小事件接口,所有变更订阅都走 nanoevents,把依赖面压缩到极致。

9.2 culori:色彩空间转换

culori(^4.0.2)承担 HSV、RGB、hex 等色彩空间转换,是颜色面板、取色器与 .fig 颜色编码共享的基础设施,类型声明由@types/culori提供。

十、桌面端:Tauri v2 的 5MB 原生体量

10.1 为什么不用 Electron

文档对比鲜明:Tauri v2 使用系统 WebView(约 5MB),而 Electron 捆绑整个 Chromium(约 100MB);Rust 后端为文件 IO 与系统集成提供原生性能。这直接回应了"为什么不像 Figma 桌面版那样用 Electron"。

10.2 仓库中的 Rust 侧证据

  • desktop/Cargo.toml:tauri = { version = "2", features = ["devtools"] },并引入tauri-plugin-opener、tauri-plugin-dialog、tauri-plugin-fs、tauri-plugin-shell、tauri-plugin-updater、tauri-plugin-process、tauri-plugin-os、tauri-plugin-clipboard-manager、tauri-plugin-deep-link、keyring(凭据存储)等插件;macOS 专属依赖包括security-framework、objc2-core-text(系统字体枚举);
  • 二进制入口为src/main.rs(bin 名OpenPencil),src/lib.rs以staticlib/cdylib/rlib三种 crate-type 暴露;
  • 桌面端还直接依赖zstd = "0.13"与zip,与 Kiwi 容器解析在 Rust 侧闭环;
  • Tauri 配置见 desktop/tauri.conf.json,Windows/macOS/Linux 菜单由 desktop/src/menu.rs 与生成脚本 scripts/generate-tauri-menu.ts 维护;
  • 原生测试通过native-testfeature 开启tauri-plugin-wdio-webdriver,配合wdio跑真实桌面端 E2E(见 wdio.conf.ts)。

十一、工程化工具链:oxlint / oxfmt / tsgo 的"Rust + Go"提速组合

文档将测试、Lint、格式化、类型检查分别定为:

  • 测试:Playwright(E2E 视觉回归,仓库 playwright.config.ts 定义 openpencil / storybook / figma 等多个 project)+ bun:test(快速单元测试,由 tools/unit-tests/src/run.ts 统一调度);
  • Linting:oxlint(Rust 实现,比 ESLint 快数量级),根 package.json 的lint脚本以oxlint -c oxlint.json --type-aware --type-check对src/与所有工作区包做类型感知检查;结构 lint 由oxlint-tsgolint插件补充;
  • 格式化:oxfmt(Rust 实现),format脚本读取.oxfmtrc.json配置;
  • 类型检查:typescript-go (tsgo),devDependencies 中为@typescript/native-preview: ^7.0.0-dev...,typecheck脚本运行tsgo --noEmit,Vue 部分再叠加vue-tsc。

工程化层面根目录还配置了knip(依赖检测)、sherif(monorepo 一致性)、steiger(feature-sliced 架构检查)、commitlint(提交规范)、jscpd(重复代码检测)与secret-scan(tools/secret-scan),配合 AGENTS.md 与 CONTRIBUTING.md 形成完整研发规范闭环。

十二、技术选型复盘:这套栈能给你什么启示

OpenPencil 的技术栈呈现一条清晰的决策主线:

  1. 渲染走"对齐巨头"路线:直接采用 Figma 同款 CanvasKit,规避 SVG 在超大规模文档上的 DOM 开销,避免在渲染正确性上从零踩坑;
  2. 能借力就不自研:布局交给 Yoga(含 grid fork),事件用 108 字节的 nanoevents,颜色用 culori,JSX 转换用 Sucrase——每个基础层都选"已经被大规模验证的最小依赖";
  3. 格式兼容是差异化武器:Kiwi + Zstd + fzstd 的浏览器/服务端双轨解压,让 .fig 兼容成为开箱能力;
  4. 协作去中心化:Trystero MQTT 信令 + WebRTC P2P + Yjs CRDT + y-indexeddb,把协作成本压缩到几乎零服务器依赖;
  5. AI 能力外化为协议:MCP + Hono 提供 90+ 工具的 stdio/HTTP 双通道,让任何 AI 编码工具都能驱动设计编辑器;
  6. 工程效率卷到极致:oxlint、oxfmt、tsgo 全部原生语言重写,配合 Bun 运行时,把 CI 中 Lint/类型检查/测试的等待时间压缩到最低。

如果想在浏览器中逐层验证这套栈的实际运行效果,可以按仓库标准流程操作:bun install后运行bun run dev(Vite 开发服务器)或bun run build(先构建工作区包再 lint 再打包);需要桌面应用则执行bun tauri。所有技术栈的权威描述仍以 packages/docs/development/tech-stack.md 为准,本文仅在此基础上补充了源码级证据与工程化细节。

  • 前端
  • 桌面应用
  • AI 应用
  • MCP 服务

【免费下载链接】open-pencil

AI-native design editor. Open-source Figma alternative.

项目地址:https://gitcode.com/gh_mirrors/op/open-pencil
点击查看免费下载

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

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

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

立即咨询