@windypro-rourou/dsh-eda 0.1.1 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windypro-rourou/dsh-eda",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "dsh-lichuang-eda (JCL-EDA): 嘉立创 EDA 助手 DSH 插件 —— 官方 eda.* API 云端实时生成(原理图/PCB)+ 离线标准版 JSON 兜底 + 面板记录式时间线(撤回/清空/紧急保存)。开箱即用:npm i 后 cordis.patch.yml insert 一行即挂载。",
6
6
  "packageManager": "pnpm@11.22.0",
@@ -43,14 +43,5 @@
43
43
  "repository": {
44
44
  "type": "git",
45
45
  "url": "https://github.com/WindyPro-rourou/JCL-EDA.git"
46
- },
47
- "scripts": {
48
- "gen": "node src/json-gen.js",
49
- "test:json": "node --test --experimental-test-isolation=none src/json-gen.test.js",
50
- "test:plugin": "node selfcheck.mjs && node --experimental-test-isolation=none --test \"test/*.test.mjs\"",
51
- "test:all": "npm run test:json && npm run test:plugin",
52
- "check": "node --check lib/index.js && node --check lib/client.js && node --check lib/backend.js",
53
- "publish:dry": "node scripts/publish.mjs --dry",
54
- "publish": "node scripts/publish.mjs"
55
46
  }
56
47
  }
package/skill/INDEX.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  本目录为官方 easyeda-api-skill 文档的 vendored 拷贝(含 SKILL.md 总纲、官方 README、format/ 文档格式、guide/ 教程、references/ 参考文档)。agent 通过 `eda_skill_read` 查阅:`doc` 参数填本文件内的相对路径,如 `references/classes/SCH_PrimitiveComponent.md`。
4
4
 
5
+ **线上权威源(与本站文档对应,可 web 交叉核对)**:<https://prodocs.easyeda.com/cn/api/reference/pro-api.html>(官方「扩展 API 参考」,类/枚举与 vendored 同代;若线上有更新版本差异,以 prodocs 为准并反馈。
6
+
5
7
  ## 入口
6
8
 
7
9
  | 文件 | 内容 |
package/src/json-gen.js CHANGED
@@ -274,10 +274,207 @@ export function buildLedLib(center, value, designator, opts = {}) {
274
274
  return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
275
275
  }
276
276
 
