npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wechen-agent-editor-v1

v1.3.10

Published

Wechen Agent:可执行的 AI 流程编排编辑器(React Flow),节点即模块、边即执行顺序;Monaco + Ant Design,面向 npm

Readme

wechen-agent-editor-v1

面向宿主应用的 Agent 流程编排编辑器 React 组件:左侧可拖入的组件库、中间 @xyflow/react 画布、右侧基于 antdMonaco 的配置区。图上的 节点 表示功能模块, 表示执行顺序;持久化的 flow 可由你的运行时或执行引擎消费。

包名:以 package.jsonname 为准(wechen-agent-editor-v1)。安装与 import 均使用该名称。

当前发布版本:见根目录 package.jsonversion(变更见 CHANGELOG.md)。发版前自检见文末 维护者:发版

宿主集成必读:本包 dependencies 为空,运行时能力全部来自 peerDependencies。除 npm install wechen-agent-editor-v1 外,须在业务工程根目录一并安装 react / react-dom / antd / @xyflow/react / dagre / monaco-editor / @monaco-editor/react(见下文 安装)。缺任一项会导致画布空白、连线失效、自动排版失败或 Monaco 无法加载。

文档导航能力地图 · 架构说明 · 私有化部署 · 性能预算 · 图操作门控 ref · 自动布局 ref · ref 矩阵 · 集成阶梯 L0–L3 · 工程基线 · 2026 全库审计 · Playground ?tab=overview

快速开始

60 秒最小集成(L0)

只需 onSave 与 antd ConfigProvider,并保证父容器有高度(例如 height: 100% 链或 100vh)。未传 initialFlow 时为空画布(与编辑器默认一致)。

安装(在宿主项目根目录执行,勿只装本包):

npm install wechen-agent-editor-v1 react react-dom antd @xyflow/react dagre monaco-editor @monaco-editor/react

Monaco 还需在应用入口配置 worker(见 Monaco Worker)。

import { ConfigProvider } from "antd";
import {
  WechenAgentEditor,
  getDefaultComponentLibraryGroups,
} from "wechen-agent-editor-v1";
// 若生产构建后缺少 Flow 全局样式,再增加:import "wechen-agent-editor-v1/style.css";

export function AgentPage() {
  return (
    <ConfigProvider>
      <div style={{ height: "100vh" }}>
        <WechenAgentEditor
          libraryGroups={getDefaultComponentLibraryGroups()}
          onSave={(s) => console.log("flow", s.flow)}
        />
      </div>
    </ConfigProvider>
  );
}

常用配置一条对象(L1)

标题、初始流、只读、以及在默认组件库上按条目打补丁,可用 buildWechenAgentEditorProps 一次展开为 WechenAgentEditor 的 props(类型见包内导出)。

import { ConfigProvider } from "antd";
import {
  WechenAgentEditor,
  buildWechenAgentEditorProps,
  cloneWechenAgentFlow,
  COMPONENT_LIBRARY_GROUP_APP,
  defaultInitialFlow,
} from "wechen-agent-editor-v1";

const editorProps = buildWechenAgentEditorProps({
  title: "我的流程",
  initialFlow: cloneWechenAgentFlow(defaultInitialFlow),
  readOnly: false,
  library: {
    preset: "default",
    itemPatches: [
      { groupKey: COMPONENT_LIBRARY_GROUP_APP, itemKey: "alert", patch: { subtitle: "自定义副标题" } },
    ],
  },
  onSave: (s) => console.log("flow", s.flow),
});

export function AgentPage() {
  return (
    <ConfigProvider>
      <div style={{ height: "100vh" }}>
        <WechenAgentEditor {...editorProps} />
      </div>
    </ConfigProvider>
  );
}

高级扩展(自定义画布节点、右侧配置面板、组件库列表映射、顶栏按钮等)仍使用完整 WechenAgentEditorProps;说明见下文「行为与扩展(摘要)」与 src/index.ts 导出。

后端工作流 JSON(存库 / 执行 / 回显)

编辑器内部持久化形态为 WechenAgentFlowJson(React Flow 的 nodes / edges + 业务 data)。若后端希望 扁平 DTOflowIdflowNamenodes[].type + configedges[].source/target 等),请使用:

  • exportBackendWorkflowJson(snapshot | flow, meta)WechenBackendWorkflowJson(根字段 schemaVersion 当前为 "1",与业务字段 version 区分)
  • importBackendWorkflowToFlow(dto)WechenAgentFlowJson,再作为 initialFlowref.replaceFlow 回显

id(删边 / 命令式 API)

  • onSave / getSnapshot / flowJsonedges[].id 为画布稳定 id,ref.deleteEdge(edgeId) / getSelectedEdgeId() 应使用该 id。
  • 后端 DTOexportBackendWorkflowJson 会写出 edges[].id(与画布一致);importBackendWorkflowToFlow 优先 使用 DTO 中的 id,省略时按 e-${source}-${target}-${index} 合成。仅持久化 DTO 时,回灌后删边请用 回灌后的 flow.edges[].id,勿沿用导出前的 id(除非 DTO 已含 id 且已往返)。
  • 推荐:需要完整画布状态时优先存 snapshot.flowflowJson;仅存 DTO 时务必带上 edges[].id 字段。

有损字段:导出会去掉 RF 运行期字段与部分边样式;导入会按边 id 分配 sourceHandle/targetHandle。详见类型 WechenBackendWorkflowJson 的 JSDoc。

连线布局

  • flowJson / getSnapshot:在 edge.data 中持久化 wechenSourceAttachSide / wechenTargetAttachSidewechenPathOptionsoffsetborderRadius),回显时 优先使用存盘布局,与编辑态一致。
  • 后端 DTOedges[].layout 可选携带上述字段(exportBackendWorkflowJson / importBackendWorkflowToFlow);若需 完整 画布状态(含全部 edge.data),请优先持久化 flowJson 而非仅 DTO 拓扑。

节点类型映射(画布 ↔ DTO):

| 画布 | DTO nodes[].type | 说明 | |------|-------------------|------| | pill | start | 入口 | | mainAgent | agent | 主 Agent | | card(默认) | task | 通用任务 | | card + data.backendNodeType | condition / api / script | 扩展类型;导入后写回 backendNodeType |

画布类型与 DTO 解耦:导出时 config.canvasNodeType 始终为画布 node.type(可为 MAINSUB 等任意非空字符串,或内置 pill / mainAgent / card);config.libraryGroupKeyconfig.libraryItemKey 为组件库拖入元数据。导入时优先用 canvasNodeType 恢复 node.type(任意非空值),后端 nodes[].typeagent / task / start 等)仍按上表映射。保存 onSave / flowJson 直接保留 nodes[].type(与 items[].type 一致)。

| 字段 | 含义 | |------|------| | 画布 node.type | React Flow 节点类型;拖入时 items[].type(必填,任意非空字符串)决定 | | DTO nodes[].type | 后端执行类型(agent / task / start …) | | config.canvasNodeType | 往返时还原画布 node.type | | data.libraryGroupKey / libraryItemKey | 库分组 catalog id / 库项 id( props.type) |

自定义节点若需稳定 DTO 类型,请在 node.data 设置 backendNodeType(取值与 WechenBackendWorkflowNodeType 一致)。

