@huaqiu/dsh-tool-kicad-schematic-gen 0.6.8 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/README.zh.md +11 -7
- package/lib/builder.d.ts +22 -1
- package/lib/builder.js +32 -5
- package/lib/client.js +38 -59
- package/lib/{flat--E2zWV_r.js → grid-check-BgWU7a8j.js} +109 -4
- package/lib/index.js +159 -80
- package/package.json +3 -3
- package/skills/kicad-schematic-design/SKILL.md +28 -7
- package/skills/kicad-schematic-design/assets/drawing-patterns/LICENSE.kicad-symbols.md +27 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/crystal-load.mjs +35 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/crystal-load.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/dcdc-boost.mjs +46 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/dcdc-boost.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/dcdc-buck.mjs +44 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/dcdc-buck.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/dcdc-geometry.mjs +23 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/decoupling-bank.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/generate-supplement.mjs +80 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/generate-systems.mjs +71 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/generate.mjs +10 -2
- package/skills/kicad-schematic-design/assets/drawing-patterns/geometry.mjs +1 -1
- package/skills/kicad-schematic-design/assets/drawing-patterns/i2c-pullups.mjs +23 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/i2c-pullups.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/ldo-fixed-on.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/ldo-three-pin.mjs +49 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/ldo-three-pin.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/led-power-indicator.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/mcu-external-crystal.mjs +6 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/mcu-external-crystal.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/mcu-minimum.mjs +90 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/mcu-minimum.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/mosfet-low-side.mjs +44 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/mosfet-low-side.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/pullup-button.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/reference-symbols.kicad_sym +3790 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/reference-symbols.md +34 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/render.mjs +7 -1
- package/skills/kicad-schematic-design/assets/drawing-patterns/reset-rc.mjs +27 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/reset-rc.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/swd-header.mjs +24 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/swd-header.png +0 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/usb-c-device.mjs +35 -0
- package/skills/kicad-schematic-design/assets/drawing-patterns/usb-c-device.png +0 -0
- package/skills/kicad-schematic-design/assets/runtime.mjs +3 -0
- package/skills/kicad-schematic-design/references/component-pcb.md +6 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/crystal-load.md +19 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/dcdc-boost.md +15 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/dcdc-buck.md +15 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/i2c-pullups.md +19 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/ldo-three-pin.md +19 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/mcu-external-crystal.md +11 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/mcu-minimum.md +17 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/mosfet-low-side.md +17 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/reset-enable.md +1 -1
- package/skills/kicad-schematic-design/references/drawing-patterns/reset-rc.md +19 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/swd-header.md +15 -0
- package/skills/kicad-schematic-design/references/drawing-patterns/usb-c-device.md +17 -0
- package/skills/kicad-schematic-design/references/drawing-patterns.md +21 -2
- package/skills/kicad-schematic-design/references/flat-reuse.md +2 -0
- package/skills/kicad-schematic-design/references/hierarchy.md +1 -0
- package/skills/kicad-schematic-design/references/layout-delivery.md +35 -0
- package/skills/kicad-schematic-design/references/module-layout.md +1 -1
- package/skills/kicad-schematic-design/references/power-symbols.md +1 -0
- package/skills/kicad-schematic-design/references/programming.md +7 -3
- package/skills/kicad-schematic-design/references/troubleshooting.md +22 -0
- package/skills/kicad-schematic-design/scripts/run-design.mjs +26 -6
- package/skills/kicad-schematic-design/templates/project.kicad_pro +1 -0
- package/src/builder.ts +2 -0
- package/src/client/index.ts +9 -25
- package/src/client/overview-result.ts +1 -0
- package/src/client/turn-overview.ts +37 -0
- package/src/components/identity.ts +3 -5
- package/src/components/import-component.ts +3 -5
- package/src/components/import-symbol.ts +2 -4
- package/src/components/models.ts +3 -15
- package/src/components/project-footprint.ts +4 -6
- package/src/prompt.ts +3 -0
- package/src/schematic/builder.ts +29 -3
- package/src/schematic/design-plan.ts +41 -0
- package/src/schematic/flat-input.ts +6 -4
- package/src/schematic/grid-check.ts +31 -0
- package/src/schematic/grid.ts +11 -0
- package/src/schematic/overview.ts +6 -15
- package/src/schematic/project.ts +1 -1
- package/src/schematic/tools.ts +63 -19
- package/src/skills.ts +17 -1
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
One installable plugin containing twelve tools and the kicad-schematic-design skill. Internal modules preserve the former component-library, module-catalog and schematic-tool contracts. Component search is provided by the KiCad built-in plugin @huaqiu/dsh-tool-part-search and its search_hqsch_parts tool.
|
|
6
6
|
|
|
7
|
+
布局交付:简单项目默认首版、一次集中修复、验证并交付;首版达标可直接交付。额外修复必须有具体阻塞缺陷,真实错误不受轮数限制。不为装饰偏好或清零保守警告重复重建。流程和验收条件见 [布局修复与交付停止条件](skills/kicad-schematic-design/references/layout-delivery.md),启动器摘要返回 nextAction 提示,不自动宣称方案或电气验证通过。
|
|
8
|
+
|
|
7
9
|
Configuration is grouped under components, modules.search, modules.download and schematic. Templates ship under skills/kicad-schematic-design; the public scripting API is available at ./builder. Module downloads resolve the current HQ Edge host at execution time, so a missing host does not prevent other tools or the skill from loading.
|
|
8
10
|
|
|
9
11
|
Imports remain workspace scoped and use DSH sandbox policy. Model inspection reports pin/pad summaries; paired import checks unique pin/pad number sets, but does not verify electrical suitability or package dimensions. Placement and routing are explicitly authored in design scripts. Datasheet extraction preserves missing/truncated status.
|
|
10
12
|
|
|
11
13
|
Build, typecheck and test from the repository workspace. Restart the KiCad assistant after replacing the old bundles. See [merge notes](../../docs/SCHEMATIC-MERGE.md).
|
|
12
14
|
|
|
13
|
-
Paired import: `import_kicad_component({manufacturer_id, mpn, projectDir})` downloads a symbol and footprint, registers `sym-lib-table` and `fp-lib-table`, and returns `footprintId`. Place with `addSymbol({libraryPath, libId, footprint: footprintId, ...})`. Success requires both usable models and matching unique pin/pad number sets. Public tool arguments use required `manufacturer_id` and `mpn`, matching `@huaqiu/dsh-tool-part-search`; imports also accept optional `projectDir` (defaults to the workspace root). `mfgId` is
|
|
15
|
+
Paired import: `import_kicad_component({manufacturer_id, mpn, projectDir})` downloads a symbol and footprint, registers `sym-lib-table` and `fp-lib-table`, and returns `footprintId`. Place with `addSymbol({libraryPath, libId, footprint: footprintId, ...})`. Success requires both usable models and matching unique pin/pad number sets. Public tool arguments use required `manufacturer_id` and `mpn`, matching `@huaqiu/dsh-tool-part-search`; imports also accept optional `projectDir` (defaults to the workspace root). The obsolete `mfgId` parameter is rejected; all callers must use `manufacturer_id`. See the skill's [component and PCB reference](skills/kicad-schematic-design/references/component-pcb.md).
|
|
14
16
|
|
|
15
17
|
Power symbols: declare actual shared nets with the builder's `requiredPowerSymbols` option. `powerLabelCatalog(nets)` returns current-build label UUIDs, coordinates and imported-module names for reproducible placement. `checkPowerSymbols()` and render/save reject missing symbols or unconverted labels for declared nets. Layout warnings still require visual review. See [power-symbol delivery](skills/kicad-schematic-design/references/power-symbols.md).
|
|
16
18
|
|
|
@@ -18,6 +20,14 @@ Power symbols: declare actual shared nets with the builder's `requiredPowerSymbo
|
|
|
18
20
|
|
|
19
21
|
Standalone common-only symbols with pins (e.g. SW-PTB-2_0_1) are automatically normalized in project copies to unit 1 without changing pins or graphics. Model summaries and import results report builderCompatibility.normalization=common_only_to_unit_1. Inherited, empty, malformed or alternate-only definitions are not normalized. Unsupported imports report part, symbol, sections and source URL.
|
|
20
22
|
|
|
21
|
-
最终概览卡片:正式预览传入 `delivery: true` 和包含全部本地库的 `projectDir
|
|
23
|
+
最终概览卡片:正式预览传入 `delivery: true` 和包含全部本地库的 `projectDir`,待本轮正常完成后,在最终回复后的轮次尾部显示页面切换、缩放、ZIP 下载;工具执行期间不展示正式卡片。正式概览必须是本轮最后一个工具操作,之后调用其他工具或普通预览时须重新正式交付;失败、取消、中断的轮次不显示正式卡片。卡片从所属轮次的工具记录恢复,切换会话无需全局缓存。复用已有 PNG,不重新生成电路,无重新生成/详情按钮。需 KiCad Harness 的 webServer、conversation.chat.turnTail 和 useChat(ChatSnapshot 的 locations/nodes)支持;不可用时仍返回本地预览路径。持久快照默认保存在当前用户家目录的 `.hq-edge/plugin-data/dsh-tool-kicad-schematic-gen/overview-cache/`,家目录通过 `os.homedir()` 动态获取;可用绝对路径 `HQ_EDGE_HOME` 覆盖 `.hq-edge` 根目录,不随 `DSH_HOME` 或插件版本变化。重启后可读取,手动删除快照后对应历史卡片下载将失效。工程、中间产物和预览保存在当前工作区;插件安装目录仅用于读取代码和模板,不读写概览快照。ZIP 为工程目录内相关设计文件与库的快照,不收集脚本、预览或外部库。
|
|
22
24
|
|
|
23
25
|
Module analysis queries use `analyze_kicad_module({schematicPath, view, reference?, pin?, net?, offset?, kicadCliPath?})`, with explicit `view`. Downloads already return a summary: query only needed components, grouped nets or grouped BOM. CLI/project options are resolved internally; optional absolute `kicadCliPath` is for recovery using the existing `get_eda_host_info` result. On `CLI_START_FAILED`, retry analysis with that path and `view:summary`, without downloading again. Successful paths are reused; default CLI command names preserve automatic discovery. Probe errors return `error:{code,executable}` and retain causes; capability failures are distinct from startup failures. Matching same-stem projects and downloaded `.source.kicad_pro` files are discovered. Detail output contains `status`, `items`, and optional `nextOffset`/`warnings`; missing `nextOffset` means completion. Component custom fields require a reference filter. Partial results are reused for detail queries; after fixing configuration use `view:summary` to retry. Full local artifacts are preserved. This replaces the previous analysis query arguments and response contract; existing callers must migrate. See [module analysis](skills/kicad-schematic-design/references/module-analysis.md).
|
|
26
|
+
|
|
27
|
+
方案先行:先保存硬件方案,用 `scripts/run-design.mjs --read-plan <方案.md>` 读回,再按文档编写脚本。运行时必须传 `--plan <path> --plan-sha256 <sha256>`;构建库直接调用也要求 `designPlan:{path,sha256}`。平铺 create 与层次生成工具同样检查方案。缺失、空文档或版本变化时阻止生成;只读模块分析和预览不受影响。构建报告保存方案全文、方案 hash 和脚本 hash。此检查保证文件和版本前置,不代替方案电气完整性及文图一致性审查。
|
|
28
|
+
|
|
29
|
+
电气栅格:默认 50 mil(1.27 mm)。构建器吸附器件放置、导线坐标、标签和模块平移量;`gridPoint/snapGrid` 可用于脚本规划。引脚库必须兼容同一格点,原理图生成与正式交付检查实际引脚及连接点,偏离时阻止交付。已有工程需从脚本重新构建,不通过独立舍入引脚/连线修补。
|
|
30
|
+
|
|
31
|
+
候选预览与交付状态:普通 `render_kicad_module_preview` 可传 `projectDir`,在首版预览时校验全部页的本地封装,无需先复制库到候选目录。`footprintCheck` 返回逐页的实际解析结果,缺库时顶层 `status:error`,已渲染图片仍保留。`renderStatus` 单独表示渲染结果;`deliveryStatus` 为 `not_requested`、`ready` 或 `blocked`。正式交付失败不会返回整体成功;只有 `deliveryStatus:ready` 和有效概览才代表正式快照已创建,这仍不代表电气验证通过。
|
|
32
|
+
|
|
33
|
+
技能加载时提供 `pluginDir`、`skillDir`、`runtimePath`、`runnerPath`、`geometryPath` 等只读资源绝对路径,避免混淆插件根目录与技能根目录。资源安装目录不用于保存项目输出。失败恢复、有限重试及候选检查顺序见技能的 `references/troubleshooting.md`。
|
package/README.zh.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
| 电路模块搜索下载 | eda_module_rag_search、eda_module_category_search、eda_module_precise_search、download_eda_module_schematic |
|
|
9
9
|
| 原理图工具 | get_kicad_module_ports、render_kicad_module_preview、create_kicad_hierarchy、edit_kicad_flat_schematic |
|
|
10
10
|
|
|
11
|
-
同时注册 kicad-schematic-design 技能。元件搜索由KiCad 内置的 @huaqiu/dsh-tool-part-search 提供,调用 search_hqsch_parts。该搜索插件随 KiCad 提供,可在插件管理器中启用;原理图插件本身不注册它的四个查询工具。两者间将搜索结果的 manufacturer.id 映射为 manufacturer_id,mpn 保持不变;公开工具统一要求 manufacturer_id 和 mpn,不再暴露 mfgId;两个导入工具额外支持可选 projectDir
|
|
11
|
+
同时注册 kicad-schematic-design 技能。元件搜索由KiCad 内置的 @huaqiu/dsh-tool-part-search 提供,调用 search_hqsch_parts。该搜索插件随 KiCad 提供,可在插件管理器中启用;原理图插件本身不注册它的四个查询工具。两者间将搜索结果的 manufacturer.id 映射为 manufacturer_id,mpn 保持不变;公开工具统一要求 manufacturer_id 和 mpn,不再暴露 mfgId;两个导入工具额外支持可选 projectDir,默认工作区根目录。内部身份解析同样拒绝旧参数 mfgId,所有调用统一使用 manufacturer_id。
|
|
12
12
|
|
|
13
13
|
配置分为 components、modules.search、modules.download、schematic,使用原配置的默认限额。flatTemplateDir 默认指向本包技能模板。Node 22.19+,KiCad 10 CLI 路径可通过 schematic.kicadCliPath 配置。
|
|
14
14
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
公开脚本入口是 lib/builder.js 或包的 ./builder 导出。设计技能、资源与模板位于 skills/kicad-schematic-design/。
|
|
18
18
|
|
|
19
|
-
新导入器件库默认保存到 `project/libraries/symbols/` 和 `project/libraries/footprints/`,库表保留在 project 根目录,使用 `${KIPRJMOD}`
|
|
19
|
+
新导入器件库默认保存到 `project/libraries/symbols/` 和 `project/libraries/footprints/`,库表保留在 project 根目录,使用 `${KIPRJMOD}` 相对路径注册。相同器件重复导入复用原文件;不再复用工程根目录中的旧库;同名库表仍指向旧目录时明确报冲突,不自动迁移或覆盖。脚本使用导入返回的实际 libraryPath 和 footprintId;概览 ZIP 会递归收集 libraries 下的器件库。
|
|
20
20
|
|
|
21
21
|
复用模块的封装通过 `localize_kicad_module_footprints` 的 list/import/check 流程本地化。模块下载现在保留 ZIP 中的 `.kicad_mod` 并返回 footprints 列表;缺失时使用明确选择的 manufacturer_id/mpn 查询在线封装。原嵌入符号、引脚和布线不替换,编号集合不匹配时拒绝导入。映射存入 `libraries/mappings/`,在设计脚本中使用 `importModule({...,footprintMappingPath})` 持久应用;来源变化后旧映射会拒绝使用。正式 delivery 在生成概览前验证根图和子页的本地封装引用。映射和封装一并进入项目 ZIP;尺寸、电气功能及 3D 完整性仍需单独确认。已有工程不会在插件加载时自动选择或替换器件。
|
|
22
22
|
|
|
@@ -24,11 +24,11 @@ KiCad CLI 自动从当前运行时安装位置或 PATH 查找,也可通过 KIC
|
|
|
24
24
|
|
|
25
25
|
路径发现由配置与 skill 共用缓存,环境变量变化后重新识别。模型已有 get_eda_host_info 返回结果时,可以把其中的 kicad-cli 绝对路径传给 render_kicad_module_preview 的可选 kicadCliPath;只影响当前调用,不修改默认配置。原理图文件生成和预览不要求 PCB IPC 诊断或 Python 路径查询。
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
正式概览快照默认保存到 `~/.hq-edge/plugin-data/dsh-tool-kicad-schematic-gen/overview-cache/`(`~` 为当前用户家目录,使用 `os.homedir()` 获取);设置绝对路径 `HQ_EDGE_HOME` 时保存到该根目录下同一子目录。不会使用 `DSH_HOME`、`LOCALAPPDATA` 或插件安装目录作为新快照的默认存储位置。快照完整写入后才发布;不再兼容读取插件目录中的旧快照,相关旧会话卡片需重新生成正式概览。不会自动删除历史快照;手动清理对应快照后,旧会话中的下载和放置操作将失效。
|
|
28
28
|
|
|
29
|
-
在工作区根目录执行 pnpm --filter @huaqiu/dsh-tool-kicad-schematic-gen build、typecheck 或 test。使用配套 DSH CLI 注册本目录,替换旧四个插件,并重新打开 KiCad AI
|
|
29
|
+
在工作区根目录执行 pnpm --filter @huaqiu/dsh-tool-kicad-schematic-gen build、typecheck 或 test。使用配套 DSH CLI 注册本目录,替换旧四个插件,并重新打开 KiCad AI 助手。旧脚本须更新为当前接口,不提供历史调用兼容。
|
|
30
30
|
|
|
31
|
-
成套器件导入:`import_kicad_component({manufacturer_id, mpn, projectDir})` 同时下载符号与封装,登记两个项目库表,返回 `footprintId`。放置时传 `footprint: imported.footprintId`。两种实际模型可用且引脚/焊盘编号集合匹配才成功;仍需手册核对尺寸与功能。公开调用只使用 `manufacturer_id
|
|
31
|
+
成套器件导入:`import_kicad_component({manufacturer_id, mpn, projectDir})` 同时下载符号与封装,登记两个项目库表,返回 `footprintId`。放置时传 `footprint: imported.footprintId`。两种实际模型可用且引脚/焊盘编号集合匹配才成功;仍需手册核对尺寸与功能。公开调用只使用 `manufacturer_id`,内部解析层同样拒绝旧 `mfgId` 参数。
|
|
32
32
|
|
|
33
33
|
电源符号:在构建选项中声明实际共享网络 `requiredPowerSymbols`。`powerLabelCatalog(nets)` 返回本次标签 UUID、网络、坐标和导入模块名,按这些条件定位后生成 placements,不引用上一轮 UUID。`checkPowerSymbols()` 与 render/save 检查声明网络的标准符号和残留标签;未完成时拒绝保存。遮挡提示仍需局部预览确认,详见 [电源符号交付](skills/kicad-schematic-design/references/power-symbols.md)。
|
|
34
34
|
|
|
@@ -36,14 +36,18 @@ KiCad CLI 自动从当前运行时安装位置或 PATH 查找,也可通过 KIC
|
|
|
36
36
|
|
|
37
37
|
含引脚的独立公共单元符号(如 SW-PTB-2_0_1)在项目副本中自动规范为单元 1,保留引脚与图形。模型摘要和导入结果通过 builderCompatibility.normalization=common_only_to_unit_1 报告;继承、空单元、异常命名及仅替代图形的符号不做自动规范。不支持的导入错误包含器件、符号、单元名称及源文件 URL。
|
|
38
38
|
|
|
39
|
-
最终概览卡片:正式预览传入 `delivery: true` 和包含全部本地库的 `projectDir`,显示页面切换、缩放、ZIP 下载。复用已有 PNG,不重新生成电路,无重新生成/详情按钮。需 KiCad Harness 的 webServer
|
|
39
|
+
最终概览卡片:正式预览传入 `delivery: true` 和包含全部本地库的 `projectDir`,显示页面切换、缩放、ZIP 下载。复用已有 PNG,不重新生成电路,无重新生成/详情按钮。需 KiCad Harness 的 webServer 与前端加载支持;不可用时仍返回本地预览路径。持久快照默认保存在当前用户家目录的 `.hq-edge/plugin-data/dsh-tool-kicad-schematic-gen/overview-cache/`,家目录通过 `os.homedir()` 动态获取;可用绝对路径 `HQ_EDGE_HOME` 覆盖 `.hq-edge` 根目录,不随 `DSH_HOME` 或插件版本变化。重启后可读取,手动删除快照后对应历史卡片下载将失效。工程、中间产物和预览保存在当前工作区;插件安装目录仅用于读取代码和模板,不读写概览快照。ZIP 为工程目录内相关设计文件与库的快照,不收集脚本、预览或外部库。
|
|
40
40
|
# 生成流程耗时优化
|
|
41
41
|
|
|
42
42
|
- 模块封装 `import` 返回 `stage: prepared`,仅表示本地库和映射准备完成;源文件默认不改写。`check_mapping` 验证来源应用映射后的状态,`check` 检查实际候选或最终原理图,避免混淆。
|
|
43
43
|
- 封装准备最多 4 并发;项目库表按项目串行写入。批次验证失败不写库,错误返回失败 assignment 索引;已验证下载在当前进程缓存 15 分钟(最多 128 项、16 MiB),退出进程后清空。瞬时网络/429/5xx 错误内部最多重试一次,格式和编号错误不自动重试。
|
|
44
|
-
-
|
|
44
|
+
- 使用现有供应商符号缓存对齐逻辑读取模块;封装仅支持当前 `(footprint ...)` 格式,旧式 `(module ...)` 明确拒绝,不自动转换。编号核对不等于尺寸或电气验证。
|
|
45
45
|
- builder 新增 `attachPinLabel`,支持显式方向、引线长度与通道错列;不推断引脚方向或自动避障。
|
|
46
46
|
- 技能目录 `scripts/run-design.mjs` 使用参数数组启动生成脚本,将退出码、执行时间和完整输出保存为 UTF-8 JSON;终端仅返回摘要,避免 PowerShell 转义、输出截断和 BOM 返工。模块封装准备提前进行,文档与脚本复用连接计划。
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
模块分析查询:`analyze_kicad_module({schematicPath, view, reference?, pin?, net?, offset?, kicadCliPath?})`。`view` 必填;下载已给分析摘要,直接查询所需 `components`、`nets` 或 `bom`。CLI/源工程默认内部解析;路径或启动失败 `CLI_START_FAILED` 时,复用 `get_eda_host_info` 返回的 kicad-cli 绝对路径,通过可选 `kicadCliPath` + `view:summary` 直接重试分析,不重新下载。成功路径后续复用,默认命令名不会覆盖自动发现;探测失败返回 `error:{code,executable}` 并区分启动、能力不支持、超时和命令失败。任意本地图采用同名 `.kicad_pro`,下载模块重启后可识别同名 `.source.kicad_pro`。详情返回 `status`、`items`、可选 `nextOffset`/`warnings`;网络按组分块,BOM 按全部物料字段与装配状态合并,器件自定义字段在指定 `reference` 时返回。部分失败结果供详情查询复用,修复后用 `view:summary` 重试。缺失 `nextOffset` 表示结束;不可用数据会明确警告。完整本地分析文件仍保留,详见 [模块分析](skills/kicad-schematic-design/references/module-analysis.md)。此接口替换旧版分析查询参数及返回结构,旧调用需同步更新。
|
|
50
|
+
|
|
51
|
+
候选预览与交付状态:普通 `render_kicad_module_preview` 可传 `projectDir`,在首版预览时校验全部页的本地封装,无需先复制库到候选目录。`footprintCheck` 返回逐页的实际解析结果,缺库时顶层 `status:error`,已渲染图片仍保留。`renderStatus` 单独表示渲染结果;`deliveryStatus` 为 `not_requested`、`ready` 或 `blocked`。正式交付失败不会返回整体成功;只有 `deliveryStatus:ready` 和有效概览才代表正式快照已创建,这仍不代表电气验证通过。
|
|
52
|
+
|
|
53
|
+
技能加载时提供 `pluginDir`、`skillDir`、`runtimePath`、`runnerPath`、`geometryPath` 等只读资源绝对路径,避免混淆插件根目录与技能根目录。资源安装目录不用于保存项目输出。失败恢复、有限重试及候选检查顺序见技能的 `references/troubleshooting.md`。
|
package/lib/builder.d.ts
CHANGED
|
@@ -180,6 +180,17 @@ interface PowerPlanningOptions extends Pick<PowerLabelSpec, 'libraryPath' | 'net
|
|
|
180
180
|
maxMoveMm?: number;
|
|
181
181
|
}
|
|
182
182
|
//#endregion
|
|
183
|
+
//#region src/schematic/design-plan.d.ts
|
|
184
|
+
interface DesignPlanReference {
|
|
185
|
+
path: string;
|
|
186
|
+
sha256: string;
|
|
187
|
+
}
|
|
188
|
+
interface DesignPlan extends DesignPlanReference {
|
|
189
|
+
text: string;
|
|
190
|
+
}
|
|
191
|
+
declare function readDesignPlan(path: string): DesignPlan;
|
|
192
|
+
declare function loadDesignPlan(input?: DesignPlanReference): DesignPlan;
|
|
193
|
+
//#endregion
|
|
183
194
|
//#region src/schematic/builder.d.ts
|
|
184
195
|
/** Explicit instance placement in millimetres; library files must be trusted project inputs. */
|
|
185
196
|
interface SymbolPlacement {
|
|
@@ -200,6 +211,8 @@ interface SymbolPlacement {
|
|
|
200
211
|
}
|
|
201
212
|
/** Explicit deployment templates and page choice; no implicit file discovery. */
|
|
202
213
|
interface SchematicOptions {
|
|
214
|
+
/** Saved plan version reviewed before generation; runner supplies this via environment. */
|
|
215
|
+
designPlan?: DesignPlanReference;
|
|
203
216
|
projectName: string;
|
|
204
217
|
templateDir: string;
|
|
205
218
|
paper: 'A4' | 'A3' | 'A2' | 'A1' | 'A0';
|
|
@@ -312,6 +325,7 @@ declare class Schematic {
|
|
|
312
325
|
private readonly objectModules;
|
|
313
326
|
private readonly requiredPowerSymbols;
|
|
314
327
|
private readonly templates;
|
|
328
|
+
private readonly designPlan;
|
|
315
329
|
/** Load validated templates. @param options Project identity, page and template directory. */
|
|
316
330
|
constructor(options: SchematicOptions);
|
|
317
331
|
/** Reserve a non-overlapping fixed module frame before placing its circuit.
|
|
@@ -419,6 +433,7 @@ declare class Schematic {
|
|
|
419
433
|
projectPath: string;
|
|
420
434
|
frames: ResolvedFlatFrame[];
|
|
421
435
|
layoutWarnings: PowerLayoutWarning[];
|
|
436
|
+
designPlan: DesignPlanReference;
|
|
422
437
|
};
|
|
423
438
|
}
|
|
424
439
|
//#endregion
|
|
@@ -483,4 +498,10 @@ declare function attachPinLabels(target: LabelTarget, spec: {
|
|
|
483
498
|
})[];
|
|
484
499
|
};
|
|
485
500
|
//#endregion
|
|
486
|
-
|
|
501
|
+
//#region src/schematic/grid.d.ts
|
|
502
|
+
/** Electrical schematic coordinates use KiCad's 50 mil connection grid. */
|
|
503
|
+
declare const SCHEMATIC_GRID_MM = 1.27;
|
|
504
|
+
declare function snapGrid(n: number): number;
|
|
505
|
+
declare function gridPoint(p: readonly number[]): [number, number];
|
|
506
|
+
//#endregion
|
|
507
|
+
export { ModuleRegion, ModuleRegionSpec, PowerLabelEntry, PowerLabelReplacement, PowerLabelSpec, PowerLayoutWarning, type PowerPlacementPlan, type PowerPlanningOptions, PowerSymbolStatus, SCHEMATIC_GRID_MM, Schematic, SchematicOptions, SymbolPlacement, attachPinLabel, attachPinLabels, gridPoint, loadDesignPlan, readDesignPlan, resolveKiCadPaths, snapGrid };
|
package/lib/builder.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as required, D as children, E as applyEdits, N as resolveKiCadPaths, O as parse, S as alignVendorSymbolIds, T as normalizeCommonUnits, a as snapGrid, c as loadDesignPlan, d as buildFlatCopy, f as listFlatObjects, h as flatTextSize, i as onGrid, j as value, k as quote, n as SCHEMATIC_GRID_MM, p as loadFlatTemplates, r as gridPoint, t as assertSchematicGrid, u as readDesignPlan, v as validatePreviewSchematic, y as assignFootprintText } from "./grid-check-BgWU7a8j.js";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -402,7 +402,7 @@ var ModuleRegion = class {
|
|
|
402
402
|
};
|
|
403
403
|
function point(at) {
|
|
404
404
|
if (at.length !== 2 || at.some((n) => !Number.isFinite(n))) throw new Error("coordinates require two finite millimetre values");
|
|
405
|
-
return at;
|
|
405
|
+
return gridPoint(at);
|
|
406
406
|
}
|
|
407
407
|
function definition(source, name, chain = []) {
|
|
408
408
|
const root = parse(source);
|
|
@@ -549,9 +549,19 @@ var Schematic = class Schematic {
|
|
|
549
549
|
objectModules = /* @__PURE__ */ new Map();
|
|
550
550
|
requiredPowerSymbols;
|
|
551
551
|
templates;
|
|
552
|
+
designPlan;
|
|
552
553
|
/** Load validated templates. @param options Project identity, page and template directory. */
|
|
553
554
|
constructor(options) {
|
|
554
555
|
this.options = options;
|
|
556
|
+
const { path, sha256 } = loadDesignPlan(options.designPlan);
|
|
557
|
+
if (options.designPlan && process.env.DSH_DESIGN_BRIEF_PATH) {
|
|
558
|
+
const runnerPlan = loadDesignPlan();
|
|
559
|
+
if (path !== runnerPlan.path || sha256 !== runnerPlan.sha256) throw new Error("DESIGN_PLAN_MISMATCH: script and runner must use the same saved plan version");
|
|
560
|
+
}
|
|
561
|
+
this.designPlan = {
|
|
562
|
+
path,
|
|
563
|
+
sha256
|
|
564
|
+
};
|
|
555
565
|
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(options.projectName)) throw new Error("projectName must be an ASCII filename without an extension");
|
|
556
566
|
if (![
|
|
557
567
|
"A4",
|
|
@@ -606,9 +616,14 @@ var Schematic = class Schematic {
|
|
|
606
616
|
* @returns Reference map, translated label anchors, net names and object UUIDs for frames.
|
|
607
617
|
*/
|
|
608
618
|
importModule(spec) {
|
|
619
|
+
spec = {
|
|
620
|
+
...spec,
|
|
621
|
+
offset: gridPoint(spec.offset)
|
|
622
|
+
};
|
|
609
623
|
if (this.importedModules.has(spec.name)) throw new Error(`duplicate imported module: ${spec.name}`);
|
|
610
624
|
const used = new Set([...this.references.keys()].map((key) => key.slice(0, key.lastIndexOf(":"))));
|
|
611
625
|
const sourceText = readFileSync(spec.sourcePath, "utf8");
|
|
626
|
+
assertSchematicGrid(sourceText);
|
|
612
627
|
const prepared = prepareFlatImport(sourceText, spec, this.options.projectName, this.rootId, used);
|
|
613
628
|
const footprints = {};
|
|
614
629
|
if (spec.footprintMappingPath) {
|
|
@@ -656,6 +671,14 @@ var Schematic = class Schematic {
|
|
|
656
671
|
const symbolName = spec.libId.slice(separator + 1);
|
|
657
672
|
const raw = normalizeCommonUnits(definition(readFileSync(spec.libraryPath, "utf8"), symbolName)), form = parse(raw);
|
|
658
673
|
const sections = children(form, "symbol");
|
|
674
|
+
for (const section of sections) {
|
|
675
|
+
const suffix = /_(\d+)_(\d+)$/u.exec(value(section));
|
|
676
|
+
if (!suffix || ![0, unit].includes(Number(suffix[1])) || ![0, 1].includes(Number(suffix[2]))) continue;
|
|
677
|
+
for (const pin of children(section, "pin")) {
|
|
678
|
+
const at = children(pin, "at")[0];
|
|
679
|
+
if (!at || ![Number(value(at, 1)), Number(value(at, 2))].every(onGrid)) throw new Error(`SYMBOL_PIN_OFF_GRID: ${spec.libId} pin ${value(children(pin, "number")[0])} requires a 50 mil (1.27 mm) compatible library symbol. Do not round pin geometry independently of the circuit.`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
659
682
|
if (!sections.some((s) => {
|
|
660
683
|
const m = /_(\d+)_(\d+)$/u.exec(value(s));
|
|
661
684
|
return m && Number(m[1]) === unit && [0, 1].includes(Number(m[2]));
|
|
@@ -741,7 +764,8 @@ var Schematic = class Schematic {
|
|
|
741
764
|
this.endpoint(from),
|
|
742
765
|
...via.map(point),
|
|
743
766
|
this.endpoint(to)
|
|
744
|
-
];
|
|
767
|
+
].filter((p, i, all) => i === 0 || p[0] !== all[i - 1][0] || p[1] !== all[i - 1][1]);
|
|
768
|
+
if (points.length < 2) throw new Error("wire collapses on the 50 mil (1.27 mm) grid; increase the lead length");
|
|
745
769
|
const segments = [], ids = [];
|
|
746
770
|
for (let i = 1; i < points.length; i++) {
|
|
747
771
|
const a = points[i - 1], b = points[i];
|
|
@@ -1004,7 +1028,9 @@ var Schematic = class Schematic {
|
|
|
1004
1028
|
* @returns Schematic text, project JSON text, resolved frames and edit counts.
|
|
1005
1029
|
*/
|
|
1006
1030
|
render() {
|
|
1031
|
+
loadDesignPlan(this.designPlan);
|
|
1007
1032
|
this.checkPowerSymbols();
|
|
1033
|
+
assertSchematicGrid(this.source());
|
|
1008
1034
|
if (!this.instances.length) throw new Error("place at least one symbol before rendering");
|
|
1009
1035
|
return buildFlatCopy(this.source(), this.options.projectName, {
|
|
1010
1036
|
removeReferences: [],
|
|
@@ -1030,7 +1056,8 @@ var Schematic = class Schematic {
|
|
|
1030
1056
|
schematicPath,
|
|
1031
1057
|
projectPath,
|
|
1032
1058
|
frames: result.resolvedFrames,
|
|
1033
|
-
layoutWarnings: this.powerLayoutWarnings()
|
|
1059
|
+
layoutWarnings: this.powerLayoutWarnings(),
|
|
1060
|
+
designPlan: { ...this.designPlan }
|
|
1034
1061
|
};
|
|
1035
1062
|
}
|
|
1036
1063
|
};
|
|
@@ -1131,4 +1158,4 @@ function attachPinLabels(target, spec) {
|
|
|
1131
1158
|
};
|
|
1132
1159
|
}
|
|
1133
1160
|
//#endregion
|
|
1134
|
-
export { ModuleRegion, Schematic, attachPinLabel, attachPinLabels, resolveKiCadPaths };
|
|
1161
|
+
export { ModuleRegion, SCHEMATIC_GRID_MM, Schematic, attachPinLabel, attachPinLabels, gridPoint, loadDesignPlan, readDesignPlan, resolveKiCadPaths, snapGrid };
|
package/lib/client.js
CHANGED
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
message: String(o.overviewError),
|
|
20
20
|
formal: true
|
|
21
21
|
};
|
|
22
|
+
if (o.footprintCheck?.status === "blocked") return {
|
|
23
|
+
kind: "error",
|
|
24
|
+
message: String(o.footprintCheck.error || "工程本地封装检查未通过")
|
|
25
|
+
};
|
|
22
26
|
if (!v) return o.status === "error" ? {
|
|
23
27
|
kind: "error",
|
|
24
28
|
message: "未能生成预览"
|
|
@@ -47,6 +51,32 @@
|
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
//#endregion
|
|
54
|
+
//#region src/client/turn-overview.ts
|
|
55
|
+
const NO_OVERVIEW = { kind: "review" };
|
|
56
|
+
/** Read only this completed turn's persisted tool nodes; no global/session cache or DOM timing. */
|
|
57
|
+
function overviewForTurn(turn, chat) {
|
|
58
|
+
if (turn?.status !== "closed" || turn.end?.data.reason.kind !== "completed" || !chat) return NO_OVERVIEW;
|
|
59
|
+
const calls = [];
|
|
60
|
+
const visited = /* @__PURE__ */ new Set();
|
|
61
|
+
function walk(value, depth = 0) {
|
|
62
|
+
if (!value || typeof value !== "object" || visited.has(value) || depth > 64) return;
|
|
63
|
+
visited.add(value);
|
|
64
|
+
const block = value;
|
|
65
|
+
if (block.kind === "tool-result" && !block.subCalls?.length && typeof block.seq === "number") calls.push({
|
|
66
|
+
seq: block.seq,
|
|
67
|
+
block,
|
|
68
|
+
name: block.call?.name
|
|
69
|
+
});
|
|
70
|
+
if (Array.isArray(block.subCalls)) for (const child of block.subCalls) walk(child, depth + 1);
|
|
71
|
+
}
|
|
72
|
+
for (const key of chat.locations.getTurn(turn.turn)) {
|
|
73
|
+
const node = chat.nodes.get(key);
|
|
74
|
+
if (node?.kind === "tool-call") walk(node.data?.root);
|
|
75
|
+
}
|
|
76
|
+
const latest = calls.sort((a, b) => a.seq - b.seq).at(-1);
|
|
77
|
+
return latest?.name === "render_kicad_module_preview" ? parseCard(latest.block) : NO_OVERVIEW;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
50
80
|
//#region src/client/place.ts
|
|
51
81
|
function placementHost(get) {
|
|
52
82
|
try {
|
|
@@ -71,18 +101,6 @@
|
|
|
71
101
|
if (result?.ok === false) throw new Error(result.message ?? "编辑器拒绝放置。");
|
|
72
102
|
}
|
|
73
103
|
//#endregion
|
|
74
|
-
//#region src/client/latest-overview.ts
|
|
75
|
-
/** Choose the newest mounted snapshot for each session/project; never compare titles. */
|
|
76
|
-
function supersededOverviews(rows) {
|
|
77
|
-
const latest = /* @__PURE__ */ new Map();
|
|
78
|
-
rows.forEach((row, index) => {
|
|
79
|
-
if (!row.session || !row.project) return;
|
|
80
|
-
const key = JSON.stringify([row.session, row.project]), previous = latest.get(key);
|
|
81
|
-
if (previous === void 0 || row.createdAt >= rows[previous].createdAt) latest.set(key, index);
|
|
82
|
-
});
|
|
83
|
-
return rows.map((row, index) => !!row.session && !!row.project && latest.get(JSON.stringify([row.session, row.project])) !== index);
|
|
84
|
-
}
|
|
85
|
-
//#endregion
|
|
86
104
|
//#region ../dsh-sidebar-entry/lib/index.mjs
|
|
87
105
|
/** Find the sidebar shell root element, or undefined while not yet mounted. */
|
|
88
106
|
function sidebarRoot() {
|
|
@@ -262,11 +280,11 @@
|
|
|
262
280
|
* family row means adding its selector here (and to no plugin-local list).
|
|
263
281
|
*/
|
|
264
282
|
const HUAQIU_SIDEBAR_ENTRY_FAMILY = [
|
|
265
|
-
"[data-hq-plugin-update-entry]",
|
|
266
283
|
"[data-hqcg-footprint-entry]",
|
|
267
284
|
"[data-hqcg-symbol-entry]",
|
|
268
285
|
"[data-hqcg-schematic-entry]",
|
|
269
|
-
"[data-hq-dummy-entry]"
|
|
286
|
+
"[data-hq-dummy-entry]",
|
|
287
|
+
"[data-hq-plugin-update-entry]"
|
|
270
288
|
];
|
|
271
289
|
/**
|
|
272
290
|
* Read the host UI language from `<html lang>`.
|
|
@@ -1057,52 +1075,14 @@
|
|
|
1057
1075
|
}
|
|
1058
1076
|
//#endregion
|
|
1059
1077
|
//#region src/client/index.ts
|
|
1060
|
-
/** Harness compacts finished tool calls; keep only our delivery card visible. */
|
|
1061
|
-
function keepDeliveryVisible() {
|
|
1062
|
-
if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => {};
|
|
1063
|
-
let frame = 0, disposed = false;
|
|
1064
|
-
const reveal = () => {
|
|
1065
|
-
frame = 0;
|
|
1066
|
-
if (disposed) return;
|
|
1067
|
-
const cards = Array.from(document.querySelectorAll(".dsh-local-overview"));
|
|
1068
|
-
const hidden = supersededOverviews(cards.map((card) => ({
|
|
1069
|
-
session: card.dataset.overviewSession ?? "",
|
|
1070
|
-
project: card.dataset.overviewProject ?? "",
|
|
1071
|
-
createdAt: Number(card.dataset.overviewCreated ?? 0)
|
|
1072
|
-
})));
|
|
1073
|
-
for (const [index, card] of cards.entries()) {
|
|
1074
|
-
if (card.hidden !== hidden[index]) card.hidden = hidden[index];
|
|
1075
|
-
if (hidden[index]) continue;
|
|
1076
|
-
let parent = card.parentElement;
|
|
1077
|
-
while (parent) {
|
|
1078
|
-
if (parent.hasAttribute("data-turn-process-hidden")) parent.removeAttribute("hidden");
|
|
1079
|
-
parent = parent.parentElement;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
};
|
|
1083
|
-
const observer = new MutationObserver(() => {
|
|
1084
|
-
if (!frame) frame = requestAnimationFrame(reveal);
|
|
1085
|
-
});
|
|
1086
|
-
observer.observe(document.documentElement, {
|
|
1087
|
-
subtree: true,
|
|
1088
|
-
childList: true,
|
|
1089
|
-
attributes: true,
|
|
1090
|
-
attributeFilter: ["hidden", "data-turn-process-hidden"]
|
|
1091
|
-
});
|
|
1092
|
-
reveal();
|
|
1093
|
-
return () => {
|
|
1094
|
-
disposed = true;
|
|
1095
|
-
observer.disconnect();
|
|
1096
|
-
if (frame) cancelAnimationFrame(frame);
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
1078
|
window.__ModuleLoader__.load({
|
|
1100
1079
|
id: "@huaqiu/dsh-tool-kicad-schematic-gen",
|
|
1101
1080
|
factory: (require) => {
|
|
1102
1081
|
const { createElement: h, useState, useEffect } = require("react");
|
|
1103
1082
|
let getHost;
|
|
1104
1083
|
function Card(props) {
|
|
1105
|
-
const
|
|
1084
|
+
const chat = props.useChat?.((snapshot) => snapshot);
|
|
1085
|
+
const state = chat ? overviewForTurn(props.turn, chat) : NO_OVERVIEW, result = state.result;
|
|
1106
1086
|
const [page, setPage] = useState(0), [zoom, setZoom] = useState(1), [message, setMessage] = useState(""), [busy, setBusy] = useState(false);
|
|
1107
1087
|
useEffect(() => {
|
|
1108
1088
|
setPage(0);
|
|
@@ -1247,7 +1227,6 @@
|
|
|
1247
1227
|
inject: ["slots", "inputTriggers"],
|
|
1248
1228
|
apply: (ctx) => {
|
|
1249
1229
|
getHost = () => ctx.get?.("hqEdge");
|
|
1250
|
-
const stopVisibility = keepDeliveryVisible();
|
|
1251
1230
|
const source = registerSchematicGenSourceWhenReady(() => ctx.get?.("inputTriggers"));
|
|
1252
1231
|
try {
|
|
1253
1232
|
console.debug("[schematic-gen:client] apply", {
|
|
@@ -1283,12 +1262,12 @@
|
|
|
1283
1262
|
try {
|
|
1284
1263
|
console.debug("[schematic-gen:client] sidebar row", { rows: document.querySelectorAll("[data-hqcg-schematic-entry]").length });
|
|
1285
1264
|
} catch {}
|
|
1286
|
-
const dispose = ctx.slots.inject("
|
|
1287
|
-
name: "
|
|
1288
|
-
|
|
1265
|
+
const dispose = ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
|
|
1266
|
+
name: "conversation.chat.turnTail",
|
|
1267
|
+
id: "huaqiu-schematic-overview",
|
|
1268
|
+
order: 100
|
|
1289
1269
|
}, Card));
|
|
1290
1270
|
ctx.effect?.(() => () => {
|
|
1291
|
-
stopVisibility();
|
|
1292
1271
|
source.dispose();
|
|
1293
1272
|
stopEntry();
|
|
1294
1273
|
if (typeof dispose === "function") dispose();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { delimiter, dirname, join } from "node:path";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
//#region src/runtime-paths.ts
|
|
5
5
|
const isDefaultCommand = (value) => !value || /^kicad-cli(?:\.exe)?$/i.test(value.trim());
|
|
6
6
|
/** Explicit paths override discovery; default command names still discover the host and PATH. */
|
|
@@ -602,7 +602,8 @@ function buildProject(edited, projectName, sheetName, expectedPorts, drivenInput
|
|
|
602
602
|
schematic: {
|
|
603
603
|
legacy_lib_dir: "",
|
|
604
604
|
legacy_lib_list: [],
|
|
605
|
-
...imported.schematic
|
|
605
|
+
...imported.schematic,
|
|
606
|
+
connection_grid_size: 50
|
|
606
607
|
},
|
|
607
608
|
sheets: [],
|
|
608
609
|
text_variables: {},
|
|
@@ -1504,4 +1505,108 @@ function buildFlatCopy(source, projectName, plan, templates) {
|
|
|
1504
1505
|
};
|
|
1505
1506
|
}
|
|
1506
1507
|
//#endregion
|
|
1507
|
-
|
|
1508
|
+
//#region src/schematic/design-plan.ts
|
|
1509
|
+
/** Required, version-bound design document input for schematic generation. */
|
|
1510
|
+
const DESIGN_PLAN_MAX_BYTES = 1048576;
|
|
1511
|
+
function designPlanReference(input) {
|
|
1512
|
+
const plan = input;
|
|
1513
|
+
if (!plan || typeof plan.path !== "string" || !plan.path.trim() || typeof plan.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(plan.sha256)) throw new Error("DESIGN_PLAN_REQUIRED: save the hardware design document, read it with run-design.mjs --read-plan <path>, then supply designPlan:{path,sha256}. Do not generate first and write the document afterwards.");
|
|
1514
|
+
return {
|
|
1515
|
+
path: plan.path,
|
|
1516
|
+
sha256: plan.sha256
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
/** Structural check only; the model must still review engineering completeness. */
|
|
1520
|
+
function inspectDesignPlan(path, text) {
|
|
1521
|
+
if (Buffer.byteLength(text, "utf8") > 1048576 || text.trim().length < 200 || !/^#\s+\S/m.test(text) || !/^##\s+\S/m.test(text)) throw new Error("DESIGN_PLAN_INCOMPLETE: provide a saved Markdown hardware plan with a title, substantive sections, and at least 200 characters. Review requirements, parts, circuits, connections and budgets before generation.");
|
|
1522
|
+
return {
|
|
1523
|
+
path,
|
|
1524
|
+
sha256: createHash("sha256").update(text).digest("hex"),
|
|
1525
|
+
text
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function matchDesignPlan(reference, text) {
|
|
1529
|
+
const plan = inspectDesignPlan(reference.path, text);
|
|
1530
|
+
if (plan.sha256 !== reference.sha256) throw new Error("DESIGN_PLAN_CHANGED: the document changed after it was read. Read the latest document and update the design script/connection plan before rebuilding with its new sha256.");
|
|
1531
|
+
return plan;
|
|
1532
|
+
}
|
|
1533
|
+
function readDesignPlan(path) {
|
|
1534
|
+
try {
|
|
1535
|
+
const canonical = realpathSync(path), info = statSync(canonical);
|
|
1536
|
+
if (!info.isFile() || info.size > 1048576) throw new Error("expected a document file no larger than 1 MiB");
|
|
1537
|
+
return inspectDesignPlan(canonical, new TextDecoder("utf-8", { fatal: true }).decode(readFileSync(canonical)));
|
|
1538
|
+
} catch (error) {
|
|
1539
|
+
throw new Error(`DESIGN_PLAN_UNAVAILABLE: ${path}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function loadDesignPlan(input) {
|
|
1543
|
+
const reference = designPlanReference(input ?? {
|
|
1544
|
+
path: process.env.DSH_DESIGN_BRIEF_PATH,
|
|
1545
|
+
sha256: process.env.DSH_DESIGN_BRIEF_SHA256
|
|
1546
|
+
});
|
|
1547
|
+
const plan = readDesignPlan(reference.path);
|
|
1548
|
+
return matchDesignPlan({
|
|
1549
|
+
...reference,
|
|
1550
|
+
path: plan.path
|
|
1551
|
+
}, plan.text);
|
|
1552
|
+
}
|
|
1553
|
+
//#endregion
|
|
1554
|
+
//#region src/schematic/grid.ts
|
|
1555
|
+
/** Electrical schematic coordinates use KiCad's 50 mil connection grid. */
|
|
1556
|
+
const SCHEMATIC_GRID_MM = 1.27;
|
|
1557
|
+
function snapGrid(n) {
|
|
1558
|
+
if (!Number.isFinite(n)) throw new Error("GRID_COORDINATE_INVALID: expected finite millimetres");
|
|
1559
|
+
return Number((Math.round(n / SCHEMATIC_GRID_MM) * SCHEMATIC_GRID_MM).toFixed(6));
|
|
1560
|
+
}
|
|
1561
|
+
function gridPoint(p) {
|
|
1562
|
+
if (p.length !== 2) throw new Error("grid point requires two coordinates");
|
|
1563
|
+
return [snapGrid(p[0]), snapGrid(p[1])];
|
|
1564
|
+
}
|
|
1565
|
+
function onGrid(n) {
|
|
1566
|
+
return Number.isFinite(n) && Math.abs(n - snapGrid(n)) < 1e-6;
|
|
1567
|
+
}
|
|
1568
|
+
//#endregion
|
|
1569
|
+
//#region src/schematic/grid-check.ts
|
|
1570
|
+
/** Never round an existing circuit's wires and library pins independently: that can break nets. */
|
|
1571
|
+
function assertSchematicGrid(source) {
|
|
1572
|
+
const failures = [];
|
|
1573
|
+
const root = parse(source), sheets = children(root, "sheet");
|
|
1574
|
+
const check = (name, p) => {
|
|
1575
|
+
if (!p.every(onGrid) && failures.length < 16) failures.push(`${name} (${p.join(", ")})`);
|
|
1576
|
+
};
|
|
1577
|
+
const flat = applyEdits(source, sheets.map((sheet) => ({
|
|
1578
|
+
start: sheet.start,
|
|
1579
|
+
end: sheet.end,
|
|
1580
|
+
text: ""
|
|
1581
|
+
})));
|
|
1582
|
+
for (const object of listFlatObjects(flat)) if (object.type === "symbol") {
|
|
1583
|
+
if (object.pinError) throw new Error(`GRID_PIN_UNRESOLVED: ${object.reference}: ${object.pinError}`);
|
|
1584
|
+
if (object.at) check(object.reference ?? "symbol", object.at);
|
|
1585
|
+
for (const pin of object.pins ?? []) check(`${object.reference}.${pin.number}`, pin.at);
|
|
1586
|
+
} else if (["wire", "bus"].includes(object.type)) {
|
|
1587
|
+
if (object.from) check(`${object.type} start`, object.from);
|
|
1588
|
+
if (object.to) check(`${object.type} end`, object.to);
|
|
1589
|
+
} else if ([
|
|
1590
|
+
"label",
|
|
1591
|
+
"global_label",
|
|
1592
|
+
"hierarchical_label",
|
|
1593
|
+
"junction",
|
|
1594
|
+
"no_connect"
|
|
1595
|
+
].includes(object.type) && object.at) check(object.type, object.at);
|
|
1596
|
+
for (const sheet of sheets) {
|
|
1597
|
+
for (const at of children(sheet, "at")) check("sheet", [Number(value(at, 1)), Number(value(at, 2))]);
|
|
1598
|
+
for (const pin of children(sheet, "pin")) for (const at of children(pin, "at")) check(`sheet pin ${value(pin)}`, [Number(value(at, 1)), Number(value(at, 2))]);
|
|
1599
|
+
}
|
|
1600
|
+
for (const kind of ["wire", "bus"]) for (const wire of children(root, kind)) for (const pts of children(wire, "pts")) for (const xy of children(pts, "xy")) check(kind, [Number(value(xy, 1)), Number(value(xy, 2))]);
|
|
1601
|
+
for (const entry of children(root, "bus_entry")) {
|
|
1602
|
+
const at = children(entry, "at")[0], size = children(entry, "size")[0];
|
|
1603
|
+
if (at && size) {
|
|
1604
|
+
const x = Number(value(at, 1)), y = Number(value(at, 2));
|
|
1605
|
+
check("bus entry", [x, y]);
|
|
1606
|
+
check("bus entry end", [x + Number(value(size, 1)), y + Number(value(size, 2))]);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
if (failures.length) throw new Error(`SCHEMATIC_OFF_GRID: expected 50 mil (1.27 mm). ${failures.join("; ")}. Fix source placement/routing or select a 50 mil compatible symbol; do not round library pins and connected wires separately.`);
|
|
1610
|
+
}
|
|
1611
|
+
//#endregion
|
|
1612
|
+
export { required as A, buildProject as C, children as D, applyEdits as E, getKiCadPaths as M, resolveKiCadPaths as N, parse as O, alignVendorSymbolIds as S, normalizeCommonUnits as T, previewExportError as _, snapGrid as a, auditFootprints as b, loadDesignPlan as c, buildFlatCopy as d, listFlatObjects as f, PreviewError as g, flatTextSize as h, onGrid as i, value as j, quote as k, matchDesignPlan as l, summarizeFlatLayout as m, SCHEMATIC_GRID_MM as n, DESIGN_PLAN_MAX_BYTES as o, loadFlatTemplates as p, gridPoint as r, designPlanReference as s, assertSchematicGrid as t, readDesignPlan as u, validatePreviewSchematic as v, isCommonOnlyUnit as w, footprintRequirements as x, assignFootprintText as y };
|