277
+ /**
278
+ * 电容符号(两脚,水平):两平行极板(竖直短线)+ 引脚引线,引脚间距 100(±50)。
279
+ * 位号在上、值在下。spice 前缀 C。
280
+ */
281
+ export function buildCapacitorLib(center, value, designator, opts = {}) {
282
+ const cx = Number(center.x)
283
+ const cy = Number(center.y)
284
+ const pinLength = 20
285
+ const pin1x = cx + 50 // 右侧脚(旋转 0,连接点朝外)
286
+ const pin2x = cx - 50 // 左侧脚(旋转 180)
287
+ // 两平行极板,板间距 12
288
+ const plateL = polylineShape([[cx - 6, cy - 16], [cx - 6, cy + 16]], { color: '#000000' })
289
+ const plateR = polylineShape([[cx + 6, cy - 16], [cx + 6, cy + 16]], { color: '#000000' })
290
+ // 极板 → 引脚引线(图内可见,连接极板与引脚线)
291
+ const leadL = polylineShape([[cx - 30, cy], [cx - 6, cy]], { color: '#000000' })
292
+ const leadR = polylineShape([[cx + 30, cy], [cx + 6, cy]], { color: '#000000' })
293
+ const p1 = pinShape({ x: pin1x, y: cy, rotation: 0, number: 1, name: '1', id: nextId(), pinLength, color: '#880000' })
294
+ const p2 = pinShape({ x: pin2x, y: cy, rotation: 180, number: 2, name: '2', id: nextId(), pinLength, color: '#880000' })
295
+ const tValue = textShape('N', cx, cy + 26, value || '1u')
296
+ const tRef = textShape('P', cx, cy - 26, designator)
297
+ const cPara =
298
+ `package\`NONE\`nameAlias\`Value\`Value\`${sanitize(value || '1u')}\`spicePre\`C\`spiceSymbolName\`capacitor\``
299
+ const children = [tRef, tValue, plateL, plateR, leadL, leadR, p1, p2]
300
+ return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
301
+ }
302
+
303
+ /**
304
+ * 二极管符号(两脚,水平,阳极朝右):三角形 + 阴极竖线,两脚间距 80(±40,同 LED)。
305
+ * 位号在上、值在下。spice 前缀 D(防反接/续流常用)。
306
+ */
307
+ export function buildDiodeLib(center, value, designator, opts = {}) {
308
+ const cx = Number(center.x)
309
+ const cy = Number(center.y)
310
+ const pinLength = 20
311
+ const anodeX = cx + 40 // 阳极(右)
312
+ const cathodeX = cx - 40 // 阴极(左)
313
+ const tri = polylineShape(
314
+ [
315
+ [cx - 20, cy - 10],
316
+ [cx + 20, cy],
317
+ [cx - 20, cy + 10],
318
+ [cx - 20, cy - 10],
319
+ ],
320
+ { color: '#A00000' },
321
+ )
322
+ const bar = polylineShape(
323
+ [
324
+ [cx - 25, cy - 8],
325
+ [cx - 25, cy + 8],
326
+ ],
327
+ { color: '#A00000' },
328
+ )
329
+ const p1 = pinShape({ x: anodeX, y: cy, rotation: 0, number: 1, name: 'A', id: nextId(), pinLength, color: '#880000' })
330
+ const p2 = pinShape({ x: cathodeX, y: cy, rotation: 180, number: 2, name: 'K', id: nextId(), pinLength, color: '#880000' })
331
+ const tValue = textShape('N', cx, cy + 26, value || '1N4007')
332
+ const tRef = textShape('P', cx, cy - 26, designator)
333
+ const cPara =
334
+ `package\`NONE\`nameAlias\`Value\`Value\`${sanitize(value || '1N4007')}\`spicePre\`D\`spiceSymbolName\`diode\``
335
+ const children = [tRef, tValue, tri, bar, p1, p2]
336
+ return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
337
+ }
338
+
339
+ /**
340
+ * 开关符号(SPST,两脚,水平):左右触点 + 斜向上扳杆(表示常开),引脚间距 100(±50)。
341
+ * 位号在上、值在下。spice 前缀 S。
342
+ */
343
+ export function buildSwitchLib(center, value, designator, opts = {}) {
344
+ const cx = Number(center.x)
345
+ const cy = Number(center.y)
346
+ const pinLength = 20
347
+ const pin1x = cx + 50 // 右侧脚(旋转 0)
348
+ const pin2x = cx - 50 // 左侧脚(旋转 180)
349
+ const contactL = polylineShape([[cx - 20, cy - 5], [cx - 20, cy + 5]], { color: '#000000' })
350
+ const contactR = polylineShape([[cx + 20, cy - 5], [cx + 20, cy + 5]], { color: '#000000' })
351
+ const lever = polylineShape([[cx - 20, cy], [cx + 16, cy - 22]], { color: '#000000' })
352
+ const leadL = polylineShape([[cx - 50, cy], [cx - 20, cy]], { color: '#000000' })
353
+ const leadR = polylineShape([[cx + 20, cy], [cx + 50, cy]], { color: '#000000' })
354
+ const p1 = pinShape({ x: pin1x, y: cy, rotation: 0, number: 1, name: '1', id: nextId(), pinLength, color: '#880000' })
355
+ const p2 = pinShape({ x: pin2x, y: cy, rotation: 180, number: 2, name: '2', id: nextId(), pinLength, color: '#880000' })
356
+ const tValue = textShape('N', cx, cy + 26, value || 'SW')
357
+ const tRef = textShape('P', cx, cy - 26, designator)
358
+ const cPara =
359
+ `package\`NONE\`nameAlias\`Value\`Value\`${sanitize(value || 'SW')}\`spicePre\`S\`spiceSymbolName\`switch\``
360
+ const children = [tRef, tValue, contactL, contactR, lever, leadL, leadR, p1, p2]
361
+ return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
362
+ }
363
+
364
+ /**
365
+ * 电感符号(两脚,水平):4 段半圆弧凸起(用 3 点折线近似),两脚间距 100(±50)。
366
+ * 位号在上、值在下。spice 前缀 L。
367
+ */
368
+ export function buildInductorLib(center, value, designator, opts = {}) {
369
+ const cx = Number(center.x)
370
+ const cy = Number(center.y)
371
+ const pinLength = 20
372
+ const pin1x = cx + 50 // 右侧脚(旋转 0)
373
+ const pin2x = cx - 50 // 左侧脚(旋转 180)
374
+ // 4 段凸弧(各宽 20、凸高 14),对称分布在 cx-40..cx+40
375
+ const bumps = [
376
+ [[cx - 40, cy], [cx - 30, cy - 14], [cx - 20, cy]],
377
+ [[cx - 20, cy], [cx - 10, cy - 14], [cx, cy]],
378
+ [[cx, cy], [cx + 10, cy - 14], [cx + 20, cy]],
379
+ [[cx + 20, cy], [cx + 30, cy - 14], [cx + 40, cy]],
380
+ ]
381
+ const body = bumps.map((b) => polylineShape(b, { color: '#A00000' }))
382
+ const leadL = polylineShape([[cx - 40, cy], [cx - 30, cy]], { color: '#000000' })
383
+ const leadR = polylineShape([[cx + 30, cy], [cx + 40, cy]], { color: '#000000' })
384
+ const p1 = pinShape({ x: pin1x, y: cy, rotation: 0, number: 1, name: '1', id: nextId(), pinLength, color: '#880000' })
385
+ const p2 = pinShape({ x: pin2x, y: cy, rotation: 180, number: 2, name: '2', id: nextId(), pinLength, color: '#880000' })
386
+ const tValue = textShape('N', cx, cy + 26, value || '10u')
387
+ const tRef = textShape('P', cx, cy - 26, designator)
388
+ const cPara =
389
+ `package\`NONE\`nameAlias\`Value\`Value\`${sanitize(value || '10u')}\`spicePre\`L\`spiceSymbolName\`inductor\``
390
+ const children = [tRef, tValue, ...body, leadL, leadR, p1, p2]
391
+ return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
392
+ }
393
+
394
+ /**
395
+ * 晶振符号(两脚,水平):中间矩形 + 两侧竖板,两脚间距 100(±50)。
396
+ * 位号在上、值在下。spice 前缀 X(晶振惯例 XTAL/Y,此处统一记作 X)。
397
+ */
398
+ export function buildCrystalLib(center, value, designator, opts = {}) {
399
+ const cx = Number(center.x)
400
+ const cy = Number(center.y)
401
+ const pinLength = 20
402
+ const pin1x = cx + 50 // 右侧脚(旋转 0)
403
+ const pin2x = cx - 50 // 左侧脚(旋转 180)
404
+ const body = rectShape(cx - 12, cy - 10, 24, 20, { color: '#000000' })
405
+ const plateL = polylineShape([[cx - 20, cy - 10], [cx - 20, cy + 10]], { color: '#000000' })
406
+ const plateR = polylineShape([[cx + 20, cy - 10], [cx + 20, cy + 10]], { color: '#000000' })
407
+ const leadL = polylineShape([[cx - 30, cy], [cx - 20, cy]], { color: '#000000' })
408
+ const leadR = polylineShape([[cx + 20, cy], [cx + 30, cy]], { color: '#000000' })
409
+ const p1 = pinShape({ x: pin1x, y: cy, rotation: 0, number: 1, name: '1', id: nextId(), pinLength, color: '#880000' })
410
+ const p2 = pinShape({ x: pin2x, y: cy, rotation: 180, number: 2, name: '2', id: nextId(), pinLength, color: '#880000' })
411
+ const tValue = textShape('N', cx, cy + 26, value || '16MHz')
412
+ const tRef = textShape('P', cx, cy - 26, designator)
413
+ const cPara =
414
+ `package\`NONE\`nameAlias\`Value\`Value\`${sanitize(value || '16MHz')}\`spicePre\`X\`spiceSymbolName\`crystal\``
415
+ const children = [tRef, tValue, body, plateL, plateR, leadL, leadR, p1, p2]
416
+ return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
417
+ }
418
+
419
+ /**
420
+ * 电池符号(两脚,水平):长短板交替(长板长、短板短),两脚间距 100(±50)。
421
+ * 位号在上、值在下。spice 前缀 BT。
422
+ */
423
+ export function buildBatteryLib(center, value, designator, opts = {}) {
424
+ const cx = Number(center.x)
425
+ const cy = Number(center.y)
426
+ const pinLength = 20
427
+ const pin1x = cx + 50 // 右侧脚(旋转 0,负极侧)
428
+ const pin2x = cx - 50 // 左侧脚(旋转 180,正极侧)
429
+ const longPlate = polylineShape([[cx - 4, cy - 18], [cx - 4, cy + 18]], { color: '#000000' })
430
+ const shortPlate = polylineShape([[cx + 4, cy - 8], [cx + 4, cy + 8]], { color: '#000000' })
431
+ const leadL = polylineShape([[cx - 30, cy], [cx - 4, cy]], { color: '#000000' })
432
+ const leadR = polylineShape([[cx + 4, cy], [cx + 30, cy]], { color: '#000000' })
433
+ const p1 = pinShape({ x: pin1x, y: cy, rotation: 0, number: 1, name: '1', id: nextId(), pinLength, color: '#880000' })
434
+ const p2 = pinShape({ x: pin2x, y: cy, rotation: 180, number: 2, name: '2', id: nextId(), pinLength, color: '#880000' })
435
+ const tValue = textShape('N', cx, cy + 26, value || '3.7V')
436
+ const tRef = textShape('P', cx, cy - 26, designator)
437
+ const cPara =
438
+ `package\`NONE\`nameAlias\`Value\`Value\`${sanitize(value || '3.7V')}\`spicePre\`BT\`spiceSymbolName\`battery\``
439
+ const children = [tRef, tValue, longPlate, shortPlate, leadL, leadR, p1, p2]
440
+ return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
441
+ }
442
+
443
+ /**
444
+ * 保险丝符号(两脚,水平):矩形 + 过中线,两脚间距 100(±50)。
445
+ * 位号在上、值在下。spice 前缀 F。
446
+ */
447
+ export function buildFuseLib(center, value, designator, opts = {}) {
448
+ const cx = Number(center.x)
449
+ const cy = Number(center.y)
450
+ const pinLength = 20
451
+ const pin1x = cx + 50 // 右侧脚(旋转 0)
452
+ const pin2x = cx - 50 // 左侧脚(旋转 180)
453
+ const body = rectShape(cx - 16, cy - 4, 32, 8, { color: '#000000' })
454
+ const midLine = polylineShape([[cx - 16, cy], [cx + 16, cy]], { color: '#000000' })
455
+ const leadL = polylineShape([[cx - 30, cy], [cx - 16, cy]], { color: '#000000' })
456
+ const leadR = polylineShape([[cx + 16, cy], [cx + 30, cy]], { color: '#000000' })
457
+ const p1 = pinShape({ x: pin1x, y: cy, rotation: 0, number: 1, name: '1', id: nextId(), pinLength, color: '#880000' })
458
+ const p2 = pinShape({ x: pin2x, y: cy, rotation: 180, number: 2, name: '2', id: nextId(), pinLength, color: '#880000' })
459
+ const tValue = textShape('N', cx, cy + 26, value || '1A')
460
+ const tRef = textShape('P', cx, cy - 26, designator)
461
+ const cPara =
462
+ `package\`NONE\`nameAlias\`Value\`Value\`${sanitize(value || '1A')}\`spicePre\`F\`spiceSymbolName\`fuse\``
463
+ const children = [tRef, tValue, body, midLine, leadL, leadR, p1, p2]
464
+ return `LIB~${num(cx)}~${num(cy)}~${cPara}~0~0~${nextId()}#@$` + children.join('#@$')
465
+ }
466
+
277
467
  /** 类型 → 符号构造器 */