import {
  WechenAgentEditor,
  exportBackendWorkflowJson,
  importBackendWorkflowToFlow,
} from "wechen-agent-editor-v1";

<WechenAgentEditor
  onSave={(s) => {
    const dto = exportBackendWorkflowJson(s, {
      flowId: "agent_flow_001",
      workflowVersion: "1.0",
    });
    void fetch("/api/flows", {
      method: "POST",
      body: JSON.stringify(dto),
      headers: { "Content-Type": "application/json" },
    });
  }}
/>;

// 回显:从接口取 dto 后
const flow = importBackendWorkflowToFlow(dtoFromApi);
// <WechenAgentEditor initialFlow={flow} />

本仓库 playground 提供对照:

  • pnpm run dev:文档站(侧栏能力 Tab + 仓库文档 ?tab=repo-docs + 分栏独立滚动;默认 ?tab=overview),见 playground/README.md
  • pnpm run dev:minimal:仅最小宿主集成(对照上文「60 秒最小集成」)。

编辑器与 Agent 运行时

本包是 编排 UI + 序列化 + ref API不包含 Agent / LLM 执行引擎。推荐分层:

WechenAgentEditor(编排) → onSave / exportBackendWorkflowJson → 宿主持久化 → 你们的运行时调度
  • onTestRun / ref.testRun():仅触发宿主回调,具体执行由宿主实现。
  • validateWechenAgentFlow(flow):纯函数图校验(默认不阻断保存);可在 onBeforeSave 内返回 false 拦截。
  • ref 增删改查getSnapshot / getFlow / getInitialFlow / getNode / getEdge / replaceFlow / setInitialFlow / patchNodeDatanode.data / patchNode / setNodePosition(坐标与壳层) / setTitle / removeNode / duplicateNode / removeEdge / deleteEdge / 路径与布局 ref 等(见 WechenAgentEditorRef、Playground ?tab=ref-api)。
  • 连线路径(自由折线):选中边后浮动条「添加拐点」;仅 中间拐点 可拖拽(任意角度线段),首尾随节点桩固定;水平/垂直线段 可整条平移(鼠标移到线段上,光标变为 ↔ 或 ↕);Delete 在顶点选中时删点。normalizeWechenAgentFlowJson 会清除无效 path 并回到 auto
  • 选中浮动操作条:单选节点时显示 删除 / 复制;单选边时显示 删除添加/删除拐点、手动边 恢复自动路径。与边上 路径顶点手柄(几何编辑)分工;浮动条锚点在路径外侧,避免压住连线与节点。readOnly / readingMode / 多选时不显示。可选 renderSelectionToolbar 扩展。
  • 布局相关 refreadOnly / readingMode 下均为 no-op):完整说明 → docs/AUTO-LAYOUT-REF.md

| 方法 | 作用 | |------|------| | relayoutEdges() | 重算连线,不移动节点保留手调路径中间拐点 | | autoLayoutNodes(options?) | 低层节点排版(默认一体化);默认布线但 不清 手调拐点 | | autoLayout(options?) | 顶栏推荐:无参 = node+edge(清手调路径)+fitViewonly 白名单 | | fitView() | 仅适配视口 |

editorRef.current?.autoLayout();
editorRef.current?.autoLayout({ only: ["node"] });
editorRef.current?.autoLayout({ only: ["edge"] });
editorRef.current?.autoLayoutNodes({ direction: "TB" });
editorRef.current?.relayoutEdges();
  • 回显后连线布局getSnapshot / flowJson 保存锚边与路径选项;replaceFlow 优先恢复存盘布局。
  • 路径避让 / 一体化排版:见 AUTO-LAYOUT-REF.md
import { validateWechenAgentFlow } from "wechen-agent-editor-v1";

<WechenAgentEditor
  onBeforeSave={(snap) => {
    const { valid, issues } = validateWechenAgentFlow(snap.flow);
    if (!valid) {
      console.warn(issues);
      return false;
    }
  }}
  onSave={(s) => { /* POST exportBackendWorkflowJson(s, meta) */ }}
/>

安装

本包为 纯组件库package.jsondependencies 为空不会把 React Flow、Dagre、Monaco 等打进你的 node_modules 树里作为本包的私有副本。宿主必须在应用根声明下列 peer,并与本包解析到同一份运行时(尤其 @xyflow/react)。

推荐:一条命令装齐

npm install wechen-agent-editor-v1 react react-dom antd @xyflow/react dagre monaco-editor @monaco-editor/react

使用 pnpm / yarn 时同样在业务包中声明上述依赖,勿仅在 monorepo 子包里安装而根目录缺失 peer。

仅安装本包时常见现象

| 缺失 peer | 现象 | |-----------|------| | react / react-dom | 无法渲染 | | antd | 壳层/配置区样式或组件报错 | | @xyflow/react | 画布不显示、连线/拖线无响应、Handle 失效 | | dagre | autoLayout / autoLayoutNodes 报错或无效 | | monaco-editor + @monaco-editor/react | 代码编辑区空白(另需 Monaco Worker) |

Peer 依赖清单(须在宿主 package.json 中声明)

版本须 本包 peerDependencies 下限,并与业务栈锁定策略一致(下表为开发本仓库时使用的参考版本):

| 包 | 下限(peer) | 用途 | |----|-------------|------| | reactreact-dom | ≥18 | UI 运行时 | | antd | ≥5 | 编辑器壳层、配置区、顶栏 | | @xyflow/react | ≥12 | 画布(节点/边/连线) | | dagre | ≥0.8.5 | 节点自动排版(autoLayoutNodes) | | monaco-editor | ≥0.45 | 代码编辑内核 | | @monaco-editor/react | ≥4.6 | React 封装 |

1.2.3@xyflow/reactdagrepeer 提供(不再随本包内嵌)。自 1.0.xreact / antd / monaco-* 即为 peer。升级自内嵌 Flow 的旧版时,请务必在宿主根目录 新增 @xyflow/reactdagre 依赖。

1.3.2 起推荐用 onFlowOperation 统一图操作策略(替代仅覆盖删除的 onBeforeRemove*);peer 清单无变化,从 1.3.0 升级通常只需 bump 版本并回归构建与画布烟测。

不要再依赖本包 tarball 内自带的 @xyflow/react(历史 bundledDependencies 已移除)。宿主与编辑器 必须 npm ls @xyflow/react 仅一棵有效树。

安装后建议自检:

npm ls @xyflow/react
npm ls react

理想情况为各包 仅一棵 依赖树、无重复主版本。若出现多份,请在打包配置中对 @xyflow/react(及必要时 react)配置 resolve.alias / dedupe,或统一锁定版本后重装 node_modules

