如果你用过 OpenCode、Claude Code 这类终端 AI 编码助手,大概率遇到过一个尴尬:一条对话走到半路想试另一条思路,要么新开会话丢掉上下文,要么接着聊把两条线搅在一起。人类有 Git,AI 会话却还在"线形宇宙"里打转。
: https://github.com/dfytensor/OpenCodeCanvas
🎯 它做了什么
一句话:Electron 壳 + React Flow 无限画布 + node-pty 真实终端 + OpenCode CLI,每个画布节点就是一个可交互的 OpenCode 终端,主线到分支用紫色虚线标血缘。
主会话节点 ──fork──▶ 分支A(session A')
└──fork──▶ 分支B(session A'')
fork 的时候背后跑的是 opencode --session <父> --fork,OpenCode 原生会把父会话对话历史完整复制到新 session,画布只管"节点 + 血缘 + pty 容器",对话状态全交给 OpenCode 自己的 SQLite。分治得很干净。
✨ 核心特性
- 无限缩放画布:React Flow 点阵,滚轮缩放、拖拽平移、小地图齐全
- 终端节点:node-pty + xterm.js,每个节点一个独立 pty,完全可交互
- 会话分支 :右键 / 点节点右上角
⑂,fork 出新会话并行跑,互不干扰 - 独立全屏:单节点 Portal 全屏,滚动历史不丢
- 多画布管理:侧栏新建 / 重命名 / 复制 / 删除,localStorage 持久化
- AI 多分支合并 :Shift+ 选 ≥2 个分支节点 → 右键 "Merge N selected branches",生成绿色 merge 节点,让 OpenCode agent 自己把各分支改动合进来,项目不写任何 merge 算法,合并语义全甩给 OpenCode(这点设计取舍后面聊)
🏗️ 技术栈
| 层 | 选型 |
|---|---|
| 桌面壳 | Electron 33 |
| 终端 | node-pty 1.x + xterm.js |
| 画布 | React Flow 12 |
| 渲染 | React 18 + Vite (electron-vite) |
| 状态 | zustand + persist |
| 样式 | Tailwind |
Windows 上 opencode 走 cmd.exe /c 包装解析 PATH+PATHEXT;node-pty 基于 N-API,跨 Electron ABI 稳定,不用 electron-rebuild。
🤔 几个有意思的设计取舍
为什么不用 git worktree,而是整份 copy?
git worktree 从最近一次 commit 分叉,不包含工作区未提交改动。而 OpenCode agent 通常只改工作区不 commit,如果走 worktree,分叉出的对话上下文和分支文件状态就对不上了。
作者选的方案:fork 时把当前项目整份 copy 到 .opencode-canvas/{snapshots,copies}/<id>,snapshots 是冻结基线,copies 是分支运行副本。fork 起点 = 主线此刻的真实状态 ,对话与文件完全一致。diff/apply 也只用 git diff --no-index(不需要仓库),逻辑统一。
merge 节点为什么不自己写合并算法?
merge 节点的做法是:以主线当前状态为基线 copy 一份,把每个源分支的"工作目录 + 相对 fork 点的 diff"写进 MERGE_TASK.md,再往 AGENTS.md 注入合并指令,起一个 OpenCode 会话让 agent 自己合。血缘是多父的绿框 + merge 徽标,diff/apply 逻辑复用 fork 那套------不造轮子,把"怎么合"交给 AI agent,"什么时候合 / 合哪几个分支"由画布管。这个分层挺聪明。
🚀 快速上手
前置:Node ≥ 20,OpenCode CLI 已装且登录。
bash
git clone https://github.com/dfytensor/OpenCodeCanvas.git
cd OpenCodeCanvas
npm install
npm run dev
启动后左栏 Pick directory 选工作目录 → 顶栏 + OpenCode 建终端节点 → 在终端里发第一条消息真正创建会话 → 状态点变绿后点 ⑂ fork → Shift+ 选多个分支可走 merge 流程。
还缺什么
作者在 roadmap 里列的:分支节点自动取 OpenCode session title 做标题、fork 点上点 ⌗ 出 diff 节点(± 着色可刷新)、fork 点 ⬇ 把分支改动 apply 回主线(只回写 diff 涉及的文件,不影响主线其他改动)。这几个补齐后,"fork → 并行试 → diff 审查 → apply / merge 回主线"的闭环就完整了。
碎念
OpenCode Canvas 目前才 9 个 commit,作者一个人撸的,功能还不算厚,但方向挺对------AI 编码会话正在从"一次性对话"走向"可分支、可合并、可 revisiting 的结构化资产",类似的产品形态 Cursor 里也有些影子,但 OpenCode Canvas 把"画布 + 终端 + fork 血缘"这三件事焊到一起的方式,对 OpenCode 生态算是补了一块挺有意思的拼图。
MIT 协议,想给 OpenCode 会话"装 Git"的可以盯一下。