278
468
  const SYMBOL_BUILDERS = {
279
469
  resistor: buildResistorLib,
280
470
  led: buildLedLib,
471
+ capacitor: buildCapacitorLib,
472
+ diode: buildDiodeLib,
473
+ switch: buildSwitchLib,
474
+ inductor: buildInductorLib,
475
+ crystal: buildCrystalLib,
476
+ battery: buildBatteryLib,
477
+ fuse: buildFuseLib,
281
478
  }
282
479
 
283
480
  // ----------------------------------------------------------------------------
@@ -4,8 +4,9 @@
4
4
  // 面向 0 基础用户:把「中文需求 / 模板 ID」翻译成 json-gen.js 能消费的
5
5
  // design 对象({ name, components, wires, nets })。
6
6
  //
7
- // v0 为规则版(模板 + 关键词匹配),生成器目前只支持 resistor / led,
8
- // 所以可生成的模板有限;其余能力如实返回 ok:false + 说明(不编造)。
7
+ // v0 为规则版(模板 + 关键词匹配),生成器支持 9 种两脚水平符号:
8
+ // resistor / led / capacitor / diode / switch / inductor / crystal / battery / fuse。
9
+ // 模板与关键词命中即返回 design;其余需求如实返回 ok:false + 说明(不编造)。
9
10
  // 后续接入 LLM 后可这里挂一个"语义翻译"钩子(见 TODO 注释)。