安装失败(ERESOLVE

npm 7+ 会校验整棵依赖树的 peer。冲突常来自宿主已有包,未必与本包声明矛盾。优先:升级冲突链上的上游(如 Pro 组件、ahooks、Umi 插件)使 peer 与 React 18 一致。临时可 npm install wechen-agent-editor-v1 --legacy-peer-deps 或在宿主 .npmrc 使用 legacy-peer-deps=true(上线前务必用 npm run build 验证,并计划去掉该放宽)。

Umi / MFSU

一般可直接使用。若遇预编译或模块解析异常,可在配置里将 wechen-agent-editor-v1@xyflow/reactmonaco-editor@monaco-editor/react 加入 mfsu.exclude(见 Umi MFSU),删除 .umi 后重启;或用 mfsu: false 做对比排查。

依赖示例(版本请按团队锁定,须满足本包 peerDependencies):

{
  "dependencies": {
    "@monaco-editor/react": "^4.6.0",
    "@xyflow/react": "^12.3.6",
    "antd": "^5.22.0",
    "dagre": "^0.8.5",
    "monaco-editor": "^0.45.0",
    "react": "^18.3.0",
    "react-dom": "^18.3.0",
    "wechen-agent-editor-v1": "^1.3.6"
  }
}

集成排错

白屏优先:打开控制台过滤 [wechen-agent-editor],对照 docs/WHITE-SCREEN-RISK-REGISTER.md 中的 ID/code;Playground 手测 ?tab=legacy-host-api-flow(JSON 文本域)与 ?tab=custom-config-panel(配置区边界)。

| 现象 | 可能原因 | 处理 | |------|----------|------| | 代码编辑区空白 / 控制台 Monaco worker 报错 | 未配置 Monaco worker | 按 README「Monaco Worker」在应用入口引导 worker | | worker-javascript.js MIME text/html / importScripts 失败 | 开发服务器对 worker 返回 HTML 404 | 配置 MonacoEnvironmentUmidocs/UMI-MONACO-WORKER.md | | 选中边后白屏、Rendered more hooks than during the previous render | 旧版边组件在 return null 后调用 hook | 升级含边组件 Hooks 修复的版本(见 CHANGELOG) | | 有边即白屏、reading 'offset' | 旧 flow 无 wechenPathOptions 或几何未就绪 | 升级含 legacy 边容错版本;控制台搜 [wechen-agent-editor](如 EDGE_PATH_OPTIONS_NULLEDGE_GEOMETRY_UNAVAILABLE) | | 画布区域 Alert「渲染异常」、其余页面正常 | 画布子树未捕获异常 | 看控制台 CANVAS_RENDER_CRASH 堆栈;修复宿主 renderFlowNode 或数据 | | 配置区 Alert「配置区渲染异常」、画布仍可用 | renderConfigPanel 抛错 | 看 CONFIG_PANEL_RENDER_CRASH;修复宿主配置面板实现 | | 画布不可见或高度为 0 | 父级无 定高 或 flex 链缺 minHeight: 0 | 使用「嵌入 flex 布局」示例;或传编辑器 minHeight | | 连线异常、Handle 无响应、样式错乱 | 未装 peer 或 @xyflow/react 双实例 | 按上文 一条命令 装齐 peer;npm ls @xyflow/react 仅一棵;打包器 alias/dedupe | | autoLayout 报错 | 未安装 dagre | 在宿主 dependencies 添加 dagre | | ref.deleteEdge(id) 无效 | 持久化载体无稳定 edges[].id | 优先存 snapshot.flow / flowJson;仅存 DTO 时导出须含 edges[].id,回灌后用 id | | 只读仍出现浮动条或可删节点 | readOnly 未传给编辑器根组件 | <WechenAgentEditor readOnly />;勿仅 Form disabled | | 存库后连线形状与编辑时不一致 | 仅存拓扑 DTO、缺 layout | 使用 flowJson 或 DTO edges[].layout(见「连线布局」) |

Playground ?tab=overview 提供术语表与持久化对比;工程清单见 docs/ENGINEERING-BASELINE.md

package.jsonrepository / homepage / bugs 若为占位 URL,表示尚未绑定公开仓库;私有 fork 可忽略,发版前请改为真实地址。


使用

入口与样式

import { WechenAgentEditor, type WechenAgentSnapshot } from "wechen-agent-editor-v1";

发布产物中 dist/index.js 会包含对 ./style.css 的引用,正常情况下图样式会随主入口进入打包结果。若生产环境仍看不到 Flow 全局样式,可增加:

import "wechen-agent-editor-v1/style.css";

antd 5 为 CSS-in-JS,请用 ConfigProvider 包裹应用(与官方用法一致)。

嵌入 flex 布局

列方向 flex 页面中,推荐与 playground 相同的外层结构:父级定高(或 100vh),编辑器所在子项使用 flex: 1minHeight: 0,编辑器根节点会 flex: 1height/width: 100% 填满该区域。

<div style={{ height: "100vh", display: "flex", flexDirection: "column", minHeight: 0 }}>
  <YourToolbar />
  <div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
    <WechenAgentEditor {...props} />
  </div>
</div>

行为说明:自 1.1.x 起,画布区域 不再 默认施加 min-height: 400px;若需要最小可视高度,请对编辑器传入 minHeight prop,或在页面级容器设置 min-height

骨架 CSS 类名(自定义样式)

加载 wechen-agent-editor-v1/style.css 后,可用下列稳定类名覆盖布局分区样式(与根 className 合并,不会替换默认类):

| 类名 | 区域 | | --- | --- | | wechen-agent-editor | 根布局 | | wechen-agent-editor--reading | 阅读模式根修饰 | | wechen-agent-editor__workspace | 工作区(顶栏 + 主体) | | wechen-agent-editor__header | 顶栏 | | wechen-agent-editor__body | 主体行(左栏 + 画布 + 右栏) | | wechen-agent-editor__library-sider | 左侧组件库 Sider | | wechen-agent-editor__library-panel | 组件库面板内容 | | wechen-agent-editor__main | 中间主内容 | | wechen-agent-editor__canvas / wechen-agent-canvas | 画布壳(二者同元素,后者为兼容别名) | | wechen-agent-editor__config-sider | 右侧配置 Sider | | wechen-agent-editor__config-panel | 配置面板内容 | | wechen-agent-node-shell / wechen-agent-node-shell--selected | 内置节点外壳 / 选中态 | | wechen-agent-node-shell--mainAgent / --card / --pill | 节点类型修饰 | | wechen-agent-edge / wechen-agent-edge--selected | 默认连线 / 选中态 |

自定义选中高亮(颜色写在 CSS 变量中,勿依赖行内 style):

| CSS 变量 | 默认 | 说明 | | --- | --- | --- | | --wechen-selection-color | #52c41a | 节点选中边框、默认边选中描边 | | --wechen-selection-border-width | 2px | 节点选中边框宽度 | | --wechen-selection-shadow | 绿晕阴影 | 节点选中阴影 | | --wechen-node-border-color | #d9d9d9 | 未选中节点边框 | | --wechen-edge-stroke | #b1b1b7 | 未选中边描边 | | --wechen-edge-stroke-selected-width | 2 | 选中边线宽 | | --wechen-zone-strip-border-color | rgba(22, 119, 255, 0.55) | 四边感应条 hover 描边色 | | --wechen-zone-strip-background | rgba(22, 119, 255, 0.06) | 四边感应条 hover 背景色 | | --wechen-zone-strip-border-width | 2px | 感应条 hover 描边宽度 |

四边感应条稳定类名:wechen-agent-zone-strip(基类)、wechen-agent-zone-strip--top / --right / --bottom / --left(边向)、wechen-agent-zone-strip--active(当前边 hover)。宿主可直接写选择器覆盖,或与上表变量一并设置在 .wechen-agent-editor 上。

/* 例:自定义顶栏背景 */
.wechen-agent-editor__header {
  background: #fafafa !important;
}

/* 例:选中高亮改为品牌蓝 */
.my-app .wechen-agent-editor {
  --wechen-selection-color: #1677ff;
}

/* 例:四边感应条 hover 色 */
.my-app .wechen-agent-editor {
  --wechen-zone-strip-border-color: #1677ff;
  --wechen-zone-strip-background: rgba(22, 119, 255, 0.12);
}

国际化(壳层 vs 宿主自定义)

编辑器是 编排基座:画布节点由 renderFlowNode、右侧内容区由 renderConfigPanel / renderNodeConfig 自定义时,业务文案由宿主自行 i18n。包内 locale / localeMessages 覆盖 壳层(顶栏按钮、左右栏分区标题、组件库搜索/空状态、画布缩放控件等;经 React Flow ariaLabelConfig 注入)。

语言契约

| 条件 | 编辑器显示语言 | | --- | --- | | 未传 localelocaleMessages | 内置中文zh-CN / DEFAULT_ZH_CN),与 antd ConfigProvider、浏览器语言 无关 | | locale="en-US"(无 localeMessages) | 内置英文DEFAULT_EN_US) | | locale + localeMessages | 先解析 locale 底包,再 浅合并 localeMessages 覆盖同 key | | 仅传 localeMessages(无 locale) | legacy:仅以 pack 为准;缺失 key 显示 key 字符串(与旧版一致) | | 存在 localeTranslate | 最高优先级(便于对接 i18next) |

