OpenCode Canvas:给 AI 编码会话装上“Git 分支“

如果你用过 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 上 opencodecmd.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"的可以盯一下。


项目地址:https://github.com/dfytensor/OpenCodeCanvas