10
11
  // ============================================================================
11
12
 
@@ -58,6 +59,41 @@ export const TEMPLATE_CATALOG = [
58
59
  desc: '需要电平转换/USB 芯片符号,v0 暂不支持',
59
60
  supported: false,
60
61
  },
62
+ {
63
+ id: 'cap-bypass',
64
+ ico: '🔋',
65
+ name: '电源去耦电容',
66
+ desc: '5V → 电容(0.1u) → GND,为电源滤高频噪声',
67
+ supported: true,
68
+ },
69
+ {
70
+ id: 'diode-protect',
71
+ ico: '🛡️',
72
+ name: '二极管续流保护',
73
+ desc: '感性负载并联二极管(防反接/续流):R(10k) + D(1N4007) 于负载回路',
74
+ supported: true,
75
+ },
76
+ {
77
+ id: 'rc-delay',
78
+ ico: '⏱️',
79
+ name: 'RC 延时电路',
80
+ desc: '电阻+电容,5V 经 R 充电到 C',
81
+ supported: true,
82
+ },
83
+ {
84
+ id: 'battery-switch-led',
85
+ ico: '🔦',
86
+ name: '电池开关 LED',
87
+ desc: '电池+开关+限流电阻+LED 手电电路',
88
+ supported: true,
89
+ },
90
+ {
91
+ id: 'switch-demo',
92
+ ico: '🔘',
93
+ name: '开关符号演示',
94
+ desc: '5V → 开关 → GND,展示开关元件符号',
95
+ supported: true,
96
+ },
61
97
  ]
