
CoComment 团队评论
CoComment 团队评论
zhaozicong1CoComment — 嘉立创EDA 团队评论批注扩展
版本:v0.4.1 适用:嘉立创EDA专业版 / EasyEDA Pro (EDA 引擎 ^3.0.0) 状态:本地评论 MVP + 手动协同(导出/导入工作流)已开发完成;实时多人协同等待嘉立创开放 API
在原理图和 PCB 画布上直接圈选区域、添加评论、追踪解决状态,类似 Figma 的评论协作能力。每个工程有独立的评论区,通过工程 UUID 自动隔离。团队协作通过 JSON 导出/导入工作流实现手动协同(A 导出 → 发给同事 → B 导入)。
一、功能列表
已实现
| 功能 | 说明 |
|---|---|
| 批注绘制 | 打开绘制 Dialog,手绘/粘贴截图/上传图片作为批注 |
| 评论线程管理 | 增删改查、未解决/已解决状态切换 |
| 评论 CRUD | 在线程下添加、删除评论(仅作者可删) |
| 视图自动跟随 | 缩放/平移画布时批注框自动跟随(sys_Timer 轮询) |
| 点击定位 | 在列表点线程卡片 → 画布定位到批注位置 + 闪烁高亮 |
| 按工程隔离 | 通过工程 UUID 自动隔离评论区,每个工程独立互不干扰 |
| 本地存储 | 评论数据存到 eda.sys_Storage,按工程 UUID 分区,刷新不丢 |
| JSON 导入导出 | 把当前工程所有评论导出为 JSON(文件名含工程名),或从 JSON 导入(跨工程导入时弹窗确认归属) |
| 用户设置 | 修改昵称 + 选择批注颜色(8 色可选) |
| 搜索过滤 | 按评论内容/作者名搜索,按状态(全部/未解决/已解决)过滤 |
| 显示/隐藏批注 | 一键切换画布上所有批注框的显隐 |
| 多行文字批注 | textarea 多行输入,6 档字号选择,Enter 换行 / Ctrl+Enter 提交 / Esc 取消 |
未实现
- ❌ 原理图批注(
SCH_Document坐标转换 API 未暴露) - ❌ 实时多人协同(等待嘉立创开放协作者列表、在线状态、实时光标、跨用户消息广播等 API,详见 DEV_DOC.md 阶段 3)
- ❌ 评论附件(图片/文件上传,EDA 无附件上传 API)
- ❌ @ 提及
二、编译
环境要求
- Node.js >= 20.17.0
- npm
编译命令
在 pro-api-sdk/ 目录下:
# 方式 1:仅编译到 dist/(开发调试用)
npm run compile
# 方式 2:编译 + 打包成 .eext(发布用)
npm run build
编译产物
npm run compile 产物(位于 pro-api-sdk/dist/):
dist/
├── index.js # 扩展主入口(esbuild 打包为 IIFE)
└── iframe/
├── panel.html # 评论面板
├── annotation.html # 批注渲染层
└── draw.html # 绘制 Dialog
npm run build 额外产物(位于 pro-api-sdk/build/dist/):
build/dist/
└── cocomment_v0.4.1.eext # 嘉立创EDA扩展包(zip 格式,按 .edaignore 过滤)
.eext 是嘉立创EDA扩展的官方打包格式,本质是 zip 压缩包,内部按 .edaignore 规则过滤掉 node_modules、src、.npm-cache 等开发文件,只保留运行时所需文件。
编译流程包含两步校验:
- TypeScript 编译(esbuild)
- HTML 内联
<script>语法检查(esbuild.transformSync,防止 HTML 直接复制导致 JS 语法错误不被发现)
三、安装
方式 A:本地开发加载(推荐调试用)
- 打开嘉立创EDA专业版
- 顶部菜单 → 扩展 → 扩展管理(或类似入口)
- 选择 加载本地扩展 / 开发模式加载
- 选择目录:
pro-api-sdk项目根目录(不是 dist,因为 extension.json 中entry: "./dist/index"已指向 dist) - 加载成功后,打开任意 PCB 工程,顶部菜单栏会出现 CoComment 菜单
方式 B:安装 .eext 包(发布用)
- 执行
npm run build生成build/dist/cocomment_v0.4.1.eext - 在嘉立创EDA专业版的扩展管理页面选择 从文件安装
- 选择
cocomment_v0.4.1.eext文件
四、使用方法
4.1 菜单功能
在原理图或 PCB 编辑器中,顶部 CoComment 菜单提供 6 个操作:
| 菜单项 | 功能 |
|---|---|
| 显示评论面板 | 切换右侧评论列表面板的显隐 |
| 添加批注 | 打开绘制 Dialog,手绘/粘贴截图/上传图片作为批注 |
| 显示/隐藏批注 | 切换画布上所有批注框的显隐 |
| 导出评论 | 导出当前工程所有评论为 JSON(手动协同:发给同事) |
| 导入评论 | 从 JSON 文件导入评论(手动协同:接收同事的 JSON) |
| 关于 CoComment | 显示版本信息 |
Home 页面只有"关于"一个菜单。
4.2 面板交互
右侧评论面板内:
- + 按钮:添加批注(同菜单)
- ⚙ 设置:修改昵称和批注颜色
- 👁 显隐:切换批注框显隐
- ⬇ / ⬆:导出 / 导入
- 搜索框:实时搜索评论内容和作者名
- 状态过滤:全部 / 未解决 / 已解决
- 点线程卡片:画布定位到批注位置 + 闪烁高亮
- 展开线程:发新评论、删除自己的评论、解决/重开线程

