关于 easyeda-api
easyeda-api 是面向使用 EasyEDA Pro 的 AI Agent 的技能,涵盖 API、文档格式和扩展开发。它将参考资料与 WebSocket 桥接服务结合,可在运行中的 EDA 客户端执行代码并验证行为。
主要功能
- 01API 类与类型参考
- 02通过 WebSocket 桥接执行代码
- 03PCB、原理图、库和项目操作范例
- 04项目、原理图与 PCB 源格式指南
- 05扩展开发与调试指导
适用场景
- 01开发 EasyEDA 扩展时查询 API
- 02在已连接的 EasyEDA Pro 中运行代码
- 03检查或修改 EasyEDA 文档源数据
- 04验证 PCB 和原理图操作
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ easyeda/ easyeda-api-skill --skill easyeda-api安装后在 Claude Code 中输入/easyeda-api确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdREADME.zh-Hans.mdformat/index.mdformat/pcb/obj.mdcommon.mdattr.md3d.mddimension.mdcomponent.mdindex.mdpad_via.mdpanel.mdrule.mdpartition.mdprimitive.mdtext.mdshape.mdformat/project/common.mdgroup.mdblob.mdindex.mdmeta.mdvariant.mdinstance.mdformat/schematic/common.mdobj.mdcomponent.mdindex.mdpin.mdshape.md详细介绍
EasyEDA Pro API 技能
通过 AI 以编程方式控制 EasyEDA Pro(嘉立创EDA专业版)。此技能提供:
- 完整的 API 参考 — 120 个类、62 个枚举、70 个接口、19 个类型别名
- WebSocket 桥接 — 在运行中的 EasyEDA Pro 客户端内执行代码
- 代码模式 — PCB、原理图、库和项目管理的常见操作
- 文档源格式参考 — 用户需要直接分析或修改 EasyEDA 文档源文件,而非使用 API 时,可查阅项目、原理图和 PCB 源文件的格式规范
此技能不仅支持在 EasyEDA 中进行实时调试,也支持 EasyEDA 扩展开发。 在 EasyEDA 扩展开发过程中,AI Agent 可以使用此技能提供的扩展相关文档、API 参考、类型信息、用法示例和基于桥接的调试功能,查询 API、编写代码,并在集成和调试期间验证行为。
架构
┌───────────┐ HTTP/WS ┌─────────────────┐ WebSocket ┌───────────┐
│ AI Agent │ ◄───────────► │ Bridge Server │ ◄───────────► │ EasyEDA │
│ │ Port Range │ (Node.js) │ Port Range │ (Client) │
└───────────┘ 49620-49629 └─────────────────┘ 49620-49629 └───────────┘
服务器启动时会从 49620-49629 中自动选择一个可用端口。
AI 和 EDA 客户端都会通过扫描端口范围并验证握手(service: "easyeda-bridge")
自动发现服务器。
快速开始
1. 命令触发词
当用户说 “嘉立创EDA,启动!”、“立创EDA,启动!” 或 “EDA,启动!” 时:
重要:会话初始化 — 立即回复以下准确文本,以设置正确的会话标题:
📋 EasyEDA Session
然后继续执行以下设置步骤。
2. 安装依赖(如有需要)
cd ${CLAUDE_SKILL_DIR} && npm install
3. 启动桥接服务器
⚠️ 重要:桥接服务器必须在后台运行。切勿在前台运行,否则 AI 将一直等待服务器退出而被阻塞。
# Check if bridge is already running
for port in $(seq 49620 49629); do
resp=$(curl -s http://localhost:$port/health 2>/dev/null)
if echo "$resp" | grep -q '"easyeda-bridge"'; then
echo "Bridge already running on port $port"
BRIDGE_PORT=$port
break
fi
done
# Start bridge if not running
if [ -z "$BRIDGE_PORT" ]; then
node ${CLAUDE_SKILL_DIR}/scripts/bridge-server.mjs &
sleep 2
# Find the port bridge is running on
for port in $(seq 49620 49629); do
resp=$(curl -s http://localhost:$port/health 2>/dev/null)
if echo "$resp" | grep -q '"easyeda-bridge"'; then
BRIDGE_PORT=$port
break
fi
done
fi
echo "Bridge running on port: ${BRIDGE_PORT:-unknown}"
4. 连接 EasyEDA
在 EasyEDA Pro 中安装 run-api-gateway.eext 扩展。下载链接:
加载扩展后,它会自动建立 WebSocket 连接。
5. 验证连接并选择 EDA 窗口
# Check bridge and EDA connection status
curl http://localhost:${BRIDGE_PORT:-49620}/health
# List all connected EDA windows
curl http://localhost:${BRIDGE_PORT:-49620}/eda-windows
/eda-windows 的响应如下:
{
"windows": [
{ "windowId": "abc-123", "connected": true, "active": true },
{ "windowId": "def-456", "connected": true, "active": false }
],
"activeWindowId": "abc-123",
"count": 2
}
根据窗口数量处理:
- 0 个窗口:告知用户“没有连接 EasyEDA 窗口。请确保已在 EasyEDA 中安装并加载 EasyEDA 扩展(run-api-gateway.eext)。下载地址:https://jlc-ext.com/item/oshwhub/run-api-gateway”
- 1 个窗口:自动选为活动窗口。告知用户:“✅ 已连接!活动 EDA 窗口:abc-123。可以开始工作了。”
- 2 个或更多窗口:显示可用窗口,并请用户选择:
Multiple EDA windows detected: - abc-123 (active) - def-456 Which EDA window should I use?
选择窗口:
curl -X POST http://localhost:${BRIDGE_PORT}/eda-windows/select \
-H "Content-Type: application/json" \
-d '{"windowId": "abc-123"}'
选择后,确认:“✅ 活动 EDA 窗口:abc-123。可以开始工作了。”
6. 在 EDA 中执行代码
curl -X POST http://localhost:${BRIDGE_PORT:-49620}/execute \
-H "Content-Type: application/json" \
-d '{"code": "return await eda.dmt_Project.getCurrentProjectInfo();"}'
API 文档
完整的 API 参考位于 references/ 目录中:
- references/_index.md — 所有类、枚举、接口和类型的总索引
- references/_quick-reference.md — 所有方法签名,便于快速查找
references/classes/— 120 个类文档(DMT_、PCB_、SCH_、LIB_、SYS_、IPCB_、ISCH_*)references/enums/— 62 个枚举文档references/interfaces/— 70 个接口文档references/types/— 19 个类型别名文档
如何查找 API
- 从
_index.md开始,查找适用于任务的类或模块 - 阅读类文档(例如
references/classes/DMT_Board.md),查看所有方法和签名 - 使用
_quick-reference.md,快速查找所有类的方法签名 - 查看枚举和接口,了解参数类型和返回类型
文档源格式文档
如果用户需要直接分析或修改 EasyEDA 文档源,而不是使用 API,请使用 format/ 目录中的文档。
- format/index.md — EasyEDA 文档源格式参考概览及版本说明
format/project/— 项目源结构、元数据、二进制数据、变体和分组数据format/schematic/— 原理图源格式,包括结构、导线、形状、引脚、组件和表格format/pcb/— PCB 源格式,包括图元、焊盘和过孔、形状、文本、属性、规则和拼板数据
当任务涉及了解源布局、生成兼容的文档源,或编辑之后将重新导入 EasyEDA 的源数据时,请使用这些文件。
API 模块概览
| 前缀 | 领域 | 主要类 |
|---|---|---|
DMT_ | 文档管理 | Board, EditorControl, Folder, Panel, Pcb, Project, Schematic, SelectControl, Team, Workspace |
PCB_ | PCB 和封装 | Document, Drc, Event, Layer, Net, Primitive, PrimitiveComponent, PrimitiveLine, PrimitivePad, PrimitivePour, PrimitiveVia, SelectControl |
SCH_ | 原理图 | Document, Event, Primitive, PrimitiveComponent, PrimitiveWire, SelectControl |
LIB_ | 库 | 3DModel, Cbb, Classification, Device, Footprint, LibrariesList, PanelLibrary, SelectControl, Symbol |
SYS_ | 系统 | Dialog, Environment, FileManager, FileSystem, FontManager, HeaderMenu, I18n, IFrame, LoadingAndProgressBar, Log, Message, MessageBox, MessageBus, PanelControl, Setting, ShortcutKey, Storage, Timer, ToastMessage, WebSocket, Window |
IPCB_ | PCB 接口(图元) | PrimitiveArc, PrimitiveComponent, PrimitivePad, PrimitiveFill, PrimitivePour, PrimitiveRegion, PrimitiveVia, ... |
ISCH_ | 原理图接口(图元) | PrimitiveArc, PrimitiveComponent, PrimitiveWire, PrimitiveText, PrimitiveRectangle, ... |
EPCB_ / ESCH_ | 枚举 | PrimitiveType, Layer, PadType, ... |
代码执行上下文
所有代码均在 EasyEDA Pro 的浏览器运行时中执行,形式如下:
async function(eda) {
// Your code here — `eda` is the global EDA API object
// You MUST use `return` to send results back
// Do not add comments to the generated code, as the code is typically executed as a single line
}
重要规则:
eda对象提供对所有 API 模块的访问权限(例如eda.dmt_Board、eda.pcb_Primitive)。- 始终参阅 API 文档以确保正确使用。
- 不要在生成的代码中添加注释,因为代码通常作为单行执行。
- 始终使用
return获取结果——不会捕获console.log输出。 - 所有返回 Promise 的 API 方法都必须使用
await。 - 代码在浏览器上下文中运行——无法使用 Node.js API(如 fs、path 等)。
- 使用
eda.sys_Message.showToastMessage(msg)向用户显示通知。 - 查阅 API 文档并遇到枚举时,不要猜测枚举值。必须使用枚举成员,例如,在创建 PCB 图元时,图层参数应使用
EPCB_LayerId.TOP,而不是1。
扩展运行时限制
编写 EasyEDA 扩展时,主进程中禁止使用标准浏览器 API。请使用 EDA 提供的替代方案:
| 用途 | ❌ 禁止使用 | ✅ 替代方案 |
|---|---|---|
| 获取用户输入 | — | eda.sys_Dialog.showInputDialog() |
| 用户选择 | — | eda.sys_Dialog.showSelectDialog() |
| 显示消息 | alert() | eda.sys_Dialog.showInformationMessage() |
| 确认操作 | confirm() | eda.sys_Dialog.showConfirmationMessage() |
| Toast 通知 | 操作 DOM | eda.sys_Message.showToastMessage() |
| 存储数据 | localStorage(主进程) | eda.sys_Storage.setExtensionUserConfig(key, value) |
| 自定义 UI | 操作宿主 DOM | eda.sys_IFrame.openIFrame() |
| 显示 HTML | showInformationMessage(html) | 必须使用 iframe |
| 打开链接 | window.open() | eda.sys_Window.open() |
| 浏览器硬件 API | 在主进程中使用 | 在 iframe 中可用(navigator.serial 等) |
注意: localStorage、window、document 等在 sys_IFrame 内可用,但在主扩展进程中不可用。
扩展 ↔ IFrame 数据传递
主扩展进程和 sys_IFrame iframe 是隔离上下文。如需在两者之间传递数据:
选项 A(推荐): 使用 eda.sys_Storage 作为桥接
// In main extension process:
await eda.sys_Storage.setExtensionUserConfig('myKey', JSON.stringify(data));
// In iframe:
const data = JSON.parse(await eda.sys_Storage.getExtensionUserConfig('myKey'));
选项 B: 两种上下文都可以直接访问 eda——从任一侧调用相同的 API
// In iframe HTML (NOT window.parent.eda):
const info = await eda.dmt_Project.getCurrentProjectInfo();
通信协议
端口发现
桥接服务会监听 49620-49629 范围内第一个可用的端口。 要查找服务,请扫描该端口范围并验证服务标识:
for port in $(seq 49620 49629); do
resp=$(curl -s http://127.0.0.1:$port/health 2>/dev/null)
if echo "$resp" | grep -q '"easyeda-bridge"'; then
BRIDGE_PORT=$port; break
fi
done
握手
- HTTP:
GET /health返回{ "service": "easyeda-bridge", "edaConnected": bool, ... } - WebSocket:连接时,服务端发送
{ "type": "handshake", "service": "easyeda-bridge" } - 客户端在使用连接前,必须验证
service === "easyeda-bridge"
消息格式
通过 WebSocket / HTTP 传输的 JSON 消息:
| 字段 | 类型 | 说明 |
|---|---|---|
type | "execute" | "result" | "error" | "ping" | "pong" | "handshake" | 消息类型 |
id | string | 用于匹配请求与响应的请求 UUID |
code | string | 要执行的 JavaScript 代码(用于 execute 类型) |
result | any | 执行结果(用于 result 类型) |
error | string | 错误消息(用于 error 类型) |
service | string | 服务标识符(用于 handshake 类型) |
timestamp | number | Unix 毫秒时间戳 |
常见模式
项目和电路板操作
// Get current project info
return await eda.dmt_Project.getCurrentProjectInfo();
// List all boards in project
return await eda.dmt_Board.getAllBoardsInfo();
// Create a new board (optionally link to schematic/PCB)
return await eda.dmt_Board.createBoard();
// Switch to a document tab
await eda.dmt_EditorControl.activateDocument(tabId);
示例:按名称打开项目并打开其第一个原理图页
const targetProjectName = 'NE555 Circuit';
// 1. Enumerate projects by team/folder and find the target by name
const teams = await eda.dmt_Team.getAllTeamsInfo();
const seenProjectUuid = new Set();
let targetProjectUuid = null;
for (const team of teams || []) {
const teamProjects = await eda.dmt_Project.getAllProjectsUuid(team.uuid);
for (const projectUuid of teamProjects || []) {
if (seenProjectUuid.has(projectUuid)) continue;
seenProjectUuid.add(projectUuid);
const info = await eda.dmt_Project.getProjectInfo(projectUuid);
const name = info?.friendlyName || info?.name || '';
if (name === targetProjectName) {
targetProjectUuid = projectUuid;
break;
}
}
if (targetProjectUuid) break;
const folderUuids = await eda.dmt_Folder.getAllFoldersUuid(team.uuid);
for (const folderUuid of folderUuids || []) {
const folderProjects = await eda.dmt_Project.getAllProjectsUuid(team.uuid, folderUuid);
for (const projectUuid of folderProjects || []) {
if (seenProjectUuid.has(projectUuid)) continue;
seenProjectUuid.add(projectUuid);
const info = await eda.dmt_Project.getProjectInfo(projectUuid);
const name = info?.friendlyName || info?.name || '';
if (name === targetProjectName) {
targetProjectUuid = projectUuid;
break;
}
}
if (targetProjectUuid) break;
}
if (targetProjectUuid) break;
}
if (!targetProjectUuid) {
throw new Error(`Project not found: ${targetProjectName}`);
}
// 2. Open the project
const opened = await eda.dmt_Project.openProject(targetProjectUuid);
if (!opened) {
throw new Error(`Failed to open project: ${targetProjectName}`);
}
// 3. Get the first schematic page and open it
const schematics = await eda.dmt_Schematic.getAllSchematicsInfo();
if (!Array.isArray(schematics) || schematics.length === 0) {
throw new Error(`No schematics found in project: ${targetProjectName}`);
}
const firstSchematic = schematics[0];
const pages = await eda.dmt_Schematic.getAllSchematicPagesInfo();
const firstPage = (pages || []).find((page) => page.parentSchematicUuid === firstSchematic.uuid)
|| firstSchematic.page?.[0];
if (!firstPage) {
throw new Error(`No schematic page found in: ${firstSchematic.name}`);
}
const tabId = await eda.dmt_EditorControl.openDocument(firstPage.uuid);
return {
projectUuid: targetProjectUuid,
schematicUuid: firstSchematic.uuid,
pageUuid: firstPage.uuid,
tabId,
};
注意:
- 实际上,
dmt_Project.getAllProjectsUuid()并非一个全局且无需参数的枚举方法。要可靠地按名称查找项目,请先遍历团队,再遍历文件夹。 openProject(projectUuid)可能会丢弃当前打开项目中未保存的更改。调用前请谨慎。
PCB 图元操作
// Get selected primitives
const selected = eda.pcb_SelectControl.getAllSelectedPrimitives_PrimitiveId();
// Create a line on copper layer
// Layer parameter uses EPCB_LayerId enum — see references/enums/EPCB_LayerId.md
await eda.pcb_PrimitiveLine.create(
"GND", // net (string)
EPCB_LayerId.TOP, // layer — use enum, NOT raw number
0, // startX (unit: 1mil)
0, // startY (unit: 1mil)
1000, // endX (unit: 1mil)
0, // endY (unit: 1mil)
10, // lineWidth
false // primitiveLock
);
// Modify an existing primitive (async pattern)
const prim = await eda.pcb_PrimitiveComponent.get([id]);
const asyncPrim = prim.toAsync();
asyncPrim.setState_X(newX);
asyncPrim.setState_Y(newY);
asyncPrim.done();
PCB 异步图元模式(重要)
要修改 PCB/SCH 图元,必须使用异步模式:
// 1. Get the primitive
const prim = await eda.pcb_PrimitiveVia.get([viaId]);
// 2. Convert to async mode
const asyncPrim = prim.toAsync();
// 3. Set new values
asyncPrim.setState_X(newX);
asyncPrim.setState_Y(newY);
asyncPrim.setState_Diameter(diameter);
// 4. Apply changes
asyncPrim.done();
原理图操作
// Get all pages
return await eda.dmt_Schematic.getAllSchematicDocumentsInfo();
// Create a schematic component
// component parameter requires {libraryUuid, uuid} object — NOT a plain string
await eda.sch_PrimitiveComponent.create(
{ libraryUuid: "...", uuid: "device-uuid-from-library" }, // component object
5000, // x (unit: 0.01inch = 10mil)
5000, // y (unit: 0.01inch = 10mil)
"", // subPartName
0, // rotation (degrees, plain number)
false, // mirror
true, // addIntoBom
true // addIntoPcb
);
// Get selected schematic primitives
const ids = eda.sch_SelectControl.getAllSelectedPrimitives_PrimitiveId();
库操作
// List all libraries in workspace
return await eda.lib_LibrariesList.getAllLibrariesList();
// Search devices
return await eda.lib_Device.search("STM32");
// Get symbol info
return await eda.lib_Symbol.get(symbolUuid);
系统函数
// Show toast message
eda.sys_Message.showToastMessage("Operation complete!");
// Show confirm dialog
const confirmed = await eda.sys_Dialog.showConfirmationMessage("Proceed?");
// File system (limited to extension sandbox)
const content = await eda.sys_FileSystem.readFileFromFileSystem(path);
await eda.sys_FileSystem.saveFileToFileSystem(path, content);
DRC(设计规则检查)
// Run DRC check
const passed = await eda.pcb_Drc.check(true, true, false);
// passed is boolean: true if DRC passed, false if errors found
常见错误
仔细阅读 API 签名(至关重要)
调用任何 API 方法之前,必须从 references/ 中阅读完整签名,包括参数类型、返回类型和备注。
AI Agent 经常会因匆忙浏览文档而犯下以下错误:
错误 1:没有对返回 Promise 的方法使用 await
几乎所有 EDA API 方法都会返回 Promise<T>。如果忘记使用 await,得到的将是 Promise 对象,而不是实际结果。
// WRONG: Missing await — result is a Promise, not the project info
const project = eda.dmt_Project.getCurrentProjectInfo();
console.log(project); // Promise { <pending> }
// CORRECT: Always await async methods
const project = await eda.dmt_Project.getCurrentProjectInfo();
console.log(project); // { uuid: "...", name: "...", ... }
**如何判断方法是否需要使用 await:**查看签名中的返回类型。如果显示 Promise<...>,就必须使用 await。
// From references/classes/DMT_Project.md:
getCurrentProjectInfo(): Promise<IDMT_ProjectItem | null>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// This is a Promise — you MUST await it
错误 2:使用原始数字,而不是枚举值
许多参数都需要特定的枚举值。使用错误的数字不会报错,却会导致行为不正确。
// WRONG: What does layer "1" mean? Which copper layer? Signal? Mask?
await eda.pcb_PrimitiveLine.create("GND", 1, 0, 0, 1000, 0, 10, false);
// CORRECT: Use the enum — EPCB_LayerId.TOP is clear and type-safe
// Check references/enums/EPCB_LayerId.md for the correct value
await eda.pcb_PrimitiveLine.create("GND", EPCB_LayerId.TOP, 0, 0, 1000, 0, 10, false);
使用数值常量之前,务必先在 references/enums/ 中查找对应的枚举。
错误 3:未经确认就假定参数类型
不同 API 使用的单位、ID 格式和可选参数约定各不相同。切勿想当然。
// WRONG: Assuming create() parameters match modify() parameters
// create() and modify() often have DIFFERENT parameter orders/types
// CORRECT: Always read the exact signature for the method you're calling
// Check references/classes/<ClassName>.md for each method's specific signature
总结 — 每次调用 API 之前:
- 阅读签名——查看参数类型、返回类型和
references/classes/中的所有内容 - 如果返回
Promise,就使用await——检查返回类型是否为Promise<...> - 使用枚举——在
references/enums/中查找,不要猜测数字或字符串 - 检查备注——“备注”部分通常包含重要的使用说明
坐标单位(至关重要)
不同领域使用不同的坐标单位:
| 领域 | 单位 | 换算 |
|---|---|---|
| PCB | 1mil | 1mm ≈ 39.37 个单位 |
| 原理图 | 0.01inch(10mil) | 1mm ≈ 3.937 个单位 |
**这是 AI Agent 最常犯的错误。**混淆单位会导致元件放置位置不正确。
- PCB:1 个单位 = 1mil = 0.001 英寸 = 0.0254 mm
- 原理图:1 个单位 = 0.01inch = 10mil = 0.254 mm
如果使用了错误的单位,元件与预期位置的距离会相差 10 倍。
// WRONG: thinking unit is 1mil (it's actually 0.01inch = 10mil)
// Placing at x=500 thinking it's 500mil = 0.5inch — but 500 units = 5000mil = 5inch!
await eda.sch_PrimitiveComponent.create({libraryUuid: "...", uuid: "dev-uuid"}, 500, 0, "", 0, false, true, true);
// CORRECT: 50 units = 500mil = 0.5inch (in 0.01inch units)
await eda.sch_PrimitiveComponent.create({libraryUuid: "...", uuid: "dev-uuid"}, 50, 0, "", 0, false, true, true);
文档状态(至关重要)
创建项目后,必须先打开项目,然后才能对其中的文档进行操作。
操作文档时,务必确认:
- 项目已打开——使用
eda.dmt_Project.getCurrentProjectInfo()进行确认 - 当前文档正确——使用
eda.dmt_SelectControl.getCurrentDocumentInfo()检查文档类型 - 文档类型与 API 领域匹配——PCB API 需要当前文档为 PCB 文档,SCH API 需要当前文档为原理图文档
// WRONG: Assuming operation will work without checking document state
await eda.pcb_PrimitiveLine.create(...); // May fail if no PCB document is open
// CORRECT: Always verify document state first
const project = await eda.dmt_Project.getCurrentProjectInfo();
if (!project) {
// If you just created a project, you MUST open it before any operations!
return "Error: No project is currently opened. If you just created a project using dmt_Project.createProject(), you MUST call dmt_Project.openProject(projectPath) to open it first. You cannot operate on documents until a project is opened.";
}
const doc = await eda.dmt_SelectControl.getCurrentDocumentInfo();
if (doc?.documentType !== EDMT_EditorDocumentType.PCB) {
return "Error: No PCB document is currently active. Please open a PCB document first.";
}
// Now safe to perform PCB operations
await eda.pcb_PrimitiveLine.create(...);
**对错误的文档类型进行操作会返回错误或 null 结果。**例如:
- 当前没有 PCB 文档(
documentType !== EDMT_EditorDocumentType.PCB)时执行PCB_*API → 错误/null - 当前没有原理图文档(
documentType !== EDMT_EditorDocumentType.SCHEMATIC_PAGE)时执行SCH_*API → 错误/null
IFrame 上下文 — 始终直接使用 eda(至关重要)
编写通过 sys_IFrame.openIFrame() 创建 iframe 的扩展代码时,iframe HTML 中的 JavaScript 代码可以直接访问全局对象 eda。不要使用 window.parent.eda。
原因:
EasyEDA 会自动将 eda 对象注入 iframe 的执行上下文。标准的浏览器跨框架访问方式(window.parent、parentWindow 等)在此处并不适用,会导致失败。
// WRONG: Using window.parent to reach parent frame's eda object
// This is a common mistake made by AI agents familiar with web iframe patterns
const project = await window.parent.eda.dmt_Project.getCurrentProjectInfo();
// CORRECT: eda is available directly in the iframe context
const project = await eda.dmt_Project.getCurrentProjectInfo();
此规则适用于 sys_IFrame iframe 中的所有代码:
- 通过
openIFrame()加载的扩展 UI 面板 - 扩展包中在 iframe 内显示的任何 HTML 文件
**规则:**在 EasyEDA 扩展中,无论代码运行在主上下文中,还是运行在由 sys_IFrame 创建的 iframe 中,都始终通过 eda.xxx 访问 API——绝不要使用 window.parent.eda 或任何其他父框架访问方式。
多窗口支持
当多个 EasyEDA 窗口连接到桥接服务时,无需在每次 EDA 操作时检查窗口选择。仅在以下情况检查:
- 首次 EDA 操作 — 确认哪个窗口处于活动状态,并告知用户
- EDA 断开连接错误 — 如果桥接服务返回 EDA 已断开连接的错误,请让用户选择一个新窗口
多窗口操作:
# List all connected EDA windows
curl http://localhost:49620/eda-windows
# Select a specific window
curl -X POST http://localhost:49620/eda-windows/select \
-H "Content-Type: application/json" \
-d '{"windowId": "abc-123-def"}'
# Execute on specific window
curl -X POST http://localhost:49620/execute \
-H "Content-Type: application/json" \
-d '{"code": "return await eda.dmt_Project.getCurrentProjectInfo();", "windowId": "abc-123-def"}'
如果只连接了一个 EDA 窗口,则会自动将其选为活动窗口。
调试技巧与故障处理策略
故障排查
- 始终先检查运行状况:扫描 49620-49629 端口,查找
{"service":"easyeda-bridge"} - EDA 未连接?:确保 EasyEDA 中已加载桥接扩展。下载地址:https://jlc-ext.com/item/oshwhub/run-api-gateway
- 超时错误:默认超时时间为 30s。如果发生超时:
- 检查是否已打开正确的项目和文档(使用
dmt_Project.getCurrentProjectInfo()) - 如果未打开项目,使用
dmt_Project.openProject(projectPath)打开一个项目 - 如果没有活动文档,使用
dmt_EditorControl.openDocument(docId)打开正确的文档类型(PCB/原理图) - 如果文档类型不正确(例如,在原理图上运行 PCB API),请先切换到正确的文档
- 复杂操作可能需要拆分代码
- 检查是否已打开正确的项目和文档(使用
- 检查返回值:许多方法失败时会返回
null,务必进行验证 - 图层编号:使用
references/enums/文档中的枚举(例如,EPCB_LayerId) - EDA 窗口已断开连接:如果收到窗口已断开连接的错误,使用
GET /eda-windows检查可用窗口,并使用POST /eda-windows/select切换到另一个窗口 - 权限错误:所有 API 接口均由 EDA 的权限系统控制。如果某个特定 API 持续执行失败(返回错误或 null),而其他 API 工作正常,并且你已确认调用与文档完全一致,则问题可能是权限受限,而非代码错误。EDA 客户端可能会根据用户许可证、项目设置或文档状态限制某些操作。请告知用户,该操作可能需要更高权限或不同的 EDA 版本。
- 错误持续存在?:如果你已确认 API 调用与文档完全一致、排除了权限问题,但仍遇到意外错误,请考虑通过 EasyEDA 官方支持渠道报告问题
故障处理规则
开发扩展时,请遵循以下规则:
| 情况 | 操作 |
|---|---|
| 文档中不存在该 API 方法 | 立即停止 — 告知用户该 API 不存在 |
| 阅读文档后仍无法确定签名 | 停止生成 — 返回查询步骤并重新阅读 |
| 检测到被禁止的浏览器 API | 自动替换为 eda.sys_* 替代方案 |
| 菜单 ID 冲突 | 添加前缀以区分(例如,my-plugin-home、my-plugin-sch) |
| 权限受限 | 告知用户 — 可能需要不同的 EDA 版本或许可证 |
重要: 切勿猜测 API 签名。如果 references/classes/ 中没有相关文档,则该 API 不适用于你的用例。
AI Agent 工作流
当用户要求你执行 EDA 操作时:
- 了解任务 — 属于哪个领域?(PCB/SCH/LIB/Project)
- 验证文档状态 — 检查
eda.dmt_Project.getCurrentProjectInfo()和eda.dmt_SelectControl.getCurrentDocumentInfo() - 确认正确的文档类型 — PCB 操作需要激活的 PCB,SCH 操作需要激活的原理图
- 查阅 API — 阅读
references/中相关类的文档 - 检查类型 — 阅读枚举/接口文档,了解参数类型
- 编写代码 — 遵循上述执行上下文规则
- 执行 — 通过
POST /execute发送并检查结果 - 迭代 — 如果发生错误,阅读错误消息并进行调整
不确定某个 API 时:
- 在
_quick-reference.md中搜索方法名称 - 阅读特定类的文档,了解详细签名和说明
- 查看接口文档,了解复杂参数类型
会话管理
检测主题变更
Bridge 服务在运行时会占用系统资源。如果用户连续 3 次 转向完全不同的主题(与 EasyEDA/EDA/PCB/原理图无关),请主动询问是否要关闭 Bridge:
"我注意到我们已经转向 EasyEDA 以外的话题。Bridge 服务仍在后台运行。你想停止它以释放资源吗?"
不要自动关闭 Bridge — 用户之后可能会切回 EDA 工作。只需询问,并在用户确认后关闭。
关闭 Bridge
如果用户确认要停止 Bridge:
# Find and stop the bridge process
for port in $(seq 49620 49629); do
pid=$(lsof -ti :$port 2>/dev/null)
if [ -n "$pid" ]; then
kill $pid
echo "Bridge stopped (PID: $pid, Port: $port)"
break
fi
done