62
98
 
63
99
  /** 规则:生成 LED 点亮最小电路(R + LED 串联手拉手)。 */
@@ -94,10 +130,94 @@ function buildDividerDesign() {
94
130
  }
95
131
  }
96
132
 
133
+ /** 电容去耦:VCC(5V) → C → GND(单电容,命名网闭合)。 */
134
+ function buildCapDesign() {
135
+ return {
136
+ name: 'cap-bypass',
137
+ components: [{ ref: 'C1', type: 'capacitor', value: '0.1u', pos: { x: 400, y: 300 } }],
138
+ wires: [],
139
+ nets: [
140
+ { name: '5V', points: [[450, 300]] },
141
+ { name: 'GND', points: [[350, 300]] },
142
+ ],
143
+ }
144
+ }
145
+
146
+ /** 二极管续流保护:R(负载) 并联续流二极管 —— 5V 接 D 阴极,D 阳极 → R 左,R 右 → GND。 */
147
+ function buildDiodeProtectDesign() {
148
+ return {
149
+ name: 'diode-protect',
150
+ components: [
151
+ { ref: 'R1', type: 'resistor', value: '10k', pos: { x: 400, y: 300 } },
152
+ { ref: 'D1', type: 'diode', value: '1N4007', pos: { x: 700, y: 300 } },
153
+ ],
154
+ wires: [[450, 300, 740, 300]], // R1 右(450,300) → D1 阳极(740,300)
155
+ nets: [
156
+ { name: 'GND', points: [[350, 300]] }, // R1 左(350,300)
157
+ { name: '5V', points: [[660, 300]] }, // D1 阴极(660,300)
158
+ ],
159
+ }
160
+ }
161
+
162
+ /** RC 延时:5V → R → C → GND(R 右接 C 左,充电到 C)。 */
163
+ function buildRcDelayDesign() {
164
+ return {
165
+ name: 'rc-delay',
166
+ components: [
167
+ { ref: 'R1', type: 'resistor', value: '1k', pos: { x: 400, y: 300 } },
168
+ { ref: 'C1', type: 'capacitor', value: '10u', pos: { x: 700, y: 300 } },
169
+ ],
170
+ wires: [[450, 300, 650, 300]], // R1 右(450,300) → C1 左(650,300)
171
+ nets: [
172
+ { name: '5V', points: [[350, 300]] }, // R1 左(350,300)
173
+ { name: 'GND', points: [[750, 300]] }, // C1 右(750,300)
174
+ ],
175
+ }
176
+ }
177
+
178
+ /** 电池开关 LED 手电:BT(+) → SW → R → LED 阴极,LED 阳极 → BT(-) 同 GND。 */
179
+ function buildBatterySwitchLedDesign() {
180
+ return {
181
+ name: 'battery-switch-led',
182
+ components: [
183
+ { ref: 'BT1', type: 'battery', value: '3.7V', pos: { x: 300, y: 300 } },
184
+ { ref: 'SW1', type: 'switch', value: 'SW', pos: { x: 600, y: 300 } },
185
+ { ref: 'R1', type: 'resistor', value: '220', pos: { x: 900, y: 300 } },
186
+ { ref: 'LED1', type: 'led', value: 'red', pos: { x: 1200, y: 300 } },
187
+ ],
188
+ wires: [
189
+ [350, 300, 550, 300], // BT1 右(350) → SW1 左(550)
190
+ [650, 300, 850, 300], // SW1 右(650) → R1 左(850)
191
+ [950, 300, 1160, 300], // R1 右(950) → LED1 阴极(1160)
192
+ ],
193
+ nets: [
194
+ { name: 'GND', points: [[1240, 300], [250, 300]] }, // LED1 阳极(1240) + BT1 左(250)
195
+ ],
196
+ }
197
+ }
198
+
199
+ /** 开关演示:VCC → SW → GND(展示开关符号)。 */
200
+ function buildSwitchDesign() {
201
+ return {
202
+ name: 'switch-demo',
203
+ components: [{ ref: 'S1', type: 'switch', value: 'SW', pos: { x: 400, y: 300 } }],
204
+ wires: [],
205
+ nets: [
206
+ { name: '5V', points: [[450, 300]] },
207
+ { name: 'GND', points: [[350, 300]] },
208
+ ],
209
+ }
210
+ }
211
+
97
212
  const TEMPLATE_BUILDERS = {
98
213
  'led-blink': buildLedDesign,
99
214
  'voltage-divider': buildDividerDesign,
100
215
  buck: buildDividerDesign, // 近似:分压 + 说明
216
+ 'cap-bypass': buildCapDesign,
217
+ 'diode-protect': buildDiodeProtectDesign,
218
+ 'rc-delay': buildRcDelayDesign,
219
+ 'battery-switch-led': buildBatterySwitchLedDesign,
220
+ 'switch-demo': buildSwitchDesign,
101
221
  }