语言包 key 为默认中文(与界面中文一致),例如 { "保存": "Save", "配置": "Settings" }。代码中可用 LOCALE_KEYS / FALLBACK_LOCALE_KEYS 常量。

import {
  WechenAgentEditor,
  LOCALE_KEYS,
  registerWechenAgentEditorLocale,
  exportWechenAgentEditorLocaleBundle,
  mergeWechenAgentEditorLocaleMessages,
} from "wechen-agent-editor-v1";

// 中文(默认)
<WechenAgentEditor />;

// 英文:一行 prop,无需维护整包 JSON
<WechenAgentEditor locale="en-US" />;

// 注册自定义语言后切换
registerWechenAgentEditorLocale("ja-JP", jaPartialMessages);
<WechenAgentEditor locale="ja-JP" />;

// legacy:整包 localeMessages(key 为中文)
const enPack = mergeWechenAgentEditorLocaleMessages({
  [LOCALE_KEYS.HEADER_SAVE]: "Save",
});
<WechenAgentEditor localeMessages={enPack} />;

// ref:读取当前语言包或命令式切换(受控 locale prop 优先)
editorRef.current?.getLocaleMessages();
editorRef.current?.setLocale("en-US");

renderConfigPanel / renderNodeConfig / renderSelectionToolbarctx.localetlocale(id)、messages(与 getLocaleMessages() 一致)。

// antd 与编辑器语言独立配置
<ConfigProvider locale={antdLocale}>
  <WechenAgentEditor locale="en-US" />
</ConfigProvider>
  • exportWechenAgentEditorLocaleBundle('en-US'):导出内置英文整包(作翻译模板)。
  • exportWechenAgentEditorShellLocaleBundle(locale) / exportWechenAgentEditorFallbackLocaleBundle(locale):按 locale 导出子集。
  • registerWechenAgentEditorLocale / listWechenAgentEditorLocaleIds:扩展语言列表。
  • configSiderTitle:可完全覆盖右侧栏外框标题。
  • validateLocaleMessagesCompleteness:检查大 JSON 是否缺 key(开发期 console.warn)。

完整演示见 Playground ?tab=i18nlocale prop、注册表、ref getLocaleMessages、legacy pack)。

嵌入尺寸、readOnlyreadingMode

根组件默认 height / width100%flex: 1minHeight: 0(便于 flex 父级);可通过 className / style 透传;可选 minHeight。请保证父级链路上有明确高度(见上文 flex 示例),否则中间画布可能无法撑满。

readOnly 须挂在 <WechenAgentEditor readOnly /> 根组件上;仅对右侧 Formdisabled 不会 禁止画布改图、浮动条或组件库拖入。

| | 三栏 UI | 画布选中 | 右侧配置 | 浮动条 / 改图 | |---|:---:|:---:|:---:|:---:| | 默认 | ✓ | ✓ | 可编辑 | ✓ | | readOnly | ✓ | ✓(查看) | disabled | ✗(无浮动条;ref 改图 no-op) | | readingMode | 仅画布 | ✗ | 无右栏 | ✗ |

  • readOnly:禁止对图的增删改(组件库拖入、连线、删除键、拖动节点、选中浮动条、ref.removeNode / patchNodeData 等),仍可使用平移/缩放查看,可点选节点/边 在右侧只读查看配置。readOnlyreadingMode 会出现在 renderConfigPanel / renderNodeConfig 的入参中。
  • readingMode仅展示中间流程画布(隐藏顶栏与左右栏),并禁用选中与一切改图,适合文档内嵌、大屏预览等纯展示场景(隐含只读)。根节点会附加 wechen-agent-editor--reading

删除守卫(onBeforeRemoveNode / onBeforeRemoveEdge)— @deprecated