4.3 典型工作流(添加一条评论)
1. 点菜单 "添加批注"
2. 弹出"绘制批注" Dialog,可用画笔/矩形/箭头/文字手绘,或 Ctrl+V 粘贴截图,或上传本地图片
3. 点"确认"关闭 Dialog
4. 右侧面板自动出现新线程,输入框聚焦
5. 输入评论内容,回车提交
6. 画布上出现批注图像 + 序号徽章

4.4 团队协作工作流(手动协同)
A 同事:
1. 在自己的 EDA 客户端打开工程 X,添加/修改评论
2. 点菜单 "导出评论" → 生成 cocomment_<工程X>_<时间戳>.json 文件
3. 把 JSON 文件发给同事(微信/邮件/共享文件夹均可)
B 同事:
1. 收到 JSON 文件
2. 在自己的 EDA 客户端点菜单 "导入评论" → 选择 JSON 文件
3. 系统校验工程归属:
- 如果 B 当前正好打开工程 X → 直接导入,面板刷新显示评论
- 如果 B 打开的是工程 Y → 弹窗提示"该评论属于工程X,是否导入?"
→ 确认后评论挂到工程 X 分区下,B 需切换到工程 X 才能看到
⚠️ 注意:这是手动协同(非实时),需要 A 主动导出、B 主动导入。导入会覆盖目标工程的评论。实时多人协同(看到对方光标、评论即时推送)等待嘉立创开放 API。
为什么不用工程文档同步? v0.2.0 曾尝试用
eda.sys_FileManager.setDocumentSource把评论序列化进工程文档源码搭便车同步,但 PoC 验证 EDA 拒绝修改文档源码(返回 false)。EDA 也没有附件上传 API。因此团队协作只能走 JSON 文件交换。
五、项目结构
pro-api-sdk/
├── extension.json # 扩展清单(菜单注册 + entry 指向 dist/index)
├── package.json # npm 脚本和依赖
├── tsconfig.json # TypeScript 配置
├── .edaignore # .eext 打包过滤规则
│
├── src/ # 扩展源码(TypeScript)
│ ├── index.ts # 入口:注册菜单、装配各模块
│ │
│ ├── core/ # 业务核心层
│ │ ├── CommentEngine.ts # 评论引擎(总控入口)
│ │ ├── ThreadManager.ts # 评论线程管理
│ │ ├── CommentManager.ts # 评论管理
│ │ ├── AnnotationRenderer.ts # 批注渲染器(视图轮询 + 坐标换算)
│ │ └── Navigator.ts # 定位导航
│ │
│ ├── ui/ # UI 控制层
│ │ ├── PanelController.ts # 业务编排 + 消息路由 + 方案B同步入口
│ │ ├── IframeManager.ts # sys_IFrame 窗口管理(panel/overlay/draw)
│ │ └── MessageBridge.ts # sys_MessageBus 跨 context 通信桥
│ │
│ ├── iframe/ # iframe 承载的 UI
│ │ ├── panel.html # 评论面板(右侧列表)
│ │ ├── annotation.html # 批注渲染层
│ │ └── draw.html # 绘制 Dialog(手绘/粘贴/上传图片)
│ │
│ ├── sync/ # 存储同步层
│ │ ├── SyncProvider.ts # 同步接口(阶段切换时换实现)
│ │ └── LocalSync.ts # 本地存储实现(基于 sys_Storage)
│ │
│ ├── types/ # 类型定义
│ │ ├── comment.ts # CommentThread / Comment / BBox
│ │ ├── user.ts # User
│ │ ├── sync.ts # SyncOp / ProjectData / LocalData
│ │ └── messages.ts # 跨 iframe 消息协议
│ │
│ └── utils/ # 工具函数
│ ├── coord.ts # 坐标换算(逻辑坐标 ↔ 屏幕坐标)
│ ├── id.ts # UUID 生成
│ ├── i18n.ts # 多语言(zh-Hans)
│ └── ProjectContext.ts # 工程上下文获取(工程 UUID / 名称 / 页面类型)
│
├── config/ # 构建配置
│ ├── esbuild.common.ts # esbuild 公共配置
│ ├── esbuild.dev.ts # 开发模式(watch)
│ └── esbuild.prod.ts # 生产编译(含 HTML 语法检查 + iframe 复制)
│
├── build/
│ └── packaged.ts # .eext 打包脚本
│
├── dist/ # 编译产物(compile 后生成)
│ ├── index.js
│ └── iframe/
│ ├── panel.html
│ ├── annotation.html
│ └── draw.html
│
└── build/dist/ # 打包产物(build 后生成)
└── cocomment_v0.4.1.eext
六、技术原理(简述)
扩展本质 = 被嘉立创EDA加载到主进程 JS 上下文里的一段代码:
- EDA 读取 extension.json → 用户点菜单时反射调用
dist/index.js中对应的导出函数 eda全局对象 = 扩展调用宿主能力的唯一通道(sys_*/pcb_*/sch_*命名空间 API)- 批注渲染 = 通过
eda.sys_IFrame.openIFrame打开 iframe 窗口(panel/annotation/draw),用eda.sys_MessageBus跨 context 通信 - 视图同步 =
eda.sys_Timer.setIntervalTimer轮询视图状态,检测变化时刷新批注位置 - 坐标换算 =
eda.pcb_Document.convertDataOriginToCanvasOrigin/convertCanvasOriginToDataOrigin(逻辑坐标 ↔ 画布像素坐标) - 通信 =
eda.sys_MessageBus.publish/subscribe(主进程 ↔ iframe 双向,按 topic 路由,构造时清理旧订阅防泄漏) - 存储 =
eda.sys_Storage.getExtensionUserConfig/setExtensionUserConfig(按用户隔离,主进程可调) - 工程隔离 =
eda.sys_DocumentTree.getCurrentProjectInfo()获取工程 UUID 作为 projectId,每个工程独立评论区 - 团队协作 = JSON 文件导入导出(
eda.sys_FileSystem.saveFile/openReadFileDialog),手动协同工作流
详见 docs/DEV_DOC.md。
七、首次加载必做的验证
扩展尚未在真实 EDA 环境跑过,第一次加载后请打开开发者工具(F12)观察控制台:
正常日志:
[CoComment] activate() called, status= onStartupFinished
[CoComment] closed stale iframes on activate
[CoComment] ensureInitialized() done
重点排查 3 个可能失败的点:
-
eda.sys_IFrame.openIFrame行为 预期打开的是带标题栏的 Dialog 窗口(不是透明覆盖层)。如果弹出的窗口是空白,检查 src/iframe/draw.html 内的 onerror 错误提示框。 -
消息是否重复触发 发送一条评论后看控制台日志次数。如果每条消息触发多次,可能是旧订阅未清理,检查
globalThis.__cocomment_messagebus_tasks__是否被正确维护(见 src/ui/MessageBridge.ts)。 -
iframe 路径 控制台看 panel.html / draw.html 是否 404。当前用
./iframe/xxx.html(相对 index.js 所在目录)。
如果遇到问题,把 F12 控制台的 [CoComment] 日志和报错贴出来排查。
八、开发
重新编译
修改 src/ 下任何文件后,重新执行:
npm run compile
然后在 EDA 扩展管理页面重新加载扩展(或重启 EDA)。
开发模式(watch)
npm run dev
esbuild 会监听文件变化自动重新编译到 dist/。
修改菜单
菜单注册在 extension.json 的 headerMenus 字段,按 home / sch / pcb 三个页面分别配置。
九、参考链接
0.4.1
新增
- 导出文件名加工程名:
cocomment_<工程名>_<时间戳>.json,让用户分清是哪个工程的评论 - 导出 JSON 注入工程元数据:含
projectId/projectName字段,用于导入时校验工程归属 - 导入工程归属校验:如果导入的 JSON 属于另一个工程,弹窗提示用户确认,评论挂到原工程分区下(而非当前工程)
变更
ProjectData接口新增projectId?/projectName?可选元数据字段CommentEngine新增importProjectTo(projectId, data)方法,支持导入到指定工程分区PanelController.exportComments注入工程元数据 + 用工程名命名文件PanelController.importComments校验 projectId,跨工程导入时弹窗确认- 版本号 0.4.0 → 0.4.1
0.4.0
新增
- 按工程隔离评论区:每个工程有独立的评论区,互不干扰
- 自动识别当前工程:通过
eda.sys_DocumentTree.getCurrentProjectInfo()获取工程 UUID 作为 projectId - 工程切换检测:在 togglePanel / addAnnotation 入口检查工程是否切换,自动重载当前工程的评论
- 新增
src/utils/ProjectContext.ts模块:封装工程上下文获取逻辑,含 fallback 兜底
变更
- 修复
ThreadManager.projectId永远为'default'的问题:所有工程的评论之前都堆在一起 ensureInitialized中调用getCurrentProjectContext设置真实的工程 UUID 到 enginePanelController新增checkProjectSwitched方法,在 togglePanel/startDrawing 入口检测工程切换- 版本号 0.3.0 → 0.4.0
0.3.0
变更
- 移除方案B(工程文档同步):PoC 验证
eda.sys_FileManager.setDocumentSource返回 false,EDA 拒绝修改文档源码(标记块注入破坏源码格式解析)。方案B 不可行,已移除相关代码 - 删除
src/sync/ProjectSync.ts模块(标记块注入/提取、原始源码备份、紧急恢复) - 移除三个菜单项(sch + pcb):同步评论到工程 / 从工程读取评论 / 恢复工程源码
- 移除
PanelController中的方案B 方法:syncToProject / syncFromProject / restoreProjectBackup / confirm / showSyncResult - 团队协作改为导出/导入工作流:A 同事用"导出评论"生成 JSON → 发给同事 → B 同事用"导入评论"恢复。纯靠文件交换,不修改工程文档源码
- 确认 EDA 无附件上传 API:已查全部 DMT_*/SYS_FileSystem 类,均无云端附件上传能力。实时多人协同仍需等待嘉立创开放 API 或自建 WebSocket 后端
- 版本号 0.2.0 → 0.3.0
0.2.0
新增
- 新增方案B:评论数据与工程文档双向同步(准协同),把评论序列化为标记块(
%%COCOMMENT_V1:<base64>%%)追加到当前 sch/pcb 文档源码末尾,靠 EDA 自身的工程同步机制传播给团队成员 - 新增
src/sync/ProjectSync.ts模块:基于eda.sys_FileManager.getDocumentSource/setDocumentSource两个 BETA API 实现评论数据与文档源码的双向同步,含标记块注入/提取、原始源码备份、紧急恢复 - 新增"同步评论到工程"菜单(sch + pcb):把当前所有评论写入工程文档源码,写入前弹窗确认风险并自动备份原始源码
- 新增"从工程读取评论"菜单(sch + pcb):从工程文档源码提取评论数据并恢复到本地存储,自动刷新面板
- 新增"恢复工程源码"菜单(sch + pcb):紧急恢复,用上次同步前的备份覆盖当前文档源码,还原设计数据
- 新增
eda.sys_Dialog.showConfirmationMessage确认弹窗封装(PanelController.confirm),用于方案B写入前的风险确认 - 新增 base64 编码三级降级策略:Buffer(Node 主进程)→ btoa(浏览器/iframe)→ hex(纯 JS),保证主进程和 iframe 均可编解码
变更
- 阶段2技术方案由"自建后端 REST API 云同步"调整为"方案B:评论随工程文档同步",复用 EDA 自身的团队工程协作机制,无需自建服务器
- 阶段3"实时协同"标记为等待嘉立创开放 API:当前 EDA 未暴露协作者列表、在线状态、实时光标、跨用户消息广播、共享 KV 存储等 API,无法实现真正的实时多人协同
- 版本号 0.1.0 → 0.2.0
0.1.0
新增
- 新增 CoComment 团队评论批注扩展:在 PCB 画布上拖框圈选区域,添加评论线程
- 新增评论线程管理:创建、删除、解决、重开,支持未解决/已解决状态切换
- 新增评论 CRUD:在线程下添加评论、删除评论(仅作者可删)
- 新增视图自动跟随:缩放/平移画布时批注框自动跟随(requestAnimationFrame 轮询 eda.pcb_Document.zoomTo)
- 新增点击定位:在列表点线程卡片 → 画布定位到批注位置 + 闪烁高亮
- 新增本地存储:评论数据存到 localStorage,刷新不丢
- 新增 JSON 导入导出:当前工程所有评论可导出为 JSON,或从 JSON 导入
- 新增用户设置:修改昵称 + 选择批注颜色(8 色可选)
- 新增搜索过滤:按评论内容/作者名搜索,按状态(全部/未解决/已解决)过滤
- 新增右侧评论面板(panel.html)和透明批注覆盖层(annotation.html)两个 iframe
- 新增 IframeManager 模块:集中处理 iframe 创建/显隐/消息,探测官方 sys_PanelControl/sys_IFrame,不存在时降级到浏览器原生 HTMLIFrameElement
- 新增统一消息协议类型定义(types/messages.ts):PanelMessage / OverlayMessage / Inbound 四类联合类型,结束消息字段全靠 any 的状态
- 新增 HTML 内联 script 语法检查:集成到 npm run compile 流程,用 esbuild.transformSync 抽取 HTML 内 <script> 做纯语法检查,防止 HTML 直接复制导致 JS 语法错误不被发现
- 新增开发文档 docs/DEV_DOC.md 和插件说明 README.md
变更
- 修正虚构 API 调用:按真实类型定义文件 @jlceda/pro-api-types/index.d.ts 核对,移除所有不存在的 API(sys_Storage、sys_I18n.getLocale、sys_ToastMessage、sys_PanelControl 等),改用浏览器原生 localStorage 和 (eda as any) 防御性探测
- 修正批注框不跟随视图变化的 bug:setupViewPolling 加 lastViewKey 变化检测,viewKey 变了才调用 renderAll
- 修正 iframe url 路径:./src/iframe/panel.html → ./iframe/panel.html(编译后 dist 结构是 dist/index.js + dist/iframe/*.html,运行时相对路径应该是 ./iframe/)
- 修正导入流程重复刷新:handleImport 里 refreshThreads() 后又调 refreshRenderer(),后者已包含前者,删除重复调用
- 修正数据变更双重刷新无防抖:创建 thread 时 onThreadChange + onCommentChange 连续触发两次 refreshThreads,加 50ms 防抖合并为一次
- 解耦 AnnotationRenderer:移除从未使用的 ISyncProvider 死依赖,postMessage(CustomEvent) 改为构造函数注入 onSendToOverlay 回调
- 解耦 PanelController:抽离 iframe 创建/显隐/销毁职责到 IframeManager(453 行 → 269 行),导出导入改为 public 方法供 index.ts 复用
- 移除 ThreadManager 搜索死代码:filter.search 按 label 搜但 panel 按评论内容搜,语义冲突且无人调用,从 ThreadFilter 接口删除
- 移除 Navigator.flashThread 死代码:发 CustomEvent 但全代码库无人监听
- CommentEngine.setCurrentUser 不再用 init() 复用(语义不清,会重新走冷启动),改用新增的 refreshUser 热更新方法

类型
关键词
扩展信息
| 版本 | v0.4.1 |
| 发布者 | zhaozicong |
| 发布时间 | 2026-07-20 23:47:28 |
| 名称 | cocomment |
| UUID | df35742209c346818b65b9899677c10f |
| 适用EDA版本: | ^3.0.0 |
| 报告 | 报告滥用 |

评论