102
222
 
103
223
  /**
@@ -108,7 +228,7 @@ export function templateById(id) {
108
228
  const meta = TEMPLATE_CATALOG.find((t) => t.id === id)
109
229
  if (meta === undefined) return { ok: false, title: id, note: `未知模板:${id}` }
110
230
  if (meta.supported === false) {
111
- return { ok: false, title: meta.name, note: `模板「${meta.name}」需要其他元件符号(${meta.desc}),v0 生成器暂仅支持 电阻/ LED。` }
231
+ return { ok: false, title: meta.name, note: `模板「${meta.name}」需要其他元件符号(${meta.desc}),v0 生成器目前支持常见的两脚无源元件(电阻/电容/电感/二极管/LED/开关/晶振/电池/保险丝),尚不支持芯片等复杂符号。` }
112
232
  }
113
233
  const builder = TEMPLATE_BUILDERS[id]
114
234
  if (builder === undefined) return { ok: false, title: meta.name, note: `模板「${meta.name}」尚未实现规则。` }
@@ -126,6 +246,21 @@ function matchByKeywords(text) {
126
246
  if (/分压|降压|3\.3v|3v3|基准电压/.test(t)) {
127
247
  return templateById('buck')
128
248
  }
249
+ if (/电容|去耦|滤波|cap/.test(t)) {
250
+ return templateById('cap-bypass')
251
+ }
252
+ if (/电容|充电|延时/.test(t)) {
253
+ return templateById('rc-delay')
254
+ }
255
+ if (/二极管|续流|防反接|diode/.test(t)) {
256
+ return templateById('diode-protect')
257
+ }
258
+ if (/电池|手电|开关.*灯|开关.*led/.test(t)) {
259
+ return templateById('battery-switch-led')
260
+ }
261
+ if (/开关|switch/.test(t)) {
262
+ return templateById('switch-demo')
263
+ }
129
264
  return null
130
265
  }
131
266
 
@@ -143,8 +278,8 @@ export function translateRequest(text, opts = {}) {
143
278
  ok: false,
144
279
  title: '待支持的电路',
145
280
  note:
146
- 'v0 规则版只能识别「LED 点亮 / 电阻分压 / 降压」等简单需求。' +
147
- '请用这些词(例:一个 LED 点亮电路、电阻分压)或从模板卡选择;' +
281
+ 'v0 规则版可识别「LED 点亮 / 电阻分压 / 降压 / 电容去耦 / RC 延时 / 二极管续流(防反接) / 电池开关 LED / 开关」等需求。' +
282
+ '请用这些词(例:一个 LED 点亮电路、电阻分压、RC 延时电路、二极管续流保护、电池开关灯)或从模板卡选择;' +
148
283
  '完整自然语言理解将在接入 LLM 翻译层后支持。',
149
284
  }
150
285
  }
package/src/validate.js CHANGED
@@ -6,8 +6,11 @@
6
6
  // - deriveNetlist(design) : 从 design(元件引脚/导线/网络)推导网表
7
7
  // - checkConnectivity(design): 连通性检查(悬空引脚 / 孤立网络 / 未闭合)
8
8
  //
9
- // 说明:本次只支持 resistor / led 两种符号(与 SYMBOL_BUILDERS 一致),
10
- // 引脚几何与 buildResistorLib / buildLedLib 硬编码一致(旋转 0/180)。
9
+ // 说明:支持 9 种两脚水平符号(与 json-gen.js 的 SYMBOL_BUILDERS 一致):
10
+ // resistor / capacitor / switch / inductor / crystal / battery / fuse
11
+ // → 引脚间距 100(脚距 ±50,引脚1 在右 / 引脚2 在左);
12
+ // led / diode → 引脚间距 80(脚距 ±40,引脚1=阳极在右 / 引脚2=阴极在左)。
13
+ // 引脚几何与对应 buildXxxLib 硬编码一致(旋转 0/180)。
11
14
  // ============================================================================
12
15
 
13
16
  /** 坐标归一化为键(保留 2 位小数),用于去重/连通。 */