新集成请用 下文 图操作事件(onFlowOperation。本 API 仅覆盖删除;与 onFlowOperation 同时传入时 事件优先。迁移见 docs/CAPABILITY-MAP.md

在可编辑模式下,可通过回调 按节点/边粒度 决定是否允许删除。同步返回 false 时取消该次删除(Backspaceref.removeNode / ref.removeEdge 均生效)。readOnly / readingMode 下不调用回调。删除节点时 级联移除 的关联边 不会 逐条触发 onBeforeRemoveEdge

<WechenAgentEditor
  onBeforeRemoveNode={(node) => {
    // 例:禁止删除入口节点,或 data.deletable === false 的节点
    if (node.id === "start_node") return false;
    if (node.data?.deletable === false) return false;
    return true;
  }}
  onBeforeRemoveEdge={(edge) => edge.data?.locked !== true}
/>

L1 工厂同样支持:buildWechenAgentEditorProps({ onBeforeRemoveNode: ... })

图操作事件(onFlowOperation

在可编辑模式下,删/复制/新增/连线/patch/路径 等改图操作在应用前会派发 WechenFlowOperationEvent。宿主在 onFlowOperation 中按 event.kindevent.node / event.edge 同步返回 false 即可取消(键盘、浮动条、ref 一致)。onFlowOperationnode.remove / edge.remove 优先于 legacy 删除回调。readOnly / readingMode 下不调用。

完整 kind / source 说明表、载荷字段与反模式 → docs/FLOW-OPERATION-REF.md(SSOT)。登记类型:WechenFlowOperationKindsrc/flow/flowOperationEvents.tsWECHEN_FLOW_OPERATION_KINDS)。

拒绝 node.remove原子保留 节点及因该次删除会连带移除的边(级联边 再派发 edge.remove);显式删边仍走 edge.remove

| kind | 含义(摘要) | |--------|----------------| | node.remove | 删节点(键盘 / 浮动条 / ref) | | node.duplicate | 复制节点 | | node.add | 新增节点(拖库 / ref) | | node.patch | 补丁 data(配置区)或壳层 position/type 等(ref) | | node.move | 拖节点结束落位(拒绝则回滚坐标) | | edge.remove | 显式删边 | | edge.add | 新建连线 | | edge.reconnect | 拖拽改端点 | | edge.patch | 补丁边 | | edge.path.reset | 单条边恢复自动路径 | | edge.path.insertVertex / edge.path.removeVertex | 折线路径插/删拐点 |

<WechenAgentEditor
  onFlowOperation={(event) => {
    if (event.node?.type === "pill") {
      if (event.kind === "node.remove" || event.kind === "node.duplicate") {
        return false;
      }
    }
    return true;
  }}
/>

Playground:?tab=flow-operation-policy。L1:buildWechenAgentEditorProps({ onFlowOperation: ... })

顶底感应区连线(默认,上进下出)

内置 pill / card / mainAgent 在未使用 renderFlowNode、且未启用下文 legacy flowHandles 时,采用 顶 / 底 周界窄条:输入池 = 顶边target)、输出池 = 底边source),符合 Agent 自上而下 阅读;多条入/出边沿顶/底边按邻居 水平坐标 分列槽位;整条感应条均可拖拽拉出连线(十字光标),几何锚点仍在 条带中心 stub。新建连接桩:in-new-top-{nodeId}(顶)、out-new-bottom-{nodeId}(底)。透明 Handle、无固定圆点桩 UI。新边 sourceHandle / targetHandle 仍为 out-${edgeId} / in-${edgeId}normalizeWechenAgentFlowJson 归一化旧桩 id。新建连线写入 edge.data.wechenSourceAttachSide / wechenTargetAttachSide(默认 底→顶)。旧图 left/right 锚边在加载/同步时迁移为 top/bottomConnectionMode.StrictisValidConnection(禁自环)。默认边 平滑阶梯 + 终点箭头(箭头在 target 端)。与 autoLayoutNodes / relayoutEdges、底边扇出 共干 路径配合。

周界四向感应的 完整方案说明(交互、角区、数据字段、代码映射) 见仓库 openspec/changes/node-perimeter-sensing-scheme/design.md

组件库 legacy 接线桩(flowHandles,可选)

已弃用优先:编排默认以 四边感应区 为准。libraryGroups 中每项仍可设置 flowHandles 以兼容旧版「声明桩位」:FlowHandleSpec[]mode: 'even' 自动均分。拖入后写入 data.flowHandlescard / mainAgentflowHandles 展开非空 时仍走声明桩渲染。libraryGroups 为必传 prop(可用 getDefaultComponentLibraryGroups() 或自建分组)。group.key 仅为分组 catalog id(如 "main" / "MAIN"), 决定节点类型。items[].type(必填) 为拖入后的画布 node.type可为任意非空字符串(如宿主业务类型 MAIN / SUB,或内置 mainAgent / card / pill);items[].key 为库项 id,写入 data.libraryItemKeytype 是两码事。同组可配置不同 type。拖入后另写入 data.libraryGroupKey

items[].defaultData(可选):拖入或 ref.addNodeFromLibrary浅合并node.data(在工厂基础字段之后;libraryGroupKey / libraryItemKey 始终由包写入,不会被 defaultData 覆盖)。嵌套对象整键替换(与 patchNode 深度合并 不同)。flowHandles 仅来自 item.flowHandles,写在 defaultData 内的 flowHandles 会被忽略。内置 mainAgent 仍有包内默认(如 baseModel),可用 defaultData 覆盖同名键

const libraryGroups = [
  {
    key: "MAIN",
    title: "主Agent",
    items: [
      {
        key: "planner",
        title: "Planner",
        type: "MAIN",
        defaultData: { strategy: "Plan-Execute" },
      },
      { key: "supervisor", title: "Supervisor", type: "MAIN" },
    ],
  },
  {
    key: "SUB",
    title: "应用Agent",
    items: [
      {
        key: "alert",
        title: "告警分析",
        type: "SUB",
        defaultData: { skill: "alert-analysis" },
      },
    ],
  },
];

非内置 type 使用与 card 相同的通用节点外壳与四边感应区;缺 item.type 时拖放会提示且不会创建节点。宿主可在加载配置后调用 validateComponentLibraryGroups(libraryGroups) 预检缺 type 的条目。

组件库条目 UI(renderComponentLibraryItem

左侧列表 默认无「+」按钮,添节点主路径为 拖拽 到画布。若需行尾点击添加,在条目上设 showAddButton: true(调用与拖放相同的 addNodeToCanvas)。

宿主可通过 renderComponentLibraryItem 自定义 每一行 的呈现与 trailing 操作(如「详情」「订阅」),回调 ctxeditorRefaddNodeToCanvasdefaultContent。返回 undefined 使用内置行。外层 drag 由基座托管,自定义 UI 不会 破坏拖放。

import { Button, List } from "antd";

<WechenAgentEditor
  libraryGroups={groups}
  renderComponentLibraryItem={(ctx) => (
    <List.Item
      actions={[
        <Button
          key="detail"
          type="link"
          size="small"
          onClick={(e) => {
            e.stopPropagation();
            openAgentDetail(ctx.item.key);
          }}
        >
          详情
        </Button>,
      ]}
    >
      {ctx.defaultContent}
    </List.Item>
  )}
/>

自定义按钮 click / pointerdownstopPropagation,避免误触拖拽。

组件库分组折叠(libraryGroupCollapse

多分组场景下可 opt-in 启用分组折叠:传 libraryGroupCollapse={true} 后,分组标题变为可点击按钮(chevron + 条目数);未传 时保持现网平铺标题,无行为变化。

  • 默认 全部展开;可用 defaultCollapsedGroupKeys 指定初始收起的 group.key
  • 搜索 时含匹配项的分组 强制展开;清空搜索后恢复用户折叠状态
  • persist(默认 true)将折叠状态写入 sessionStorage
  • refgetLibraryGroupCollapseEnabled() / setLibraryGroupCollapseEnabled(true|false) 可运行时开/关;setLibraryGroupExpanded / toggleLibraryGroup 控制单组。调试面板 概览 Tab 提供开关。
<WechenAgentEditor
  libraryGroups={groups}
  libraryGroupCollapse={{
    defaultCollapsedGroupKeys: ["app"],
    persist: true,
  }}
/>

样式类名(宿主可覆盖):组件库 DOM 使用 wechen-agent-editor__* 前缀,可在宿主全局/模块样式中按需覆盖(例如修复宿主 button { min-height } 导致分组标题过高、或折叠后分组间距过大):

| 类名 | 说明 | | --- | --- | | wechen-agent-editor__library-panel | 组件库根容器(搜索 + 列表) | | wechen-agent-editor__library-search | 搜索框 | | wechen-agent-editor__library-groups | 可滚动分组列表容器 | | wechen-agent-editor__library-group | 单个分组外壳 | | wechen-agent-editor__library-group--expanded / --collapsed | 展开 / 收起状态 | | wechen-agent-editor__library-group--flat | 未启用折叠时的平铺分组 | | wechen-agent-editor__library-group-header | 可点击分组标题(<button>) | | wechen-agent-editor__library-group-body | 分组内容区(条目列表) | | wechen-agent-editor__library-group-title / --static | 标题文字 / 不可折叠标题 | | wechen-agent-editor__library-group-chevron / __library-group-count | chevron / 条目数 | | wechen-agent-editor__library-group-list / __library-list | 条目 List 外层 / antd List | | wechen-agent-editor__library-item | 可拖拽条目外壳 | | wechen-agent-editor__library-item-row / __library-item-meta | antd List.Item / Meta |

分组元素还带 data-library-group-key;条目带 data-library-item-keydata-library-group-key,便于按 key 精确定位。

/* 示例:宿主 AgentEditor 中修复折叠标题高度 */
.wechen-agent-editor__library-group-header {
  min-height: unset;
  padding: 0;
}
.wechen-agent-editor__library-group--collapsed {
  margin-bottom: 4px;
}

renderFlowNode(props, context)props.type = 落盘 item.type不是 item.key);contextselected、可选 libraryGroupKey / libraryItemKeyeditorRefnodeErrorsetNodeError / clearNodeError / getNodeErrorState)、editorScope(与 prop 同引用)。宿主侧自动化/保存等 仍建议ref。四边感应与接线桩仍由包渲染, 在返回值内放置 Handle。类型见 WechenAgentRenderFlowNodeContext 等导出。

