爱回收
75.46M · 2026-04-05
我做了一个开源矢量设计工具叫 OpenPencil,对标商业产品 Pencil.dev,核心差异点:
.op 文件就是结构化 JSON,能进 Git、能 diff、能 code review如果你用过 Pencil.dev,你应该知道它的 AI 生成设计能力很强。但它是闭源的、付费的,而且设计稿锁在它的平台里。OpenPencil 想做的事情是——把同样的能力开源出来,同时让设计稿真正属于你的代码仓库。
2025 年之后,AI 写代码已经不稀奇了。但你有没有发现一个断层——
AI 能帮你写出一个完整的后端服务,却画不好一个登录页面。
原因很简单:现有的设计工具(Figma、Sketch)都是为人类设计的。它们的文件格式是二进制或私有协议,API 有限,AI 想操作它们只能通过插件间接控制,效率极低。
而 Pencil.dev 看到了这个机会——它的 .pen 格式是结构化的,天然对 AI 友好。但问题是它是闭源商业产品。
OpenPencil 的思路是:如果设计稿本身就是一份 JSON,那 AI 读写设计稿就跟读写代码一样自然。 不需要插件,不需要 API 转换,直接操作数据结构。
这才是 AI 时代设计工具该有的样子。
这是我最想展开讲的部分,也是 OpenPencil 和传统设计工具最大的区别。
简单说:设计稿不再只是人用鼠标拖出来的,AI Agent 可以自主读取、理解、修改、生成设计稿。
除了外部 Agent 调用,OpenPencil 自己也内置了 AI 设计生成。在编辑器的聊天面板里,你可以用自然语言描述需求:
背后的架构是一个 Orchestrator + Sub-Agent 的编排系统:
graph TD
A["用户输入需求"] --> B["Orchestrator<br/>空间分解:Header / Pricing Cards / Footer"]
B --> C1["Sub-Agent 1"]
B --> C2["Sub-Agent 2"]
B --> C3["Sub-Agent 3"]
C1 --> D["流式插入画布(带淡入动画)"]
C2 --> D
C3 --> D
D --> E["Vision 校验<br/>截图 → Vision API → 自动修复视觉问题"]
style B fill:#6366f1,color:#fff
style E fill:#f59e0b,color:#fff
几个关键设计决策:
生成出来的不是图片,是真正的矢量节点树——每个元素都可以选中、编辑、调整属性、导出代码。
下面聊点硬核的,给同样想做图形编辑器的同学一些参考。
技术栈是 React 19 + Fabric.js v7 + Zustand v5。
React 是声明式的,Fabric.js 是命令式的。你用 Zustand store 管理数据,用 Fabric 渲染画布,两者之间的同步是整个项目最难的部分。
数据流架构:
graph TD
A["React 组件<br/>工具栏 / 属性面板 / 图层面板"] -->|"Zustand hooks"| B["canvas-store<br/>UI 状态:工具/选区/视口"]
A -->|"Zustand hooks"| C["document-store<br/>PenDocument · CRUD / 树操作"]
B --> D["Fabric.js Canvas<br/>命令式渲染"]
C --> E["canvas-sync-lock<br/>防止循环同步"]
E -.->|"锁保护"| D
style C fill:#6366f1,color:#fff
style E fill:#f59e0b,color:#fff
核心原则:document-store 是唯一数据源,Fabric.js 只负责渲染。
但双向同步意味着:
A 更新 B,B 又更新 A,循环了。
解决方案是一个 canvas-sync-lock——谁在写入,就锁住另一方的。听起来简单,但批量操作、undo/redo、动画中间态、组内级联更新……每个场景的同步时序都不一样,调了很久。
v7 把默认原点从 left/top 改成了 center/center。你写 left: 100, top: 100,对象中心点在 (100,100) 而不是左上角。
在做父子变换、自动布局、对齐吸附的时候,坐标系不统一会直接爆炸。解决方案就一行:
{ originX: 'left', originY: 'top' }
每个对象都显式声明,永远不信默认值。一行代码省了三天 debug。
另一个坑是默认 strokeWidth: 1,不要描边的时候必须显式设 strokeWidth: 0,否则你会看到每个对象都有一条若有若无的边框,排查起来很抓狂。
Fabric.js 没有原生的父子层级,对象模型是扁平的。但设计工具里 Frame 嵌套子元素是基本操作。所以需要:
缩放传播还好,旋转传播涉及三角函数和矩阵运算,单独抽了一个 parent-child-transform.ts 处理。
支持设计变量(类似 CSS Custom Properties),关键决策:$variable 引用在 store 里原样保留,只在渲染时解析。
// store 里存引用
{ fill: { color: '$color-primary' } }
// 渲染前解析为具体值
{ fill: { color: '#6366f1' } }
// 生成代码时输出 CSS 变量
{ fill: 'var(--color-primary)' }
好处:改变量值全局生效、多主题切换只需换变量集、代码生成零硬编码。支持多维度主题轴(Light/Dark × Compact/Comfortable = 4 种变体)。
| 层级 | 技术 |
|---|---|
| 前端框架 | React 19 + TypeScript(严格模式) |
| 样式 | Tailwind CSS v4 + shadcn/ui |
| 画布引擎 | Fabric.js v7 |
| 状态管理 | Zustand v5 |
| 路由 | TanStack Start(文件路由) |
| 服务端 | Nitro |
| 桌面端 | Electron 35(macOS / Windows / Linux) |
| AI | Claude / OpenAI,Orchestrator + Sub-Agent 编排 |
| Agent 协议 | MCP Server(兼容 Claude Code / Cursor / Windsurf) |
| 运行时 | Bun |
我做 OpenPencil 的初衷很简单:
2025 年了,AI 能写出整个应用的代码,却还是画不好一个 UI。 不是 AI 不够强,是现有的设计工具不给 AI 机会。文件格式是二进制的、API 是受限的、生态是封闭的。
Pencil.dev 证明了 AI-Native 设计工具这条路走得通,OpenPencil 想做的是把这条路开源出来——让每个开发者都能用上 Agentic Design,让设计稿真正成为代码仓库的一部分。
如果你对这些方向感兴趣,欢迎一起交流:
项目还在活跃开发中,欢迎 Star、欢迎 PR、欢迎 Issue。开源不易,你的每一颗 Star 都是继续做下去的动力。