@@ -16,23 +19,35 @@ export function coordKey(x, y) {
16
19
  }
17
20
 
18
21
  /**
19
- * 元件引脚点(resistor: 右+50/左-50;led: 阳极+40/阴极-40;与生成器一致)。
22
+ * 元件引脚点(与生成器硬编码一致):
23
+ * led / diode:引脚1=阳极(右 +40),引脚2=阴极(左 -40),脚距 80;
24
+ * resistor / capacitor / switch / inductor / crystal / battery / fuse:引脚1(右 +50),引脚2(左 -50),脚距 100。
20
25
  * @returns {Array<{x,y,ref,pin}>}
21
26
  */
22
27
  export function pinPoints(comp) {
23
28
  const cx = Number(comp.pos?.x ?? NaN)
24
29
  const cy = Number(comp.pos?.y ?? NaN)
25
30
  if (Number.isNaN(cx) || Number.isNaN(cy)) return []
26
- if (comp.type === 'resistor') {
31
+ // 两脚间距 80(±40):LED / 二极管(阳极右=引脚1,阴极左=引脚2)
32
+ if (comp.type === 'led' || comp.type === 'diode') {
27
33
  return [
28
- { x: cx + 50, y: cy, ref: comp.ref, pin: 1 },
29
- { x: cx - 50, y: cy, ref: comp.ref, pin: 2 },
34
+ { x: cx + 40, y: cy, ref: comp.ref, pin: 1 },
35
+ { x: cx - 40, y: cy, ref: comp.ref, pin: 2 },
30
36
  ]
31
37
  }
32
- if (comp.type === 'led') {
38
+ // 两脚间距 100(±50):电阻/电容/开关/电感/晶振/电池/保险丝(引脚1 右,引脚2 左)
39
+ if (
40
+ comp.type === 'resistor' ||
41
+ comp.type === 'capacitor' ||
42
+ comp.type === 'switch' ||
43
+ comp.type === 'inductor' ||
44
+ comp.type === 'crystal' ||
45
+ comp.type === 'battery' ||
46
+ comp.type === 'fuse'
47
+ ) {
33
48
  return [
34
- { x: cx + 40, y: cy, ref: comp.ref, pin: 1 },
35
- { x: cx - 40, y: cy, ref: comp.ref, pin: 2 },
49
+ { x: cx + 50, y: cy, ref: comp.ref, pin: 1 },
50
+ { x: cx - 50, y: cy, ref: comp.ref, pin: 2 },
36
51
  ]
37
52
  }
38
53
  return []