节点运行时异常态与 editorScope

异常态为 运行时状态不写入 getFlow() / 保存快照;删除节点或 replaceFlow 后自动清理。默认 blockSaveOnNodeError={true}:任一节点为异常态时,顶栏保存与 ref.save() 不调用 onSave,仅弹出提示(SAVE_BLOCKED_NODE_ERROR_HINT);设为 false 可恢复由宿主在 onBeforeSave 自行门控。

Playground ?tab=editor-scope 专页演示;与 nodeError 联用见 ?tab=render-flow-node

保存门控顺序(evaluateSaveGate):配置未保存蒙层节点异常态onBeforeSave

| API | 说明 | | --- | --- | | context.nodeError.setNodeError(reason?) | 将当前节点标为异常,右上角显示角标 | | context.nodeError.clearNodeError() | 恢复正常 | | context.nodeError.getNodeErrorState() | { status: 'ok' \| 'error', reason? } | | renderConfigPanelkind: 'node') | 同上 nodeError API,可展示 Alert | | ref.setNodeError(id, reason?) / getNodesInError() | 命令式按 nodeId 操作;批量查询异常节点 | | editorScope prop | 宿主传入可变字典;context.editorScope / ref.getEditorScope() 读同一引用 | | blockSaveOnNodeError | 默认 true:有异常节点时阻止保存 | | nodeErrorValidator | 实例级校验器 (ctx) => reason \| falsy;flow 载入 / patch / editorScope 变更时自动调度(不经 render 透传) | | nodeErrorValidateRevision | 递增时全图重校(字典异步就绪且 editorScope 引用不变时) | | ref.revalidateNodeErrors(ids?) | 手动触发校验 |

setNodeError / clearNodeError 在状态未变化时为 no-op(幂等),且默认经 微任务批量落盘(不在 renderFlowNode 渲染中途同步触发订阅更新),因此可在渲染函数内根据 node.data 同步标异常而不会死循环。推荐:配置类校验优先用 nodeErrorValidator(声明式、首屏即标角标);无 validator 时仍可在 renderFlowNode 内手动 setNodeError

nodeErrorValidator 推荐模式(稳定 editorScope + 无闭包):

const editorScope = useRef({ skillDict: [] as { value: string }[] }).current;
const [validateRevision, setValidateRevision] = useState(0);

useEffect(() => {
  editorScope.skillDict = loadedSkills;
  setValidateRevision((r) => r + 1);
}, [loadedSkills]);

const nodeErrorValidator = useCallback((ctx) => {
  const skills = ctx.nodeData.skill as string[] | undefined;
  const dict = (ctx.editorScope.skillDict as { value: string }[]) ?? [];
  const bad = skills?.filter((id) => !dict.some((d) => d.value === id));
  return bad?.length ? `异常插件: ${bad.join(",")}` : null;
}, []);

<WechenAgentEditor
  editorScope={editorScope}
  nodeErrorValidator={nodeErrorValidator}
  nodeErrorValidateRevision={validateRevision}
/>

nodeErrorValidatorrenderFlowNode 内重复 setNodeError 做同一类校验(调度结果为 SSOT)。

renderNodeErrorBadge(ctx):与 renderConfigPanel 类似的回调;按 ctx.state / ctx.reason / ctx.nodeType 返回不同图标。返回 undefined 用内置图标;null 不显示角标。可选 nodeErrorBadgeClassName / nodeErrorBadgeStyle 覆盖默认样式。默认角标在节点壳层 右上角内缩 4px--wechen-node-error-badge-inset-top / --wechen-node-error-badge-inset-right 可覆盖)。

const scope = useRef({ modelDict: MODEL_MAP }).current;

<WechenAgentEditor
  editorScope={scope}
  renderNodeErrorBadge={(ctx) =>
    ctx.reason?.includes("模型") ? <CloseCircleFilled /> : undefined
  }
  renderFlowNode={(props, ctx) => {
    if (!props.data.model) ctx.nodeError.setNodeError("未选择模型");
    else ctx.nodeError.clearNodeError();
    return <MyNode data={props.data} dict={ctx.editorScope.modelDict} />;
  }}
  renderConfigPanel={(ctx) => {
    if (ctx.kind !== "node") return undefined;
    const err = ctx.nodeError.getNodeErrorState();
    return err.status === "error" ? (
      <Alert type="error" message={err.reason} />
    ) : undefined;
  }}
/>

Monaco Worker

必须在首次渲染编辑器之前完成 Monaco worker 引导,否则代码编辑区无法初始化。可参考本仓库 playground/src/monacoBootstrap.tsplayground/src/main.tsx,并按 Monaco / @monaco-editor/react 文档适配 Vite 或 Webpack。

Umi 4 / Webpack 宿主(常见 worker-javascript.js 返回 HTML):见 docs/UMI-MONACO-WORKER.md(入口 bootstrap、MFSU excludemonaco-editor-webpack-plugin 备选)。

