最小拖拽画布:看得见地编排,再一键跑通
上一篇:并行分支:fan-out 多出边与 join 汇合(顺序模拟)
相关:03 浏览器联调台 · 06 If/Else
示例仓库:flow-forge
本篇讲:联调页从「手改 JSON」升级为最小拖拽画布------在图上加减节点、拉线、改配置,再「从画布运行」;背后仍是熟悉的 create → run → events。
读者预设:不必会 Python / React。主线是「画布解决什么、页面上怎么点、和 JSON 怎么互不打架」;技术库名只作地图。
先说结论
引擎侧已有直线 / 互斥 / 并行语义,第三篇起的 Web 却仍主要靠粘贴图 JSON。对照 Dify「在画布上编排」的体验,缺口很清楚:看得见、拖得动、连得上,而不是先当 JSON 编辑器。
本篇 change minimal-canvas 补的是学习向最小闭环:
| 能力 | 你能感知到什么 |
|---|---|
| 画布展示 | 打开页面就能看到节点与边,可拖拽挪位置 |
| 编排 | 左侧加节点、锚点拉线、右侧改 data |
| 一键运行 | 「从画布运行」→ 自动序列化成 API 图 → 看 status / outputs / events |
| JSON 仍在 | 折叠面板可预览 / 从 JSON 加载;运行以画布为准,避免双份状态飘了 |
一句话:画布是编排 UI,图 JSON 仍是交给后端的契约;本篇不追求完整 Dify 画布对等。
后端图校验与 HTTP 不变 ;主要改动在 web/。
最终实现效果
本地起好 api + web 后,联调页大致如下(可对照自己机器上的 http://localhost:3000):

| 区域 | 图上大致位置 |
|---|---|
| 添加节点 / inputs / 运行 | 左侧 |
| 节点与边(可拖拽、可拉线) | 中间画布 |
选中节点的 data 编辑 |
右侧 |
| status / outputs / events | 下方结果区 |
1. 第三篇联调页停在哪,本篇补哪块?
| 以前(纯 JSON) | 现在(画布为主) |
|---|---|
| 脑内想象拓扑 | 屏幕上直接看到拓扑 |
手写 source_handle |
if-else 从 true / false 锚点拉线 |
| 改错一个括号整图废 | 加节点、删节点、改侧栏字段 |
| 术语 | 是什么 | 本仓怎么用 |
|---|---|---|
| 画布(Canvas) | 节点-边可视化编辑区 | 中间大块区域 |
| 锚点(Handle) | 节点上可拉线的点 | 普通节点一侧进一侧出;if-else 右侧两个出口 |
| 序列化 | 把画布状态变成 API 要的图 JSON | toWorkflowGraph:剥掉位置等 UI 私有字段 |
| SSOT(提交时) | 谁说了算 | 点运行时只从画布导出;JSON 面板要加载需显式点按钮 |
2. 功能一:打开就能拖、能连
启动方式与第三篇相同:先 api,再 web,浏览器打开 http://localhost:3000。
默认加载 start → template → end 示例。你可以:
- 拖节点改位置(位置只服务 UI,不交给后端存)
- 从右侧锚点拖到另一节点左侧,画出一条边
- 用控件缩放 / 平移;可选小地图辅助定位
实现上用 @xyflow/react(业界常见的 React 流程画布库),整页仍是客户端组件,经 /api-proxy 打后端------代理故事与第三篇相同。
3. 功能二:加节点、改配置、护住 start
| 区域 | 做什么 |
|---|---|
| 左侧「添加节点」 | 可加 template / code / llm / if-else / end |
| 右侧侧栏 | 选中节点后编辑类型相关字段(模板句、代码、prompt、条件等) |
| 删除 | 可删非 start;唯一 start 不能删 |
新建节点会带上合理默认值(例如 template 预填 Hello, {name}!,if-else 预填 result = score >= 60),减少空白配置。
If/Else 注意 :必须从节点右侧标了 true / false 的两个锚点分别连出两条边------这对应第六篇的 source_handle,画错成普通单出口会校验失败。
并行 fan-out(第七篇):从一个节点拉出多条不带 handle 的边即可;不要误加 true/false。
4. 功能三:从画布运行(以及 JSON 怎么共存)
点「从画布运行」时页面做的事:
text
画布 nodes/edges
→ 序列化成 WorkflowGraph(无 position)
→ POST /workflows → POST .../runs → GET .../events
→ 下方展示 status / outputs / events
左侧仍可编辑 inputs JSON(例如 {"name":"Forge"})。
可选「显示 JSON」:
- 看当前导出载荷,方便对照前几篇的图约定
- 「从 JSON 加载到画布」是显式同步,避免画布改了、JSON 还旧、两边各改各的
失败路径与第三篇相同:故意缺变量 → failed + 可读 error / 失败事件。
5. 仓库地图(先职责)
| 路径 | 作用 |
|---|---|
app/page.tsx |
联调页壳子 + 标题说明 |
components/workflow-editor.tsx |
画布、面板、运行、JSON 折叠 |
components/workflow-node.tsx |
自定义节点外观与 if-else 双锚点 |
lib/graph-codec.ts |
画布 ↔ API 图编解码 |
lib/api.ts |
仍走 /api-proxy |
lib/sample.ts |
默认示例图 |
跟跑与构建见 web/README.md:
bash
cd web
pnpm build
pnpm exec tsx lib/graph-codec.smoke.ts
后一条是序列化冒烟:保证导出的图形状还对。
本篇顺带认识的词
| 词 | 先怎么记 | 和本篇的关系 |
|---|---|---|
| React Flow / xyflow | 专门画「节点连线」的前端库 | 本仓画布底座 |
| client 组件 | 浏览器里交互的那一层页面 | 拖拽必须在浏览器发生 |
| 序列化 | 把内存里的图整理成可提交 JSON | 运行前必做一步 |
| 锚点 id | 出口的名字 | true / false → API 的 source_handle |
和前作怎么接
| 篇 | 补哪一段 |
|---|---|
| 03 联调台 | JSON + 代理跑通 API |
| 06 / 07 | 互斥 / 并行图语义 |
| 本篇 | 用画布表达上述语义,不再手搓整图 JSON |
引擎能力与画布体验至此接上;后续可精修配置表单或 SSE,本切片停在「能拖、能连、能跑」。
你可以从这里带走什么?
- 工作流产品最终要「画」,但 API 契约仍是 nodes/edges JSON------画布是投影,不是第二套后端模型。
- 运行前只从画布序列化,JSON 面板用显式加载,可避免双源真相。
- If/Else 的 true/false 必须在 UI 锚点上体现,否则和第六篇语义对不上。
position可以只活在前端;学习向优先少改后端。- 最小画布目标是闭环学习路径,不是一次做完生产级设计系统。
仓库与相关文档
- GitHub :https://github.com/jimchou-h/flow-forge
- 画布用法 :web/README.md
- 编辑器 :workflow-editor.tsx
- 编解码 :graph-codec.ts
- 自定义节点 :workflow-node.tsx
欢迎 Star、Issue 和 PR。
_本文基于 Flow Forge minimal-canvas:覆盖最小拖拽编排与从画布运行。