与快速开始一致的最小页(可选 title

与上文 L0 相同;若需要顶栏文案可传 title。未传 initialFlow 时为空画布。需要内置示例图时,从本包导入 defaultInitialFlowcloneWechenAgentFlow 并传入 initialFlow={cloneWechenAgentFlow(defaultInitialFlow)}

行为与扩展(摘要)

  • 连线:从节点的 source Handle 拖到 target Handle;选中边可在右侧调整动画与执行方向等。执行方向sourcetarget 一致:箭头在 目标 端;选「反向」会 交换源与目标(及 handle),不再依赖仅反转 dash 的持久化字段。历史 edge.data.wechenFlowDirection: 'reverse'normalizeWechenAgentFlowJson / cloneWechenAgentFlow / parseWechenAgentFlowJson幂等归一 为交换端点并清除该键。宿主也可调用 swapWechenFlowEdgeEndpoints(edge) 得到与内置面板一致的 patchEdge 补丁。
  • 数据合并:节点 data 的更新遵循导出的 mergeNodeData(对象深度合并,数组在补丁中出现时整段替换)。详见下文 patchNodepatchNodeData
  • 自定义右侧面板:可选 renderConfigPanel(节点与连线统一入口)或 renderNodeConfig;同时存在时以 renderConfigPanel 为准(返回 undefined 时回退内置)。

patchNodeData 与配置区 ctx.patchNode

二者语义一致:把 patch 深度合并进 node.data(内部为 mergeNodeData)。区别仅在于 是否由调用方传入节点 id(右栏已选中节点时,id 由编辑器绑定)。

| API | 签名 | 使用场景 | |-----|------|----------| | ctx.patchNode | (patch) => void | renderConfigPanel 选中节点时;勿传 node.id,当前节点已固定 | | ctx.editorRef | RefObject<WechenAgentEditorRef \| null> | renderConfigPanel 节点/边共有;内部 API,无需 宿主挂 ref,在事件内读 current | | ref.patchNodeData | (nodeId, patch) => void | 命令式改 node.data(按钮、自动化、跨节点批量) |

patch 的顶层键 = node.data 的键,不是整颗 React Flow Node(不含 id / type / position / measured 等)。

ref:节点坐标与壳层(patchNode / setNodePosition

| API | 作用 | |-----|------| | ref.getNode(id) / ref.getEdge(id) | 按 id 读取当前实体(浅拷贝) | | ref.setNodePosition(id, { x, y }) | 只改 node.position(等同 patchNode(id, { position })) | | ref.patchNode(id, patch) | 改 position / type / width / height 用来写 data | | ref.setTitle(title) | 只改标题,不替换整图 |

对称读写示例(无需 replaceFlow 整图替换坐标):

const flow = editorRef.current?.getFlow();
const n = flow?.nodes.find((x) => x.id === "my-node");
if (n) {
  editorRef.current?.setNodePosition("my-node", { x: n.position.x + 80, y: n.position.y });
}

程序化改坐标会经 onFlowOperationnode.patchsource: ref);与 patchNodeData 的 data 补丁区分。

ref 实例 vs 包导出(常见误找)

下列能力 不在 editorRef.current 上,需从包 直接 import(完整对照见 docs/EDITOR-REF-API-MATRIX.md):

| 需求 | 用法 | |------|------| | 后端 DTO 导出/导入 | exportBackendWorkflowJson / importBackendWorkflowToFlow → 再 ref.replaceFlow | | 图合法性校验 | validateWechenAgentFlow(ref.getFlow()) | | 克隆/解析 JSON | cloneWechenAgentFlow / parseWechenAgentFlowJson | | 边端点交换补丁 | swapWechenFlowEdgeEndpointsref.patchEdge | | 画布 重连 | ref.reconnectEdge(edgeId, connection)(同 UI onReconnect) | | 拖动 门控 | onFlowOperation node.move(拖放结束;拒绝则回滚坐标) | | 聚焦节点 | ref.centerOnNode(nodeId) | | 校验当前图 | ref.validateFlow() |

node = { id, type, position, data: { label, config, … } }
                              ▲
                              │ mergeNodeData(node.data, patch)
patch = { label: "x" }  ──────┘   →  node.data.label === "x"

推荐写法

1. 改单个字段(最常用)

// renderConfigPanel
if (ctx.kind === "node") {
  ctx.patchNode({ label: "新名称" });
}

// ref
editorRef.current?.patchNodeData("node-1", { label: "新名称" });

2. 一次保存多个 data 顶层键

ctx.patchNode({
  label: "主入口",
  config: { maxSteps: 8 },
});

3. 嵌套对象(深度合并,未出现在 patch 中的兄弟键保留)

// 已有 node.data.config = { a: 1, b: 2 }
ctx.patchNode({ config: { a: 3 } });
// → node.data.config === { a: 3, b: 2 }

4. antd Form 即时写回(renderConfigPanel 需自建 FormrenderNodeConfig 由外层包一层 Form

if (ctx.kind === "node" && ctx.node.type === "MAIN") {
  const disabled = ctx.readOnly || ctx.readingMode;
  return (
    <Form
      key={ctx.node.id}
      layout="vertical"
      initialValues={ctx.node.data}
      disabled={disabled}
      onValuesChange={(_, all) => ctx.patchNode(all)}
    >
      <Form.Item name="label" label="节点名称">
        <Input />
      </Form.Item>
    </Form>
  );
}
  • initialValuesnode.data,不要用整颗 node
  • 切换选中节点时用 key={node.id}form.setFieldsValue,避免 initialValues 只在首次挂载生效。
  • 只读:disabled={ctx.readOnly || ctx.readingMode}(上下文 ctx.disabled 字段)。

5. 受控输入 + 按钮「应用」(防抖 / 显式保存)

const [draft, setDraft] = useState(String(ctx.node.data?.label ?? ""));
useEffect(() => {
  setDraft(String(ctx.node.data?.label ?? ""));
}, [ctx.node.id, ctx.node.data]);

<Button onClick={() => ctx.patchNode({ label: draft })}>应用</Button>

Playground 中 card 节点示例见 playground/src/playgroundPanels.tsx

常见错误(会导致不生效或 data 被污染)

| 错误写法 | 实际效果 | |----------|----------| | patchNode(node.id, { label: "x" }) | 只接收 一个 参数;node.id 字符串被当作 patch merge 进 data,出现 "0":"m" 等下标键 | | patchNode(node.data) | 闭包里的 node.data 盖回表单新值,表现为回弹或不更新 | | patchNode({ data: { label: "x" } }) | 写入 node.data.data.label,除非业务里真有名为 data 的嵌套对象 | | patchNode(allValues)initialValues={node} | 把 id / position 等 merge 进 data,结构错乱 | | patchNodeData(id, node) | 应传 node.data 上的补丁,不是整颗 Node |

对比:想改 node.data.label

ctx.patchNode({ label: "x" });              // ✅
ctx.patchNodeData(id, { label: "x" });    // ✅ ref

ctx.patchNode({ data: { label: "x" } });  // ❌ → data.data.label
ctx.patchNode(node.id, { label: "x" });   // ❌ 多传了 id

数组与其它规则

  • 数组patch 里若包含某数组键,则 整段替换该数组(不按下标合并)。
  • undefinedmergeNodeData 跳过 undefined,不会用其删除已有字段;要清空请显式传 null 或业务约定值。
  • ctx.patchEdge / ref.patchEdge 为边对象 顶层浅合并(与节点 data 规则不同),见 WechenAgentConfigPanelEdgeContext

setConfigUnsavedGuard 与延迟保存

renderConfigPanel 中若采用 编辑草稿 + 按钮保存(不每次 onValuesChange 调用 patchNode),可调用 ctx.setConfigUnsavedGuard(true/false)

  • true:在 顶栏、组件库、画布 上显示半透明蒙层(不挡 右侧配置区),点击蒙层提示「当前有未保存的修改…」(LOCALE_KEYS.CONFIG_UNSAVED_GUARD_HINT,可 localeMessages 覆盖)。ref.save() / ctx.save() 与顶栏保存走同一门控,守卫为 true 时 不调用 onSave
  • false:关闭蒙层;保存成功后应显式调用。
  • 选中变化(切换节点/边、点击画布空白)时编辑器会 自动false
  • readingMode 下为 no-op。
const [draft, setDraft] = useState(() => String(ctx.node.data?.label ?? ""));

useEffect(() => {
  setDraft(String(ctx.node.data?.label ?? ""));
  ctx.setConfigUnsavedGuard(false);
}, [ctx.node.id]);

<Input
  value={draft}
  onChange={(e) => {
    setDraft(e.target.value);
    ctx.setConfigUnsavedGuard(true);
  }}
/>
<Button
  onClick={() => {
    ctx.patchNode({ label: draft });
    ctx.setConfigUnsavedGuard(false);
  }}
>
  保存
</Button>

Playground renderConfigPanel Tab 中 card / 边配置演示了该流程。

新建节点/连线后默认选中

在可编辑模式下,以下操作成功后会 自动选中 新建对象,右侧配置区立即展示对应面板:

  • 组件库拖入 节点;
  • 画布 拖拽连线onConnect);
  • ref addNodeFromLibrary / addNode / connect(或 addEdge)。

拖入新节点后,宿主宜在 useEffect 依赖 node.id 上重置表单草稿并 setConfigUnsavedGuard(false)(见上节)。

  • 顶栏左侧:可选 renderHeaderLeading,入参含当前解析标题 title(与 ref.getTitle() 一致)及 editorRef;未传时显示默认 Title。改标题数据仍用 title propreplaceFlow
  • 顶栏右侧:可选 renderHeaderActions,入参含与默认行为一致的 save / testRuneditorRef。左右可 同时 自定义。Playground:?tab=header-leading?tab=header-actions

完整类型与符号列表以 src/index.ts 的导出为准。

集成自检

在宿主项目根目录建议逐项确认:

| 检查项 | 说明 | |--------|------| | Peer 依赖 | npm ls reactnpm ls @xyflow/reactnpm ls dagre 各仅一棵有效树、无重复主版本 | | 构建 | npm run build(或 Umi max build)通过 | | Monaco | 已在首屏前完成 worker 引导(见上文「Monaco Worker」) | | 空画布 | 未传 initialFlow 时中间画布无预置节点;业务示例需 cloneWechenAgentFlow(defaultInitialFlow)ref.replaceFlow(...) 显式注入 | | 烟测 | 拖入节点、连线、readOnly / readingMode、保存或 getSnapshot;边路径添加拐点/拖顶点 | | ref 增图 | ref.addNodeFromLibrary / ref.connect 与拖放、连线行为一致 | | DTO 往返 | exportBackendWorkflowJsonimportBackendWorkflowToFlowreplaceFlowcondition/api/scriptdata.backendNodeType | | 图操作门控 | onFlowOperation:拒 node.remove 时节点与级联边均保留(Playground ?tab=flow-operation-policy) | | ref 坐标回写 | getFlow 读 positionsetNodePosition 写 position?tab=ref-api) | | 图校验 | validateWechenAgentFlow;可选 onBeforeSave 拦截 |


本地开发(本仓库)

包管理器:本仓库使用 pnpm workspace(根 + playground/)。需 Node 20+;推荐 corepack enable 后由 packageManager 字段自动对齐 pnpm 版本。

pnpm install
pnpm run dev              # Playground 文档站(分 Tab 演示;ref 全表见 ?tab=ref-api,见 playground/README.md)
pnpm run dev:minimal      # 仅最小宿主集成(对照 README「快速开始」)
pnpm run baseline         # 工程基线:测试数、最大 LOC、dist 体积(见 docs/ENGINEERING-BASELINE.md)
pnpm test                 # Vitest(含 flowOperation / deleteGuard 表驱动与 editorGateSmoke)
pnpm run typecheck
pnpm run check:docs       # 文档覆盖 WARN(onFlowOperation kind ↔ README)
pnpm run release:prep     # 发版门禁:test + build + pack:check + playground build + baseline
pnpm run verify:change    # 可选:test + typecheck + audit:white-screen(见 docs/ENGINEERING-BASELINE.md)

门控冒烟(无 DOM):editorGateSmoke.test.tsreadonlyModeGuards.test.ts — 图操作门控与只读改图拒绝;已包含在 pnpm test / release:prep

浏览器地址以终端输出为准(默认 http://localhost:5173)。文档站默认 ?tab=overview


维护者:发版

技术债与路线图

全项目优缺点与 P0–P3 优化优先级见 docs/PROJECT-AUDIT-2026.md(壳层拆分见 EDITOR-SPLIT-PLAN.md,性能见 PERFORMANCE-BUDGET.md)。OpenSpec 制品:openspec/changes/project-holistic-audit-and-optimization/

版本摘要

| 版本 | 宿主需注意 | |------|------------| | 1.3.6 | 企业化文档(架构/私有化/性能预算);autoLayout({ only })FLOW-OPERATION-REF;manual 路径清除修复;peer 同 1.3.2 | | 1.3.2 | onFlowOperation 统一图操作门控;拒 node.remove 时级联边原子保留;legacy 边/只读/配置区白屏加固;peer 同 1.3.0 | | 1.3.0 | 自由折线路径编辑、选中浮动条、路径 ref;peer 要求同 1.2.3 | | 1.2.3 | Breaking(集成)@xyflow/reactdagre 改为 peer,须宿主显式安装 | | 1.1.x | buildWechenAgentEditorProps、后端 DTO、删除守卫等 |

完整变更见 CHANGELOG.md

发版清单(当前版本见 package.json

  1. 版本号:确认根目录 package.json version 与 CHANGELOG 最新条目一致(npm 以该字段为准;当前 1.3.6)。
  2. 依赖与锁文件pnpm install(workspace 一次安装根与 playground/)。
  3. 门禁(推荐一条命令):
pnpm run release:prep

等价分步:pnpm testpnpm run buildpnpm run pack:checkpnpm --filter wechen-agent-playground run buildpnpm run baseline(基线摘要)。可选加强:pnpm run verify:change(含 audit:white-screen)、pnpm run check:docs

日志写入 .release-check.logrelease:prep 脚本)。

  1. 登录 registrynpm whoamiregistry.npmjs.org);国内镜像发版用 npm run release:cn(勿与 npmjs 混用同一 token 流程 unless intentional)。
  2. 发布
pnpm run release
# 或:pnpm publish --access public --registry=https://registry.npmjs.org/
  1. 发后验证
npm view wechen-agent-editor-v1 version
npm pack --dry-run   # tarball 应仅含 dist/、LICENSE(见 package.json files)
  1. 空目录烟测(将 <version> 换为 package.json 中的版本):
mkdir /tmp/wechen-smoke && cd /tmp/wechen-smoke
npm init -y
npm install wechen-agent-editor-v1@<version> react react-dom antd @xyflow/react dagre monaco-editor @monaco-editor/react
node -e "import('wechen-agent-editor-v1').then(m => console.log('ok', typeof m.WechenAgentEditor))"
  1. Git 标签(建议)git tag v1.3.6 && git push origin v1.3.6(版本号与 package.json 一致,按团队规范)。

发版前可将 repository / bugs / homepage 中的占位 URL 换成真实仓库地址(私有仓库可仅在内部文档记录,见「集成排错」说明)。