@jlcpcb/mcp 0.2.0 → 0.3.1

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/dist/index.js CHANGED
@@ -1,32 +1,48 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
2
  var __create = Object.create;
4
3
  var __getProtoOf = Object.getPrototypeOf;
5
4
  var __defProp = Object.defineProperty;
6
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
8
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
9
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
10
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
22
  for (let key of __getOwnPropNames(mod))
12
23
  if (!__hasOwnProp.call(to, key))
13
24
  __defProp(to, key, {
14
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
15
26
  enumerable: true
16
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
17
30
  return to;
18
31
  };
19
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
20
37
  var __export = (target, all) => {
21
38
  for (var name in all)
22
39
  __defProp(target, name, {
23
40
  get: all[name],
24
41
  enumerable: true,
25
42
  configurable: true,
26
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
27
44
  });
28
45
  };
29
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
46
 
31
47
  // ../../node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js
32
48
  var require_code = __commonJS((exports) => {
@@ -6278,7 +6294,7 @@ var require_formats = __commonJS((exports) => {
6278
6294
  }
6279
6295
  var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
6280
6296
  function getTime(strictTimeZone) {
6281
- return function time(str) {
6297
+ return function time3(str) {
6282
6298
  const matches = TIME.exec(str);
6283
6299
  if (!matches)
6284
6300
  return false;
@@ -11947,6 +11963,9 @@ var require_dist = __commonJS((exports, module) => {
11947
11963
  exports.default = formatsPlugin;
11948
11964
  });
11949
11965
 
11966
+ // src/index.ts
11967
+ import { createRequire } from "module";
11968
+
11950
11969
  // ../../node_modules/zod/v3/external.js
11951
11970
  var exports_external = {};
11952
11971
  __export(exports_external, {
@@ -21890,31 +21909,38 @@ class StdioServerTransport {
21890
21909
  }
21891
21910
  // ../core/dist/index.js
21892
21911
  import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
21893
- import { existsSync } from "fs";
21894
- import { join as join2 } from "path";
21895
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
21896
21912
  import { existsSync as existsSync2 } from "fs";
21897
21913
  import { join as join3 } from "path";
21898
- import { homedir, platform } from "os";
21914
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
21915
+ import { existsSync as existsSync3 } from "fs";
21916
+ import { join as join4 } from "path";
21917
+ import { homedir as homedir2, platform as platform2 } from "os";
21899
21918
  import { readdir, readFile, writeFile, mkdir, access, stat } from "fs/promises";
21900
21919
  import { join, dirname, extname, basename } from "path";
21920
+ import { existsSync } from "fs";
21921
+ import { homedir, platform } from "os";
21922
+ import { join as join2 } from "path";
21901
21923
  import { execSync } from "child_process";
21902
- import { existsSync as existsSync3 } from "fs";
21903
- import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
21904
- import { homedir as homedir2, platform as platform2 } from "os";
21905
- import { join as join4 } from "path";
21924
+ import { existsSync as existsSync4 } from "fs";
21925
+ import { readFile as readFile4, readdir as readdir2, unlink } from "fs/promises";
21926
+ import { homedir as homedir3, platform as platform3 } from "os";
21927
+ import { join as join5 } from "path";
21906
21928
  import { createServer } from "http";
21907
21929
  import { readFileSync } from "fs";
21908
- import { join as join5, dirname as dirname3 } from "path";
21930
+ import { join as join6, dirname as dirname3 } from "path";
21909
21931
  import { fileURLToPath } from "url";
21910
21932
  var __defProp2 = Object.defineProperty;
21933
+ var __returnValue2 = (v) => v;
21934
+ function __exportSetter2(name, newValue) {
21935
+ this[name] = __returnValue2.bind(null, newValue);
21936
+ }
21911
21937
  var __export2 = (target, all) => {
21912
21938
  for (var name in all)
21913
21939
  __defProp2(target, name, {
21914
21940
  get: all[name],
21915
21941
  enumerable: true,
21916
21942
  configurable: true,
21917
- set: (newValue) => all[name] = () => newValue
21943
+ set: __exportSetter2.bind(all, name)
21918
21944
  });
21919
21945
  };
21920
21946
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -21934,9 +21960,16 @@ function containsKeyword(text, keyword) {
21934
21960
  return false;
21935
21961
  }
21936
21962
  function getLibraryCategory(prefix, category, description) {
21937
- const normalizedPrefix = prefix.toUpperCase();
21938
- if (PREFIX_CATEGORY_MAP[normalizedPrefix]) {
21939
- return PREFIX_CATEGORY_MAP[normalizedPrefix];
21963
+ const cleanedPrefix = prefix.replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
21964
+ if (cleanedPrefix.length >= 2) {
21965
+ const twoCharPrefix = cleanedPrefix.slice(0, 2);
21966
+ if (PREFIX_CATEGORY_MAP[twoCharPrefix]) {
21967
+ return PREFIX_CATEGORY_MAP[twoCharPrefix];
21968
+ }
21969
+ }
21970
+ const singleCharPrefix = cleanedPrefix.slice(0, 1);
21971
+ if (PREFIX_CATEGORY_MAP[singleCharPrefix]) {
21972
+ return PREFIX_CATEGORY_MAP[singleCharPrefix];
21940
21973
  }
21941
21974
  const searchText = [category || "", description || ""].join(" ");
21942
21975
  if (searchText.trim()) {
@@ -22000,6 +22033,7 @@ var init_category_router = __esm(() => {
22000
22033
  X: "Crystals",
22001
22034
  J: "Connectors",
22002
22035
  P: "Connectors",
22036
+ RJ: "Connectors",
22003
22037
  K: "Misc",
22004
22038
  F: "Misc"
22005
22039
  };
@@ -22245,6 +22279,48 @@ var init_category_router = __esm(() => {
22245
22279
  "ceramic resonator"
22246
22280
  ]
22247
22281
  },
22282
+ {
22283
+ category: "Connectors",
22284
+ keywords: [
22285
+ "connector",
22286
+ "header",
22287
+ "socket",
22288
+ "terminal",
22289
+ "terminal block",
22290
+ "jack",
22291
+ "plug",
22292
+ "receptacle",
22293
+ "usb-c",
22294
+ "usb type-c",
22295
+ "micro usb",
22296
+ "mini usb",
22297
+ "usb-a",
22298
+ "usb-b",
22299
+ "hdmi connector",
22300
+ "rj45",
22301
+ "rj11",
22302
+ "barrel jack",
22303
+ "dc jack",
22304
+ "audio jack",
22305
+ "jst",
22306
+ "jst-xh",
22307
+ "jst-ph",
22308
+ "molex",
22309
+ "dupont",
22310
+ "pin header",
22311
+ "female header",
22312
+ "fpc",
22313
+ "ffc",
22314
+ "sim card",
22315
+ "sd card",
22316
+ "microsd",
22317
+ "pogo pin",
22318
+ "spring contact",
22319
+ "test point",
22320
+ "ethernet connector",
22321
+ "modular connector"
22322
+ ]
22323
+ },
22248
22324
  {
22249
22325
  category: "Transistors",
22250
22326
  keywords: [
@@ -22345,46 +22421,6 @@ var init_category_router = __esm(() => {
22345
22421
  "chip resistor"
22346
22422
  ]
22347
22423
  },
22348
- {
22349
- category: "Connectors",
22350
- keywords: [
22351
- "connector",
22352
- "header",
22353
- "socket",
22354
- "terminal",
22355
- "terminal block",
22356
- "jack",
22357
- "plug",
22358
- "receptacle",
22359
- "usb-c",
22360
- "usb type-c",
22361
- "micro usb",
22362
- "mini usb",
22363
- "usb-a",
22364
- "usb-b",
22365
- "hdmi connector",
22366
- "rj45",
22367
- "rj11",
22368
- "barrel jack",
22369
- "dc jack",
22370
- "audio jack",
22371
- "jst",
22372
- "jst-xh",
22373
- "jst-ph",
22374
- "molex",
22375
- "dupont",
22376
- "pin header",
22377
- "female header",
22378
- "fpc",
22379
- "ffc",
22380
- "sim card",
22381
- "sd card",
22382
- "microsd",
22383
- "pogo pin",
22384
- "spring contact",
22385
- "test point"
22386
- ]
22387
- },
22388
22424
  {
22389
22425
  category: "ICs",
22390
22426
  keywords: [
@@ -22478,9 +22514,9 @@ function addLibraryToTable(tableContent, libraryName, libraryPath, type, descrip
22478
22514
  `;
22479
22515
  }
22480
22516
  async function ensureSymLibTable(projectDir, symbolLibPath, libraryName = DEFAULT_LIBRARY_NAME, description = DEFAULT_LIBRARY_DESCRIPTION) {
22481
- const tablePath = join2(projectDir, "sym-lib-table");
22517
+ const tablePath = join3(projectDir, "sym-lib-table");
22482
22518
  const relativePath = symbolLibPath.startsWith(projectDir) ? "${KIPRJMOD}" + symbolLibPath.slice(projectDir.length) : symbolLibPath;
22483
- if (!existsSync(tablePath)) {
22519
+ if (!existsSync2(tablePath)) {
22484
22520
  const content = generateSymLibTable(relativePath, libraryName, description);
22485
22521
  await writeFile2(tablePath, content, "utf-8");
22486
22522
  return { created: true, modified: false, path: tablePath };
@@ -22494,9 +22530,9 @@ async function ensureSymLibTable(projectDir, symbolLibPath, libraryName = DEFAUL
22494
22530
  return { created: false, modified: true, path: tablePath };
22495
22531
  }
22496
22532
  async function ensureFpLibTable(projectDir, footprintLibPath, libraryName = DEFAULT_LIBRARY_NAME, description = DEFAULT_LIBRARY_DESCRIPTION) {
22497
- const tablePath = join2(projectDir, "fp-lib-table");
22533
+ const tablePath = join3(projectDir, "fp-lib-table");
22498
22534
  const relativePath = footprintLibPath.startsWith(projectDir) ? "${KIPRJMOD}" + footprintLibPath.slice(projectDir.length) : footprintLibPath;
22499
- if (!existsSync(tablePath)) {
22535
+ if (!existsSync2(tablePath)) {
22500
22536
  const content = generateFpLibTable(relativePath, libraryName, description);
22501
22537
  await writeFile2(tablePath, content, "utf-8");
22502
22538
  return { created: true, modified: false, path: tablePath };
@@ -22520,37 +22556,37 @@ __export2(exports_global_lib_table, {
22520
22556
  ensureGlobalLibraryTables: () => ensureGlobalLibraryTables,
22521
22557
  ensureGlobalEasyEDALibrary: () => ensureGlobalEasyEDALibrary
22522
22558
  });
22523
- function detectKicadVersion() {
22524
- const home = homedir();
22525
- const baseDir = join3(home, "Documents", "KiCad");
22559
+ function detectKicadVersion2() {
22560
+ const home = homedir2();
22561
+ const baseDir = join4(home, "Documents", "KiCad");
22526
22562
  for (const version2 of KICAD_VERSIONS2) {
22527
- if (existsSync2(join3(baseDir, version2))) {
22563
+ if (existsSync3(join4(baseDir, version2))) {
22528
22564
  return version2;
22529
22565
  }
22530
22566
  }
22531
22567
  return "9.0";
22532
22568
  }
22533
- function getKicadConfigDir(version2) {
22534
- const home = homedir();
22535
- const plat = platform();
22569
+ function getKicadConfigDir2(version2) {
22570
+ const home = homedir2();
22571
+ const plat = platform2();
22536
22572
  if (plat === "darwin") {
22537
- return join3(home, "Library", "Preferences", "kicad", version2);
22573
+ return join4(home, "Library", "Preferences", "kicad", version2);
22538
22574
  } else if (plat === "win32") {
22539
- return join3(process.env.APPDATA || join3(home, "AppData", "Roaming"), "kicad", version2);
22575
+ return join4(process.env.APPDATA || join4(home, "AppData", "Roaming"), "kicad", version2);
22540
22576
  } else {
22541
- return join3(home, ".config", "kicad", version2);
22577
+ return join4(home, ".config", "kicad", version2);
22542
22578
  }
22543
22579
  }
22544
22580
  function get3rdPartyBaseDir(version2) {
22545
- const home = homedir();
22546
- const plat = platform();
22581
+ const home = homedir2();
22582
+ const plat = platform2();
22547
22583
  if (plat === "linux") {
22548
- return join3(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE);
22584
+ return join4(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE);
22549
22585
  }
22550
- return join3(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE);
22586
+ return join4(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE);
22551
22587
  }
22552
22588
  function getSymbolLibPath(category, version2) {
22553
- return join3(get3rdPartyBaseDir(version2), "symbols", getLibraryFilename(category));
22589
+ return join4(get3rdPartyBaseDir(version2), "symbols", getLibraryFilename(category));
22554
22590
  }
22555
22591
  function getSymbolLibUri(category) {
22556
22592
  return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${getLibraryFilename(category)}`;
@@ -22591,11 +22627,11 @@ function generateFpLibTable2() {
22591
22627
  `;
22592
22628
  }
22593
22629
  async function ensureGlobalSymLibTable(version2) {
22594
- const configDir = getKicadConfigDir(version2);
22595
- const tablePath = join3(configDir, "sym-lib-table");
22630
+ const configDir = getKicadConfigDir2(version2);
22631
+ const tablePath = join4(configDir, "sym-lib-table");
22596
22632
  const categories = getAllCategories();
22597
22633
  await mkdir2(configDir, { recursive: true });
22598
- if (!existsSync2(tablePath)) {
22634
+ if (!existsSync3(tablePath)) {
22599
22635
  const content2 = generateSymLibTable2();
22600
22636
  await writeFile3(tablePath, content2, "utf-8");
22601
22637
  return {
@@ -22635,11 +22671,11 @@ async function ensureGlobalSymLibTable(version2) {
22635
22671
  };
22636
22672
  }
22637
22673
  async function ensureGlobalFpLibTable(version2) {
22638
- const configDir = getKicadConfigDir(version2);
22639
- const tablePath = join3(configDir, "fp-lib-table");
22674
+ const configDir = getKicadConfigDir2(version2);
22675
+ const tablePath = join4(configDir, "fp-lib-table");
22640
22676
  const name = LIBRARY_PREFIX2;
22641
22677
  await mkdir2(configDir, { recursive: true });
22642
- if (!existsSync2(tablePath)) {
22678
+ if (!existsSync3(tablePath)) {
22643
22679
  const content2 = generateFpLibTable2();
22644
22680
  await writeFile3(tablePath, content2, "utf-8");
22645
22681
  return {
@@ -22673,13 +22709,13 @@ async function ensureGlobalFpLibTable(version2) {
22673
22709
  }
22674
22710
  async function ensureLibraryStubs(version2) {
22675
22711
  const baseDir = get3rdPartyBaseDir(version2);
22676
- const symbolsDir = join3(baseDir, "symbols");
22677
- const footprintsDir = join3(baseDir, "footprints", getFootprintDirName());
22678
- const models3dDir = join3(baseDir, "3dmodels", get3DModelsDirName());
22712
+ const symbolsDir = join4(baseDir, "symbols");
22713
+ const footprintsDir = join4(baseDir, "footprints", getFootprintDirName());
22714
+ const models3dDir = join4(baseDir, "3dmodels", get3DModelsDirName());
22679
22715
  const directoriesCreated = [];
22680
22716
  const symbolsCreated = [];
22681
22717
  for (const dir of [symbolsDir, footprintsDir, models3dDir]) {
22682
- if (!existsSync2(dir)) {
22718
+ if (!existsSync3(dir)) {
22683
22719
  await mkdir2(dir, { recursive: true });
22684
22720
  directoriesCreated.push(dir);
22685
22721
  }
@@ -22688,7 +22724,7 @@ async function ensureLibraryStubs(version2) {
22688
22724
  const emptyContent = generateEmptySymbolLibrary();
22689
22725
  for (const category of categories) {
22690
22726
  const filePath = getSymbolLibPath(category, version2);
22691
- if (!existsSync2(filePath)) {
22727
+ if (!existsSync3(filePath)) {
22692
22728
  await writeFile3(filePath, emptyContent, "utf-8");
22693
22729
  symbolsCreated.push(filePath);
22694
22730
  }
@@ -22702,11 +22738,11 @@ function getEasyEDAFootprintLibUri() {
22702
22738
  return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/footprints/${EASYEDA_FOOTPRINT_LIBRARY_NAME}`;
22703
22739
  }
22704
22740
  async function ensureGlobalEasyEDALibrary() {
22705
- const version2 = detectKicadVersion();
22706
- const configDir = getKicadConfigDir(version2);
22741
+ const version2 = detectKicadVersion2();
22742
+ const configDir = getKicadConfigDir2(version2);
22707
22743
  await mkdir2(configDir, { recursive: true });
22708
- const symTablePath = join3(configDir, "sym-lib-table");
22709
- if (existsSync2(symTablePath)) {
22744
+ const symTablePath = join4(configDir, "sym-lib-table");
22745
+ if (existsSync3(symTablePath)) {
22710
22746
  let content = await readFile3(symTablePath, "utf-8");
22711
22747
  if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
22712
22748
  const uri = getEasyEDASymbolLibUri();
@@ -22722,8 +22758,8 @@ async function ensureGlobalEasyEDALibrary() {
22722
22758
  `;
22723
22759
  await writeFile3(symTablePath, content, "utf-8");
22724
22760
  }
22725
- const fpTablePath = join3(configDir, "fp-lib-table");
22726
- if (existsSync2(fpTablePath)) {
22761
+ const fpTablePath = join4(configDir, "fp-lib-table");
22762
+ if (existsSync3(fpTablePath)) {
22727
22763
  let content = await readFile3(fpTablePath, "utf-8");
22728
22764
  if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
22729
22765
  const uri = getEasyEDAFootprintLibUri();
@@ -22742,7 +22778,7 @@ async function ensureGlobalEasyEDALibrary() {
22742
22778
  }
22743
22779
  async function ensureGlobalLibraryTables() {
22744
22780
  const errors22 = [];
22745
- const version2 = detectKicadVersion();
22781
+ const version2 = detectKicadVersion2();
22746
22782
  let symLibTable = {
22747
22783
  path: "",
22748
22784
  created: false,
@@ -26770,6 +26806,9 @@ var coerce2 = {
26770
26806
  };
26771
26807
  var NEVER3 = INVALID2;
26772
26808
  var LCSCPartNumberSchema = exports_external2.string().regex(/^C\d+$/, "Invalid LCSC part number format (expected C followed by digits)");
26809
+ var EasyEDAUuidSchema = exports_external2.string().regex(/^[a-f0-9]{32}$/i, "Invalid EasyEDA UUID (expected 32-character hex string)").transform((s) => s.toLowerCase());
26810
+ var ComponentIdSchema = exports_external2.union([LCSCPartNumberSchema, EasyEDAUuidSchema]);
26811
+ var SafePathSchema = exports_external2.string().min(1, "Path cannot be empty").refine((p) => !p.includes(".."), "Path traversal (..) not allowed").refine((p) => !p.includes("\x00"), "Null bytes not allowed in path");
26773
26812
  var PackageSchema = exports_external2.string().min(1, "Package cannot be empty");
26774
26813
  var PriceTierSchema = exports_external2.object({
26775
26814
  quantity: exports_external2.number().positive(),
@@ -26845,6 +26884,9 @@ var KiCadPadShapeSchema = exports_external2.enum([
26845
26884
  "roundrect",
26846
26885
  "custom"
26847
26886
  ]);
26887
+ function isLcscId(id) {
26888
+ return LCSCPartNumberSchema.safeParse(id).success;
26889
+ }
26848
26890
  async function ensureDir(path) {
26849
26891
  await mkdir(path, { recursive: true });
26850
26892
  }
@@ -26926,7 +26968,58 @@ var logger = new Logger({ prefix: "ai-eda" });
26926
26968
  function createLogger(packageName) {
26927
26969
  return logger.child(packageName);
26928
26970
  }
26971
+ var KICAD_LAYERS = {
26972
+ F_CU: "F.Cu",
26973
+ B_CU: "B.Cu",
26974
+ IN1_CU: "In1.Cu",
26975
+ IN2_CU: "In2.Cu",
26976
+ IN3_CU: "In3.Cu",
26977
+ IN4_CU: "In4.Cu",
26978
+ F_ADHES: "F.Adhes",
26979
+ F_PASTE: "F.Paste",
26980
+ F_SILKS: "F.SilkS",
26981
+ F_MASK: "F.Mask",
26982
+ F_CRTYD: "F.CrtYd",
26983
+ F_FAB: "F.Fab",
26984
+ B_ADHES: "B.Adhes",
26985
+ B_PASTE: "B.Paste",
26986
+ B_SILKS: "B.SilkS",
26987
+ B_MASK: "B.Mask",
26988
+ B_CRTYD: "B.CrtYd",
26989
+ B_FAB: "B.Fab",
26990
+ EDGE_CUTS: "Edge.Cuts",
26991
+ MARGIN: "Margin",
26992
+ DWGS_USER: "Dwgs.User",
26993
+ CMTS_USER: "Cmts.User",
26994
+ ECO1_USER: "Eco1.User",
26995
+ ECO2_USER: "Eco2.User"
26996
+ };
26997
+ var KICAD_DEFAULTS = {
26998
+ TEXT_SIZE: 1.27,
26999
+ TEXT_THICKNESS: 0.15,
27000
+ WIRE_WIDTH: 0.25,
27001
+ GRID_SCHEMATIC: 2.54,
27002
+ GRID_PCB: 0.25,
27003
+ PIN_LENGTH: 1.27,
27004
+ PIN_NAME_OFFSET: 0
27005
+ };
27006
+ var KICAD_SYMBOL_VERSION = "20241209";
27007
+ var KICAD_FOOTPRINT_VERSION = "20241209";
27008
+ var KICAD_VERSIONS = ["9.0", "8.0"];
27009
+ function detectKicadVersion() {
27010
+ const home = homedir();
27011
+ const baseDir = join2(home, "Documents", "KiCad");
27012
+ for (const version2 of KICAD_VERSIONS) {
27013
+ if (existsSync(join2(baseDir, version2))) {
27014
+ return version2;
27015
+ }
27016
+ }
27017
+ return "9.0";
27018
+ }
26929
27019
  var logger3 = createLogger("jlc-api");
27020
+ function stripTemperaturePrefix(description) {
27021
+ return description.replace(/^-?\d+℃~[+-]?\d+℃\s*/, "");
27022
+ }
26930
27023
  var JLCPCB_SEARCH_API = "https://jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList/v2";
26931
27024
 
26932
27025
  class JLCClient {
@@ -26972,7 +27065,7 @@ class JLCClient {
26972
27065
  package: c.componentSpecificationEn || "",
26973
27066
  price: c.componentPrices?.[0]?.productPrice,
26974
27067
  stock: c.stockCount,
26975
- description: c.describe,
27068
+ description: stripTemperaturePrefix(c.describe),
26976
27069
  productUrl: c.lcscGoodsUrl,
26977
27070
  datasheetPdf: c.dataManualUrl,
26978
27071
  category: c.componentTypeEn,
@@ -27079,6 +27172,34 @@ function safeParseInt(value, defaultValue = 0) {
27079
27172
  const parsed = parseInt(value, 10);
27080
27173
  return isNaN(parsed) ? defaultValue : parsed;
27081
27174
  }
27175
+ function parse3DModelNode(data) {
27176
+ try {
27177
+ const jsonStr = data.slice("SVGNODE~".length);
27178
+ const svgData = JSON.parse(jsonStr);
27179
+ const attrs = svgData?.attrs;
27180
+ if (!attrs?.uuid) {
27181
+ return;
27182
+ }
27183
+ const [translationX, translationY] = String(attrs.c_origin ?? "0,0").split(",").map((value) => safeParseFloat(value));
27184
+ const [rotationX, rotationY, rotationZ] = String(attrs.c_rotation ?? "0,0,0").split(",").map((value) => safeParseFloat(value));
27185
+ return {
27186
+ name: attrs.title || "3D Model",
27187
+ uuid: attrs.uuid,
27188
+ translation: {
27189
+ x: translationX,
27190
+ y: translationY,
27191
+ z: safeParseFloat(String(attrs.z ?? "0"))
27192
+ },
27193
+ rotation: {
27194
+ x: rotationX,
27195
+ y: rotationY,
27196
+ z: rotationZ
27197
+ }
27198
+ };
27199
+ } catch {
27200
+ return;
27201
+ }
27202
+ }
27082
27203
  function parseSymbolPin(pinData) {
27083
27204
  try {
27084
27205
  const segments = pinData.split("^^");
@@ -27212,6 +27333,25 @@ function parseSymbolPath(data) {
27212
27333
  return null;
27213
27334
  }
27214
27335
  }
27336
+ function parseSymbolText(data) {
27337
+ try {
27338
+ const f = data.split("~");
27339
+ const isPinPart = f[f.length - 1] === "pinpart" || f.some((field) => field === "pinpart");
27340
+ return {
27341
+ x: safeParseFloat(f[2]),
27342
+ y: safeParseFloat(f[3]),
27343
+ rotation: safeParseFloat(f[4]),
27344
+ color: f[5] || "#000000",
27345
+ fontSize: safeParseFloat(f[7], 7),
27346
+ textType: f[11] || "",
27347
+ text: f[12] || "",
27348
+ id: f[15] || f[13] || "",
27349
+ isPinPart
27350
+ };
27351
+ } catch {
27352
+ return null;
27353
+ }
27354
+ }
27215
27355
  function parsePad(data) {
27216
27356
  try {
27217
27357
  const f = data.split("~");
@@ -27379,6 +27519,7 @@ function parseSymbolShapes(shapes) {
27379
27519
  const polylines = [];
27380
27520
  const polygons = [];
27381
27521
  const paths = [];
27522
+ const texts = [];
27382
27523
  for (const line of shapes) {
27383
27524
  if (typeof line !== "string")
27384
27525
  continue;
@@ -27432,10 +27573,15 @@ function parseSymbolShapes(shapes) {
27432
27573
  paths.push(path);
27433
27574
  break;
27434
27575
  }
27435
- case "T":
27576
+ case "T": {
27577
+ const text = parseSymbolText(line);
27578
+ if (text && text.text.trim())
27579
+ texts.push(text);
27436
27580
  break;
27581
+ }
27437
27582
  }
27438
27583
  }
27584
+ associatePinNamesFromTexts(pins, texts);
27439
27585
  return {
27440
27586
  pins,
27441
27587
  rectangles,
@@ -27444,8 +27590,29 @@ function parseSymbolShapes(shapes) {
27444
27590
  arcs,
27445
27591
  polylines,
27446
27592
  polygons,
27447
- paths
27448
- };
27593
+ paths,
27594
+ texts
27595
+ };
27596
+ }
27597
+ function associatePinNamesFromTexts(pins, texts) {
27598
+ const pinpartTexts = texts.filter((t) => t.isPinPart && t.text.trim());
27599
+ for (const text of pinpartTexts) {
27600
+ const textContent = text.text.trim();
27601
+ const jMatch = textContent.match(/^J(\d+)(?:\/(.+))?$/i);
27602
+ if (jMatch) {
27603
+ const pinNum = jMatch[1];
27604
+ const pinFunction = jMatch[2] || textContent;
27605
+ const pin = pins.find((p) => p.number === pinNum);
27606
+ if (pin && (!pin.name || pin.name === pinNum)) {
27607
+ pin.name = pinFunction || textContent;
27608
+ }
27609
+ continue;
27610
+ }
27611
+ const numMatch = textContent.match(/^(\d+)$/);
27612
+ if (numMatch) {
27613
+ continue;
27614
+ }
27615
+ }
27449
27616
  }
27450
27617
  function parseFootprintShapes(shapes) {
27451
27618
  const pads = [];
@@ -27512,16 +27679,10 @@ function parseFootprintShapes(shapes) {
27512
27679
  break;
27513
27680
  }
27514
27681
  case "SVGNODE": {
27515
- try {
27516
- const jsonStr = line.split("~")[1];
27517
- const svgData = JSON.parse(jsonStr);
27518
- if (svgData?.attrs?.uuid) {
27519
- model3d = {
27520
- name: svgData.attrs.title || "3D Model",
27521
- uuid: svgData.attrs.uuid
27522
- };
27523
- }
27524
- } catch {}
27682
+ const parsedModel3d = parse3DModelNode(line);
27683
+ if (parsedModel3d) {
27684
+ model3d = parsedModel3d;
27685
+ }
27525
27686
  break;
27526
27687
  }
27527
27688
  case "SOLIDREGION": {
@@ -27633,6 +27794,7 @@ class EasyEDAClient {
27633
27794
  polylines: symbolData.polylines,
27634
27795
  polygons: symbolData.polygons,
27635
27796
  paths: symbolData.paths,
27797
+ texts: symbolData.texts,
27636
27798
  origin: symbolOrigin
27637
27799
  },
27638
27800
  footprint: {
@@ -27806,6 +27968,7 @@ class EasyEDACommunityClient {
27806
27968
  polylines: symbolData.polylines,
27807
27969
  polygons: symbolData.polygons,
27808
27970
  paths: symbolData.paths,
27971
+ texts: symbolData.texts,
27809
27972
  origin: symbolOrigin,
27810
27973
  head: dataStr.head || {}
27811
27974
  },
@@ -27832,14 +27995,14 @@ class EasyEDACommunityClient {
27832
27995
  }
27833
27996
  }
27834
27997
  var easyedaCommunityClient = new EasyEDACommunityClient;
27835
- function isLcscId(id) {
27998
+ function isLcscId2(id) {
27836
27999
  return /^C\d+$/.test(id);
27837
28000
  }
27838
28001
  function createComponentService() {
27839
28002
  return {
27840
28003
  async search(query, options = {}) {
27841
28004
  const { source = "lcsc", limit = 20, inStock, basicOnly } = options;
27842
- if (source === "easyeda-community") {
28005
+ if (source === "community" || source === "easyeda-community") {
27843
28006
  const results = await easyedaCommunityClient.search({
27844
28007
  query,
27845
28008
  limit
@@ -27856,7 +28019,7 @@ function createComponentService() {
27856
28019
  return jlcClient.search(query, { limit, inStock, basicOnly });
27857
28020
  },
27858
28021
  async fetch(id) {
27859
- if (isLcscId(id)) {
28022
+ if (isLcscId2(id)) {
27860
28023
  return easyedaClient.getComponentData(id);
27861
28024
  }
27862
28025
  return null;
@@ -27884,43 +28047,6 @@ function createComponentService() {
27884
28047
  }
27885
28048
  };
27886
28049
  }
27887
- var KICAD_LAYERS = {
27888
- F_CU: "F.Cu",
27889
- B_CU: "B.Cu",
27890
- IN1_CU: "In1.Cu",
27891
- IN2_CU: "In2.Cu",
27892
- IN3_CU: "In3.Cu",
27893
- IN4_CU: "In4.Cu",
27894
- F_ADHES: "F.Adhes",
27895
- F_PASTE: "F.Paste",
27896
- F_SILKS: "F.SilkS",
27897
- F_MASK: "F.Mask",
27898
- F_CRTYD: "F.CrtYd",
27899
- F_FAB: "F.Fab",
27900
- B_ADHES: "B.Adhes",
27901
- B_PASTE: "B.Paste",
27902
- B_SILKS: "B.SilkS",
27903
- B_MASK: "B.Mask",
27904
- B_CRTYD: "B.CrtYd",
27905
- B_FAB: "B.Fab",
27906
- EDGE_CUTS: "Edge.Cuts",
27907
- MARGIN: "Margin",
27908
- DWGS_USER: "Dwgs.User",
27909
- CMTS_USER: "Cmts.User",
27910
- ECO1_USER: "Eco1.User",
27911
- ECO2_USER: "Eco2.User"
27912
- };
27913
- var KICAD_DEFAULTS = {
27914
- TEXT_SIZE: 1.27,
27915
- TEXT_THICKNESS: 0.15,
27916
- WIRE_WIDTH: 0.25,
27917
- GRID_SCHEMATIC: 2.54,
27918
- GRID_PCB: 0.25,
27919
- PIN_LENGTH: 1.27,
27920
- PIN_NAME_OFFSET: 0
27921
- };
27922
- var KICAD_SYMBOL_VERSION = "20241209";
27923
- var KICAD_FOOTPRINT_VERSION = "20241209";
27924
28050
  var PATTERNS = {
27925
28051
  resistor: /(\d+(?:\.\d+)?)\s*([kKmMgGrR]?)\s*(?:ohm|Ohm|OHM|Ω|R)?/i,
27926
28052
  capacitor: /(\d+(?:\.\d+)?)\s*([pnuμmM]?)[Ff]?\s*(?:[\/\s]*(\d+)\s*[Vv])?/i,
@@ -28330,6 +28456,29 @@ function parseSvgArcPath(path) {
28330
28456
  return null;
28331
28457
  }
28332
28458
  }
28459
+ function interpolateArc(params, segmentsPerQuarter = 4) {
28460
+ const center = svgArcToCenter(params);
28461
+ if (!center) {
28462
+ return [{ x: params.x2, y: params.y2 }];
28463
+ }
28464
+ const { cx, cy, rx, ry, startAngle, deltaAngle } = center;
28465
+ const phiRad = params.phi * Math.PI / 180;
28466
+ const cosPhi = Math.cos(phiRad);
28467
+ const sinPhi = Math.sin(phiRad);
28468
+ const arcDegrees = Math.abs(deltaAngle) * (180 / Math.PI);
28469
+ const numSegments = Math.max(2, Math.ceil(arcDegrees / 90 * segmentsPerQuarter));
28470
+ const points = [];
28471
+ for (let i = 1;i <= numSegments; i++) {
28472
+ const t = i / numSegments;
28473
+ const angle = startAngle + deltaAngle * t;
28474
+ const px = rx * Math.cos(angle);
28475
+ const py = ry * Math.sin(angle);
28476
+ const x = cosPhi * px - sinPhi * py + cx;
28477
+ const y = sinPhi * px + cosPhi * py + cy;
28478
+ points.push({ x, y });
28479
+ }
28480
+ return points;
28481
+ }
28333
28482
  var CATEGORY_TO_PREFIX = {
28334
28483
  Resistors: "R",
28335
28484
  Capacitors: "C",
@@ -28392,7 +28541,12 @@ class SymbolConverter {
28392
28541
  return output;
28393
28542
  }
28394
28543
  hasRenderableShapes(symbol) {
28395
- return symbol.rectangles.length > 0 || symbol.circles.length > 0 || symbol.ellipses.length > 0 || symbol.polylines.length > 0 || symbol.polygons.length > 0 || symbol.arcs.length > 0 || symbol.paths.length > 0;
28544
+ const pinNamePattern = /^J\d+(?:\/|$)/i;
28545
+ const hasDecorativeText = (symbol.texts || []).some((t) => {
28546
+ const text = t.text.trim();
28547
+ return text && !pinNamePattern.test(text);
28548
+ });
28549
+ return symbol.rectangles.length > 0 || symbol.circles.length > 0 || symbol.ellipses.length > 0 || symbol.polylines.length > 0 || symbol.polygons.length > 0 || symbol.arcs.length > 0 || symbol.paths.length > 0 || hasDecorativeText;
28396
28550
  }
28397
28551
  generateFromShapes(component, name) {
28398
28552
  const { info, symbol } = component;
@@ -28426,6 +28580,18 @@ class SymbolConverter {
28426
28580
  if (pathOutput)
28427
28581
  output += pathOutput;
28428
28582
  }
28583
+ const pinNamePattern = /^J\d+(?:\/|$)/i;
28584
+ const decorativeTexts = (symbol.texts || []).filter((t) => {
28585
+ const text = t.text.trim();
28586
+ if (!text)
28587
+ return false;
28588
+ return !pinNamePattern.test(text);
28589
+ });
28590
+ for (const text of decorativeTexts) {
28591
+ const textOutput = this.convertText(text, origin);
28592
+ if (textOutput)
28593
+ output += textOutput;
28594
+ }
28429
28595
  output += ` )
28430
28596
  `;
28431
28597
  output += ` (symbol "${name}_1_1"
@@ -28610,6 +28776,24 @@ class SymbolConverter {
28610
28776
  `;
28611
28777
  return output;
28612
28778
  }
28779
+ convertText(text, origin) {
28780
+ if (!text.text || text.text.trim() === "")
28781
+ return null;
28782
+ const x = this.convertX(text.x, origin.x);
28783
+ const y = this.convertY(text.y, origin.y);
28784
+ const fontSize = roundTo(Math.max(text.fontSize * EE_TO_MM * 0.8, 0.5), 3);
28785
+ const rotation = (360 - text.rotation) % 360;
28786
+ const content = text.text.replace(/"/g, "'").replace(/\\/g, "");
28787
+ return ` (text "${content}"
28788
+ (at ${x} ${y} ${rotation})
28789
+ (effects
28790
+ (font
28791
+ (size ${fontSize} ${fontSize})
28792
+ )
28793
+ )
28794
+ )
28795
+ `;
28796
+ }
28613
28797
  parseSvgPath(pathStr, origin) {
28614
28798
  const points = [];
28615
28799
  let firstPoint = null;
@@ -29606,6 +29790,9 @@ var KI_PAD_SHAPE = {
29606
29790
  OVAL: "oval",
29607
29791
  POLYGON: "custom"
29608
29792
  };
29793
+ function normalize3DModelRotation(rotation) {
29794
+ return ((360 - rotation) % 360 + 360) % 360;
29795
+ }
29609
29796
  function calculatePadCenter(pads) {
29610
29797
  if (pads.length === 0) {
29611
29798
  return { x: 0, y: 0 };
@@ -29734,7 +29921,7 @@ class FootprintConverter {
29734
29921
  output += ` (embedded_fonts no)
29735
29922
  `;
29736
29923
  if (include3DModel && model3d && options.modelPath) {
29737
- output += this.generate3DModel(options.modelPath);
29924
+ output += this.generate3DModel(options.modelPath, model3d, origin);
29738
29925
  }
29739
29926
  output += `)`;
29740
29927
  return output;
@@ -29815,7 +30002,7 @@ class FootprintConverter {
29815
30002
  return this.generatePad({ ...pad, shape: "RECT", points: "" }, origin);
29816
30003
  }
29817
30004
  let holeRadius = pad.holeRadius;
29818
- if (holeRadius === 0 && pad.isPlated && pad.shape === "POLYGON") {
30005
+ if (holeRadius === 0 && pad.layerId === 11 && pad.shape === "POLYGON") {
29819
30006
  holeRadius = this.calculateDrillRadiusFromPolygon(points, pad.centerX, pad.centerY);
29820
30007
  }
29821
30008
  const isSmd = holeRadius === 0;
@@ -30157,12 +30344,26 @@ ${justify ? ` (justify ${justify})
30157
30344
  break;
30158
30345
  case "A":
30159
30346
  if (args.length >= 7) {
30347
+ const arcParams = {
30348
+ x1: currentX,
30349
+ y1: currentY,
30350
+ rx: args[0],
30351
+ ry: args[1],
30352
+ phi: args[2],
30353
+ largeArc: args[3] === 1,
30354
+ sweep: args[4] === 1,
30355
+ x2: args[5],
30356
+ y2: args[6]
30357
+ };
30358
+ const arcPoints = interpolateArc(arcParams, 4);
30359
+ for (const pt of arcPoints) {
30360
+ points.push({
30361
+ x: convertX(pt.x, origin.x),
30362
+ y: convertY(pt.y, origin.y)
30363
+ });
30364
+ }
30160
30365
  currentX = args[5];
30161
30366
  currentY = args[6];
30162
- points.push({
30163
- x: convertX(currentX, origin.x),
30164
- y: convertY(currentY, origin.y)
30165
- });
30166
30367
  }
30167
30368
  break;
30168
30369
  case "Z":
@@ -30309,16 +30510,22 @@ ${justify ? ` (justify ${justify})
30309
30510
  }
30310
30511
  return output;
30311
30512
  }
30312
- generate3DModel(modelPath) {
30513
+ generate3DModel(modelPath, model3d, origin) {
30514
+ const offsetX = roundTo(toMM(model3d.translation.x - origin.x), 3);
30515
+ const offsetY = roundTo(-toMM(model3d.translation.y - origin.y), 3);
30516
+ const offsetZ = roundTo(-toMM(model3d.translation.z), 3);
30517
+ const rotateX = normalize3DModelRotation(model3d.rotation.x);
30518
+ const rotateY = normalize3DModelRotation(model3d.rotation.y);
30519
+ const rotateZ = normalize3DModelRotation(model3d.rotation.z);
30313
30520
  return ` (model "${modelPath}"
30314
30521
  (offset
30315
- (xyz 0 0 0)
30522
+ (xyz ${offsetX} ${offsetY} ${offsetZ})
30316
30523
  )
30317
30524
  (scale
30318
30525
  (xyz 1 1 1)
30319
30526
  )
30320
30527
  (rotate
30321
- (xyz 0 0 0)
30528
+ (xyz ${rotateX} ${rotateY} ${rotateZ})
30322
30529
  )
30323
30530
  )
30324
30531
  `;
@@ -30336,7 +30543,6 @@ init_category_router();
30336
30543
  var FOOTPRINT_LIBRARY_NAME = getFootprintDirName();
30337
30544
  var MODELS_3D_NAME = get3DModelsDirName();
30338
30545
  var LIBRARY_NAMESPACE2 = "jlc_mcp";
30339
- var KICAD_VERSIONS3 = ["9.0", "8.0"];
30340
30546
  var EASYEDA_LIBRARY_NAME2 = "EasyEDA";
30341
30547
  var EASYEDA_SYMBOL_LIBRARY_NAME2 = "EasyEDA.kicad_sym";
30342
30548
  var EASYEDA_FOOTPRINT_LIBRARY_NAME2 = "EasyEDA.pretty";
@@ -30345,58 +30551,48 @@ var EASYEDA_LOCAL_LIBRARY_NAME = "EasyEDA-local";
30345
30551
  var EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME = "EasyEDA-local.kicad_sym";
30346
30552
  var EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME = "EasyEDA-local.pretty";
30347
30553
  var EASYEDA_LOCAL_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library (Project-local)";
30348
- function detectKicadVersion2() {
30349
- const home = homedir2();
30350
- const baseDir = join4(home, "Documents", "KiCad");
30351
- for (const version2 of KICAD_VERSIONS3) {
30352
- if (existsSync3(join4(baseDir, version2))) {
30353
- return version2;
30354
- }
30355
- }
30356
- return "9.0";
30357
- }
30358
30554
  function getGlobalLibraryPaths() {
30359
- const home = homedir2();
30360
- const version2 = detectKicadVersion2();
30361
- const plat = platform2();
30555
+ const home = homedir3();
30556
+ const version2 = detectKicadVersion();
30557
+ const plat = platform3();
30362
30558
  let base;
30363
30559
  if (plat === "linux") {
30364
- base = join4(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30560
+ base = join5(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30365
30561
  } else {
30366
- base = join4(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30562
+ base = join5(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE2);
30367
30563
  }
30368
30564
  return {
30369
30565
  base,
30370
- symbolsDir: join4(base, "symbols"),
30371
- footprintsDir: join4(base, "footprints"),
30372
- models3dDir: join4(base, "3dmodels"),
30373
- footprintDir: join4(base, "footprints", FOOTPRINT_LIBRARY_NAME),
30374
- models3dFullDir: join4(base, "3dmodels", MODELS_3D_NAME)
30566
+ symbolsDir: join5(base, "symbols"),
30567
+ footprintsDir: join5(base, "footprints"),
30568
+ models3dDir: join5(base, "3dmodels"),
30569
+ footprintDir: join5(base, "footprints", FOOTPRINT_LIBRARY_NAME),
30570
+ models3dFullDir: join5(base, "3dmodels", MODELS_3D_NAME)
30375
30571
  };
30376
30572
  }
30377
30573
  function getProjectLibraryPaths(projectPath) {
30378
- const librariesDir = join4(projectPath, "libraries");
30574
+ const librariesDir = join5(projectPath, "libraries");
30379
30575
  return {
30380
30576
  base: librariesDir,
30381
- symbolsDir: join4(librariesDir, "symbols"),
30382
- footprintsDir: join4(librariesDir, "footprints"),
30383
- models3dDir: join4(librariesDir, "3dmodels"),
30384
- footprintDir: join4(librariesDir, "footprints", FOOTPRINT_LIBRARY_NAME),
30385
- models3dFullDir: join4(librariesDir, "3dmodels", MODELS_3D_NAME)
30577
+ symbolsDir: join5(librariesDir, "symbols"),
30578
+ footprintsDir: join5(librariesDir, "footprints"),
30579
+ models3dDir: join5(librariesDir, "3dmodels"),
30580
+ footprintDir: join5(librariesDir, "footprints", FOOTPRINT_LIBRARY_NAME),
30581
+ models3dFullDir: join5(librariesDir, "3dmodels", MODELS_3D_NAME)
30386
30582
  };
30387
30583
  }
30388
- function isLcscId2(id) {
30584
+ function isLcscId3(id) {
30389
30585
  return /^C\d+$/.test(id);
30390
30586
  }
30391
- function getKicadConfigDir2(version2) {
30392
- const home = homedir2();
30393
- const plat = platform2();
30587
+ function getKicadConfigDir3(version2) {
30588
+ const home = homedir3();
30589
+ const plat = platform3();
30394
30590
  if (plat === "darwin") {
30395
- return join4(home, "Library", "Preferences", "kicad", version2);
30591
+ return join5(home, "Library", "Preferences", "kicad", version2);
30396
30592
  } else if (plat === "win32") {
30397
- return join4(process.env.APPDATA || join4(home, "AppData", "Roaming"), "kicad", version2);
30593
+ return join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "kicad", version2);
30398
30594
  } else {
30399
- return join4(home, ".config", "kicad", version2);
30595
+ return join5(home, ".config", "kicad", version2);
30400
30596
  }
30401
30597
  }
30402
30598
  async function parseSymbolLibrary(filePath, libraryName, category, models3dDir) {
@@ -30422,8 +30618,8 @@ async function parseSymbolLibrary(filePath, libraryName, category, models3dDir)
30422
30618
  const name = nameParts.length > 1 ? nameParts[1] : fullSymbolRef;
30423
30619
  const fpMatch = symbolBlock.match(/\(property\s+"Footprint"\s+"([^"]+)"/);
30424
30620
  const footprintRef = fpMatch ? fpMatch[1] : "";
30425
- const model3dPath = join4(models3dDir, `${name}.step`);
30426
- const has3dModel = existsSync3(model3dPath);
30621
+ const model3dPath = join5(models3dDir, `${name}.step`);
30622
+ const has3dModel = existsSync4(model3dPath);
30427
30623
  if (lcscId) {
30428
30624
  components.push({
30429
30625
  lcscId,
@@ -30439,6 +30635,42 @@ async function parseSymbolLibrary(filePath, libraryName, category, models3dDir)
30439
30635
  } catch {}
30440
30636
  return components;
30441
30637
  }
30638
+ function removeSymbolFromLibrary(content, symbolName) {
30639
+ const lines = content.split(`
30640
+ `);
30641
+ const result = [];
30642
+ let depth = 0;
30643
+ let inTargetSymbol = false;
30644
+ let symbolStartDepth = 0;
30645
+ for (const line of lines) {
30646
+ const openCount = (line.match(/\(/g) || []).length;
30647
+ const closeCount = (line.match(/\)/g) || []).length;
30648
+ const symbolMatch = line.match(/^\s*\(symbol\s+"([^"]+)"/);
30649
+ if (symbolMatch && !inTargetSymbol) {
30650
+ const fullRef = symbolMatch[1];
30651
+ const nameOnly = fullRef.includes(":") ? fullRef.split(":")[1] : fullRef;
30652
+ if (nameOnly === symbolName || nameOnly.startsWith(`${symbolName}_`)) {
30653
+ inTargetSymbol = true;
30654
+ symbolStartDepth = depth;
30655
+ depth += openCount - closeCount;
30656
+ continue;
30657
+ }
30658
+ }
30659
+ if (inTargetSymbol) {
30660
+ depth += openCount - closeCount;
30661
+ if (depth <= symbolStartDepth) {
30662
+ inTargetSymbol = false;
30663
+ }
30664
+ continue;
30665
+ }
30666
+ depth += openCount - closeCount;
30667
+ result.push(line);
30668
+ }
30669
+ return result.join(`
30670
+ `).replace(/\n{3,}/g, `
30671
+
30672
+ `);
30673
+ }
30442
30674
  function adaptCommunityComponent(component) {
30443
30675
  const symbolHead = component.symbol.head;
30444
30676
  const cPara = symbolHead?.c_para || {};
@@ -30461,6 +30693,7 @@ function adaptCommunityComponent(component) {
30461
30693
  polylines: component.symbol.polylines,
30462
30694
  polygons: component.symbol.polygons,
30463
30695
  paths: component.symbol.paths,
30696
+ texts: component.symbol.texts || [],
30464
30697
  origin: component.symbol.origin
30465
30698
  },
30466
30699
  footprint: {
@@ -30484,11 +30717,11 @@ function adaptCommunityComponent(component) {
30484
30717
  function createLibraryService() {
30485
30718
  return {
30486
30719
  async install(id, options = {}) {
30487
- const isCommunityComponent = !isLcscId2(id);
30720
+ const isCommunityComponent = !isLcscId3(id);
30488
30721
  const isGlobal = !options.projectPath;
30489
30722
  const paths = isGlobal ? getGlobalLibraryPaths() : getProjectLibraryPaths(options.projectPath);
30490
30723
  let component = null;
30491
- if (isLcscId2(id)) {
30724
+ if (isLcscId3(id)) {
30492
30725
  component = await easyedaClient.getComponentData(id);
30493
30726
  } else {
30494
30727
  const communityComponent = await easyedaCommunityClient.getComponent(id);
@@ -30537,18 +30770,18 @@ function createLibraryService() {
30537
30770
  let easyedaModelsDir;
30538
30771
  if (isGlobal) {
30539
30772
  symbolsDir = paths.symbolsDir;
30540
- footprintDir = join4(paths.footprintsDir, fpLibDir);
30541
- easyedaModelsDir = join4(paths.models3dDir, models3dDirName);
30773
+ footprintDir = join5(paths.footprintsDir, fpLibDir);
30774
+ easyedaModelsDir = join5(paths.models3dDir, models3dDirName);
30542
30775
  } else {
30543
- const librariesDir = join4(options.projectPath, "libraries");
30544
- symbolsDir = join4(librariesDir, "symbols");
30545
- footprintDir = join4(librariesDir, "footprints", fpLibDir);
30546
- easyedaModelsDir = join4(librariesDir, "3dmodels", models3dDirName);
30776
+ const librariesDir = join5(options.projectPath, "libraries");
30777
+ symbolsDir = join5(librariesDir, "symbols");
30778
+ footprintDir = join5(librariesDir, "footprints", fpLibDir);
30779
+ easyedaModelsDir = join5(librariesDir, "3dmodels", models3dDirName);
30547
30780
  }
30548
30781
  models3dDir = easyedaModelsDir;
30549
30782
  await ensureDir(symbolsDir);
30550
30783
  await ensureDir(footprintDir);
30551
- symbolFile = join4(symbolsDir, symLibFile);
30784
+ symbolFile = join5(symbolsDir, symLibFile);
30552
30785
  symbolName = component.info.name.replace(/[^a-zA-Z0-9_-]/g, "_");
30553
30786
  const include3d = options.include3d ?? true;
30554
30787
  let modelRelativePath;
@@ -30557,7 +30790,7 @@ function createLibraryService() {
30557
30790
  const model = await easyedaCommunityClient.get3DModel(component.model3d.uuid, "step");
30558
30791
  if (model) {
30559
30792
  const modelFilename = `${symbolName}.step`;
30560
- modelPath = join4(models3dDir, modelFilename);
30793
+ modelPath = join5(models3dDir, modelFilename);
30561
30794
  await writeBinary(modelPath, model);
30562
30795
  if (isGlobal) {
30563
30796
  modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${models3dDirName}/${modelFilename}`;
@@ -30571,7 +30804,7 @@ function createLibraryService() {
30571
30804
  include3DModel: !!modelRelativePath,
30572
30805
  modelPath: modelRelativePath
30573
30806
  });
30574
- footprintPath = join4(footprintDir, `${symbolName}.kicad_mod`);
30807
+ footprintPath = join5(footprintDir, `${symbolName}.kicad_mod`);
30575
30808
  footprintRef = `${libName}:${symbolName}`;
30576
30809
  await writeText(footprintPath, footprint);
30577
30810
  component.info.package = footprintRef;
@@ -30586,20 +30819,20 @@ function createLibraryService() {
30586
30819
  } else {
30587
30820
  category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
30588
30821
  const symbolLibraryFilename = getLibraryFilename(category);
30589
- symbolFile = join4(paths.symbolsDir, symbolLibraryFilename);
30822
+ symbolFile = join5(paths.symbolsDir, symbolLibraryFilename);
30590
30823
  footprintDir = paths.footprintDir;
30591
30824
  models3dDir = paths.models3dFullDir;
30592
30825
  await ensureDir(paths.symbolsDir);
30593
30826
  await ensureDir(paths.footprintDir);
30594
30827
  symbolName = symbolConverter.getSymbolName(component);
30595
- const include3d = options.include3d ?? false;
30828
+ const include3d = options.include3d ?? true;
30596
30829
  let modelRelativePath;
30597
30830
  if (include3d && component.model3d) {
30598
30831
  await ensureDir(models3dDir);
30599
30832
  const model = await easyedaClient.get3DModel(component.model3d.uuid, "step");
30600
30833
  if (model) {
30601
30834
  const modelFilename = `${symbolName}.step`;
30602
- modelPath = join4(models3dDir, modelFilename);
30835
+ modelPath = join5(models3dDir, modelFilename);
30603
30836
  await writeBinary(modelPath, model);
30604
30837
  modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${MODELS_3D_NAME}/${modelFilename}`;
30605
30838
  }
@@ -30611,7 +30844,7 @@ function createLibraryService() {
30611
30844
  if (footprintResult.type === "reference") {
30612
30845
  footprintRef = footprintResult.reference;
30613
30846
  } else {
30614
- footprintPath = join4(footprintDir, `${footprintResult.name}.kicad_mod`);
30847
+ footprintPath = join5(footprintDir, `${footprintResult.name}.kicad_mod`);
30615
30848
  footprintRef = getFootprintReference(footprintResult.name);
30616
30849
  await writeText(footprintPath, footprintResult.content);
30617
30850
  }
@@ -30624,7 +30857,7 @@ function createLibraryService() {
30624
30857
  }
30625
30858
  let symbolContent;
30626
30859
  let symbolAction;
30627
- if (existsSync3(symbolFile)) {
30860
+ if (existsSync4(symbolFile)) {
30628
30861
  const existingContent = await readFile4(symbolFile, "utf-8");
30629
30862
  if (symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
30630
30863
  if (options.force) {
@@ -30716,9 +30949,9 @@ function createLibraryService() {
30716
30949
  if (options.category && options.category !== category)
30717
30950
  continue;
30718
30951
  const libraryFilename = getLibraryFilename(category);
30719
- const libraryPath = join4(paths.symbolsDir, libraryFilename);
30952
+ const libraryPath = join5(paths.symbolsDir, libraryFilename);
30720
30953
  const libraryName = `JLC-MCP-${category}`;
30721
- if (existsSync3(libraryPath)) {
30954
+ if (existsSync4(libraryPath)) {
30722
30955
  const components = await parseSymbolLibrary(libraryPath, libraryName, category, paths.models3dFullDir);
30723
30956
  allComponents.push(...components);
30724
30957
  }
@@ -30733,13 +30966,13 @@ function createLibraryService() {
30733
30966
  await ensureGlobalLibraryTables2();
30734
30967
  },
30735
30968
  async getStatus() {
30736
- const version2 = detectKicadVersion2();
30969
+ const version2 = detectKicadVersion();
30737
30970
  const paths = getGlobalLibraryPaths();
30738
- const configDir = getKicadConfigDir2(version2);
30739
- const symLibTablePath = join4(configDir, "sym-lib-table");
30740
- const fpLibTablePath = join4(configDir, "fp-lib-table");
30971
+ const configDir = getKicadConfigDir3(version2);
30972
+ const symLibTablePath = join5(configDir, "sym-lib-table");
30973
+ const fpLibTablePath = join5(configDir, "fp-lib-table");
30741
30974
  let installed = false;
30742
- if (existsSync3(paths.symbolsDir)) {
30975
+ if (existsSync4(paths.symbolsDir)) {
30743
30976
  try {
30744
30977
  const files = await readdir2(paths.symbolsDir);
30745
30978
  installed = files.some((f) => f.endsWith(".kicad_sym"));
@@ -30748,7 +30981,7 @@ function createLibraryService() {
30748
30981
  }
30749
30982
  }
30750
30983
  let linked = false;
30751
- if (existsSync3(symLibTablePath)) {
30984
+ if (existsSync4(symLibTablePath)) {
30752
30985
  try {
30753
30986
  const content = await readFile4(symLibTablePath, "utf-8");
30754
30987
  linked = content.includes("JLC-MCP");
@@ -30774,8 +31007,8 @@ function createLibraryService() {
30774
31007
  },
30775
31008
  async isEasyEDAInstalled(componentName) {
30776
31009
  const paths = getGlobalLibraryPaths();
30777
- const easyedaLibPath = join4(paths.symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME2);
30778
- if (!existsSync3(easyedaLibPath)) {
31010
+ const easyedaLibPath = join5(paths.symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME2);
31011
+ if (!existsSync4(easyedaLibPath)) {
30779
31012
  return false;
30780
31013
  }
30781
31014
  try {
@@ -30786,6 +31019,85 @@ function createLibraryService() {
30786
31019
  } catch {
30787
31020
  return false;
30788
31021
  }
31022
+ },
31023
+ async regenerate(options = {}) {
31024
+ const include3d = options.include3d ?? true;
31025
+ const installed = await this.listInstalled({ projectPath: options.projectPath });
31026
+ const results = {
31027
+ total: installed.length,
31028
+ success: 0,
31029
+ failed: 0,
31030
+ components: []
31031
+ };
31032
+ for (let i = 0;i < installed.length; i++) {
31033
+ const component = installed[i];
31034
+ options.onProgress?.(i + 1, installed.length, component, "start");
31035
+ try {
31036
+ await this.install(component.lcscId, {
31037
+ projectPath: options.projectPath,
31038
+ include3d,
31039
+ force: true
31040
+ });
31041
+ results.success++;
31042
+ results.components.push({
31043
+ id: component.lcscId,
31044
+ name: component.name,
31045
+ status: "success"
31046
+ });
31047
+ options.onProgress?.(i + 1, installed.length, component, "success");
31048
+ } catch (error2) {
31049
+ const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
31050
+ results.failed++;
31051
+ results.components.push({
31052
+ id: component.lcscId,
31053
+ name: component.name,
31054
+ status: "failed",
31055
+ error: errorMessage
31056
+ });
31057
+ options.onProgress?.(i + 1, installed.length, component, "error", errorMessage);
31058
+ }
31059
+ }
31060
+ return results;
31061
+ },
31062
+ async remove(lcscId, options = {}) {
31063
+ const paths = options.projectPath ? getProjectLibraryPaths(options.projectPath) : getGlobalLibraryPaths();
31064
+ const installed = await this.listInstalled({ projectPath: options.projectPath });
31065
+ const component = installed.find((c) => c.lcscId === lcscId);
31066
+ if (!component) {
31067
+ throw new Error(`Component ${lcscId} not found in library`);
31068
+ }
31069
+ const result = {
31070
+ success: false,
31071
+ lcscId,
31072
+ symbolRemoved: false,
31073
+ footprintRemoved: false,
31074
+ model3dRemoved: false
31075
+ };
31076
+ const libraryFilename = getLibraryFilename(component.category);
31077
+ const symbolLibPath = join5(paths.symbolsDir, libraryFilename);
31078
+ if (existsSync4(symbolLibPath)) {
31079
+ const content = await readFile4(symbolLibPath, "utf-8");
31080
+ const newContent = removeSymbolFromLibrary(content, component.name);
31081
+ await writeText(symbolLibPath, newContent);
31082
+ result.symbolRemoved = true;
31083
+ }
31084
+ if (component.footprintRef?.startsWith("JLC-MCP:")) {
31085
+ const fpName = component.footprintRef.split(":")[1];
31086
+ const footprintPath = join5(paths.footprintDir, `${fpName}.kicad_mod`);
31087
+ if (existsSync4(footprintPath)) {
31088
+ await unlink(footprintPath);
31089
+ result.footprintRemoved = true;
31090
+ }
31091
+ }
31092
+ if (component.has3dModel) {
31093
+ const modelPath = join5(paths.models3dFullDir, `${component.name}.step`);
31094
+ if (existsSync4(modelPath)) {
31095
+ await unlink(modelPath);
31096
+ result.model3dRemoved = true;
31097
+ }
31098
+ }
31099
+ result.success = result.symbolRemoved;
31100
+ return result;
30789
31101
  }
30790
31102
  };
30791
31103
  }
@@ -30801,16 +31113,16 @@ function getHtmlPage() {
30801
31113
  return htmlCache;
30802
31114
  try {
30803
31115
  const possiblePaths = [
30804
- join5(__dirname2, "assets/search.html"),
30805
- join5(__dirname2, "../dist/assets/search.html"),
30806
- join5(__dirname2, "../assets/search.html"),
30807
- join5(__dirname2, "../assets/search-built.html"),
30808
- join5(process.cwd(), "dist/assets/search.html"),
30809
- join5(process.cwd(), "packages/core/dist/assets/search.html"),
30810
- join5(__dirname2, "../../dist/assets/search.html"),
30811
- join5(__dirname2, "../../../core/dist/assets/search.html"),
30812
- join5(__dirname2, "../../../../packages/core/dist/assets/search.html"),
30813
- join5(__dirname2, "../../node_modules/@jlcpcb/core/dist/assets/search.html")
31116
+ join6(__dirname2, "assets/search.html"),
31117
+ join6(__dirname2, "../dist/assets/search.html"),
31118
+ join6(__dirname2, "../assets/search.html"),
31119
+ join6(__dirname2, "../assets/search-built.html"),
31120
+ join6(process.cwd(), "dist/assets/search.html"),
31121
+ join6(process.cwd(), "packages/core/dist/assets/search.html"),
31122
+ join6(__dirname2, "../../dist/assets/search.html"),
31123
+ join6(__dirname2, "../../../core/dist/assets/search.html"),
31124
+ join6(__dirname2, "../../../../packages/core/dist/assets/search.html"),
31125
+ join6(__dirname2, "../../node_modules/@jlcpcb/core/dist/assets/search.html")
30814
31126
  ];
30815
31127
  for (const path of possiblePaths) {
30816
31128
  try {
@@ -31001,17 +31313,51 @@ function startHttpServer(options = {}) {
31001
31313
  });
31002
31314
  return port;
31003
31315
  }
31316
+ var EE_TO_MM3 = 10 * 0.0254;
31317
+ var EE_TO_MM4 = 10 * 0.0254;
31318
+ var EE_TO_MM5 = 10 * 0.0254;
31319
+
31320
+ // src/services.ts
31321
+ var componentService = null;
31322
+ var libraryService = null;
31323
+ function getComponentService() {
31324
+ if (!componentService) {
31325
+ componentService = createComponentService();
31326
+ }
31327
+ return componentService;
31328
+ }
31329
+ function getLibraryService() {
31330
+ if (!libraryService) {
31331
+ libraryService = createLibraryService();
31332
+ }
31333
+ return libraryService;
31334
+ }
31004
31335
 
31005
31336
  // src/tools/search.ts
31006
- var searchComponentsTool = {
31337
+ var componentSearchTool = {
31007
31338
  name: "component_search",
31008
- description: "Search the JLC/JLCPCB component database by keyword. Returns components with LCSC part numbers (JLC's preferred supplier for assembly). Includes prices, stock levels, and whether parts are in the Basic Parts Library (lower assembly cost).",
31339
+ description: `Search for electronic components by keyword.
31340
+
31341
+ Sources:
31342
+ - "lcsc" (default): Search JLCPCB/LCSC official parts library. Returns components with LCSC IDs available for JLC assembly.
31343
+ - "community": Search EasyEDA community library for user-contributed parts (Arduino modules, XIAO, custom breakouts).
31344
+
31345
+ Returns full component details for selection including:
31346
+ - LCSC ID, manufacturer part number, manufacturer name
31347
+ - Description, package, datasheet URL
31348
+ - Price, stock, library type (basic/extended)
31349
+ - Key attributes (resistance, voltage, etc.)`,
31009
31350
  inputSchema: {
31010
31351
  type: "object",
31011
31352
  properties: {
31012
31353
  query: {
31013
31354
  type: "string",
31014
- description: 'Search query (e.g., "ESP32", "STM32F103", "0805 100nF")'
31355
+ description: 'Search query (e.g., "ESP32", "STM32F103", "0805 100nF", "XIAO RP2040")'
31356
+ },
31357
+ source: {
31358
+ type: "string",
31359
+ enum: ["lcsc", "community"],
31360
+ description: 'Search source: "lcsc" for official parts (default), "community" for EasyEDA user-contributed'
31015
31361
  },
31016
31362
  limit: {
31017
31363
  type: "number",
@@ -31019,11 +31365,11 @@ var searchComponentsTool = {
31019
31365
  },
31020
31366
  in_stock: {
31021
31367
  type: "boolean",
31022
- description: "Only show in-stock items (default: false)"
31368
+ description: "Only show in-stock items (default: false, LCSC only)"
31023
31369
  },
31024
31370
  basic_only: {
31025
31371
  type: "boolean",
31026
- description: "Only show JLCPCB Basic Parts Library components (lower assembly cost, default: false)"
31372
+ description: "Only show JLCPCB Basic Parts Library components (lower assembly cost, LCSC only)"
31027
31373
  }
31028
31374
  },
31029
31375
  required: ["query"]
@@ -31031,112 +31377,64 @@ var searchComponentsTool = {
31031
31377
  };
31032
31378
  var SearchParamsSchema = exports_external.object({
31033
31379
  query: exports_external.string().min(1),
31380
+ source: exports_external.enum(["lcsc", "community"]).default("lcsc"),
31034
31381
  limit: exports_external.number().min(1).max(50).default(10),
31035
31382
  in_stock: exports_external.boolean().optional(),
31036
31383
  basic_only: exports_external.boolean().optional()
31037
31384
  });
31038
- async function handleSearchComponents(args) {
31385
+ async function handleComponentSearch(args) {
31039
31386
  const params = SearchParamsSchema.parse(args);
31040
- const results = await jlcClient.search(params.query, {
31387
+ const results = await getComponentService().search(params.query, {
31388
+ source: params.source,
31041
31389
  limit: params.limit,
31042
31390
  inStock: params.in_stock,
31043
31391
  basicOnly: params.basic_only
31044
31392
  });
31045
- return {
31046
- content: [{
31047
- type: "text",
31048
- text: JSON.stringify(results, null, 2)
31049
- }]
31050
- };
31051
- }
31052
-
31053
- // src/tools/details.ts
31054
- var getComponentTool = {
31055
- name: "component_get",
31056
- description: "Get detailed component information by LCSC part number (e.g., C2040). LCSC is JLC PCB's preferred supplier - components with LCSC IDs are available for JLC assembly. Returns symbol pins, footprint pads, manufacturer info, datasheet URL, and 3D model reference.",
31057
- inputSchema: {
31058
- type: "object",
31059
- properties: {
31060
- lcsc_id: {
31061
- type: "string",
31062
- description: "LCSC part number (e.g., C2040, C14663)"
31063
- }
31064
- },
31065
- required: ["lcsc_id"]
31066
- }
31067
- };
31068
- var GetComponentParamsSchema = exports_external.object({
31069
- lcsc_id: exports_external.string().regex(/^C\d+$/, "Invalid LCSC part number")
31070
- });
31071
- async function handleGetComponent(args) {
31072
- const params = GetComponentParamsSchema.parse(args);
31073
- const component = await easyedaClient.getComponentData(params.lcsc_id);
31074
- if (!component) {
31393
+ if (results.length === 0) {
31075
31394
  return {
31076
31395
  content: [{
31077
31396
  type: "text",
31078
- text: `Component ${params.lcsc_id} not found`
31079
- }],
31080
- isError: true
31397
+ text: JSON.stringify({
31398
+ success: true,
31399
+ query: params.query,
31400
+ source: params.source,
31401
+ count: 0,
31402
+ results: [],
31403
+ hint: params.source === "lcsc" ? 'Try searching with source: "community" for user-contributed parts' : 'Try searching with source: "lcsc" for official JLCPCB parts'
31404
+ })
31405
+ }]
31081
31406
  };
31082
31407
  }
31083
- const summary = {
31084
- info: component.info,
31085
- symbol: {
31086
- pin_count: component.symbol.pins.length,
31087
- pins: component.symbol.pins
31088
- },
31089
- footprint: {
31090
- name: component.footprint.name,
31091
- type: component.footprint.type,
31092
- pad_count: component.footprint.pads.length,
31093
- pads: component.footprint.pads
31094
- },
31095
- has_3d_model: !!component.model3d,
31096
- model_3d_uuid: component.model3d?.uuid
31097
- };
31408
+ const compactResults = results.map((r) => ({
31409
+ lcsc_id: r.lcscId,
31410
+ name: r.name,
31411
+ manufacturer: r.manufacturer,
31412
+ description: r.description,
31413
+ package: r.package,
31414
+ datasheet: r.datasheetPdf,
31415
+ stock: r.stock,
31416
+ price: r.price,
31417
+ library_type: r.libraryType,
31418
+ category: r.category,
31419
+ attributes: r.attributes
31420
+ }));
31098
31421
  return {
31099
31422
  content: [{
31100
31423
  type: "text",
31101
- text: JSON.stringify(summary, null, 2)
31424
+ text: JSON.stringify({
31425
+ success: true,
31426
+ query: params.query,
31427
+ source: params.source,
31428
+ count: results.length,
31429
+ results: compactResults
31430
+ })
31102
31431
  }]
31103
31432
  };
31104
31433
  }
31105
-
31106
31434
  // src/tools/library.ts
31107
- var libraryService = createLibraryService();
31108
- var componentService = createComponentService();
31109
- var getSymbolKicadTool = {
31110
- name: "library_get_symbol",
31111
- description: "Get a KiCad-compatible symbol definition by LCSC part number. Returns the symbol in .kicad_sym format. LCSC is JLC PCB's preferred supplier for assembly.",
31112
- inputSchema: {
31113
- type: "object",
31114
- properties: {
31115
- lcsc_id: {
31116
- type: "string",
31117
- description: "LCSC part number (e.g., C2040)"
31118
- }
31119
- },
31120
- required: ["lcsc_id"]
31121
- }
31122
- };
31123
- var getFootprintKicadTool = {
31124
- name: "library_get_footprint",
31125
- description: "Get a KiCad-compatible footprint definition by LCSC part number. Returns the footprint in .kicad_mod format. LCSC is JLC PCB's preferred supplier for assembly.",
31126
- inputSchema: {
31127
- type: "object",
31128
- properties: {
31129
- lcsc_id: {
31130
- type: "string",
31131
- description: "LCSC part number (e.g., C2040)"
31132
- }
31133
- },
31134
- required: ["lcsc_id"]
31135
- }
31136
- };
31137
- var fetchLibraryTool = {
31138
- name: "library_fetch",
31139
- description: `Fetch a component and add it to KiCad libraries.
31435
+ var libraryInstallTool = {
31436
+ name: "library_install",
31437
+ description: `Install a component to KiCad libraries.
31140
31438
 
31141
31439
  Accepts:
31142
31440
  - LCSC part numbers (e.g., C2040) → global JLC-MCP libraries
@@ -31146,11 +31444,7 @@ LCSC components are routed to category-based global libraries:
31146
31444
  - JLC-MCP-Resistors.kicad_sym, JLC-MCP-Capacitors.kicad_sym, JLC-MCP-ICs.kicad_sym, etc.
31147
31445
  - Stored at ~/Documents/KiCad/{version}/3rdparty/jlc_mcp/
31148
31446
 
31149
- EasyEDA community components are stored project-locally:
31150
- - <project>/libraries/symbols/EasyEDA.kicad_sym
31151
- - <project>/libraries/footprints/EasyEDA.pretty/
31152
-
31153
- Returns symbol_ref and footprint_ref for immediate use with add_schematic_component.`,
31447
+ Returns symbol_ref and footprint_ref for use with schematic placement.`,
31154
31448
  inputSchema: {
31155
31449
  type: "object",
31156
31450
  properties: {
@@ -31164,89 +31458,47 @@ Returns symbol_ref and footprint_ref for immediate use with add_schematic_compon
31164
31458
  },
31165
31459
  include_3d: {
31166
31460
  type: "boolean",
31167
- description: "Include 3D model if available (default: false for LCSC, true for EasyEDA)"
31461
+ description: "Include 3D model if available (default: false)"
31462
+ },
31463
+ force: {
31464
+ type: "boolean",
31465
+ description: "Reinstall even if already exists (default: false)"
31168
31466
  }
31169
31467
  },
31170
31468
  required: ["id"]
31171
31469
  }
31172
31470
  };
31173
- var get3DModelTool = {
31174
- name: "library_get_3d_model",
31175
- description: "Download a 3D model for a component. Requires the model UUID from component_get. Returns the model as base64-encoded STEP data.",
31471
+ var libraryGetComponentTool = {
31472
+ name: "library_get_component",
31473
+ description: `Get metadata for an installed component's symbol and footprint.
31474
+
31475
+ Returns symbol reference, footprint reference, file paths, and pin/pad counts.
31476
+ Does NOT return full file contents to minimize token usage.
31477
+
31478
+ Use this to verify installation or get references for schematic placement.`,
31176
31479
  inputSchema: {
31177
31480
  type: "object",
31178
31481
  properties: {
31179
- uuid: {
31180
- type: "string",
31181
- description: "3D model UUID from component_get result"
31182
- },
31183
- format: {
31482
+ id: {
31184
31483
  type: "string",
31185
- enum: ["step", "obj"],
31186
- description: 'Model format: "step" or "obj" (default: step)'
31484
+ description: "LCSC part number (e.g., C2040)"
31187
31485
  }
31188
31486
  },
31189
- required: ["uuid"]
31487
+ required: ["id"]
31190
31488
  }
31191
31489
  };
31192
- var LibraryParamsSchema = exports_external.object({
31193
- lcsc_id: exports_external.string().regex(/^C\d+$/, "Invalid LCSC part number")
31194
- });
31195
- var FetchLibraryParamsSchema = exports_external.object({
31490
+ var LibraryInstallParamsSchema = exports_external.object({
31196
31491
  id: exports_external.string().min(1),
31197
- project_path: exports_external.string().min(1).optional(),
31198
- include_3d: exports_external.boolean().optional()
31492
+ project_path: SafePathSchema.optional(),
31493
+ include_3d: exports_external.boolean().optional(),
31494
+ force: exports_external.boolean().optional()
31199
31495
  });
31200
- var Model3DParamsSchema = exports_external.object({
31201
- uuid: exports_external.string().min(1),
31202
- format: exports_external.enum(["step", "obj"]).default("step")
31496
+ var LibraryGetComponentParamsSchema = exports_external.object({
31497
+ id: LCSCPartNumberSchema
31203
31498
  });
31204
- async function handleGetSymbolKicad(args) {
31205
- const params = LibraryParamsSchema.parse(args);
31206
- const component = await easyedaClient.getComponentData(params.lcsc_id);
31207
- if (!component) {
31208
- return {
31209
- content: [{
31210
- type: "text",
31211
- text: `Component ${params.lcsc_id} not found`
31212
- }],
31213
- isError: true
31214
- };
31215
- }
31216
- const symbol = symbolConverter.convert(component);
31217
- return {
31218
- content: [{
31219
- type: "text",
31220
- text: symbol
31221
- }]
31222
- };
31223
- }
31224
- async function handleGetFootprintKicad(args) {
31225
- const params = LibraryParamsSchema.parse(args);
31226
- const component = await easyedaClient.getComponentData(params.lcsc_id);
31227
- if (!component) {
31228
- return {
31229
- content: [{
31230
- type: "text",
31231
- text: `Component ${params.lcsc_id} not found`
31232
- }],
31233
- isError: true
31234
- };
31235
- }
31236
- const footprint = footprintConverter.convert(component);
31237
- return {
31238
- content: [{
31239
- type: "text",
31240
- text: footprint
31241
- }]
31242
- };
31243
- }
31244
- function isLcscId3(id) {
31245
- return /^C\d+$/.test(id);
31246
- }
31247
- async function handleFetchLibrary(args) {
31248
- const params = FetchLibraryParamsSchema.parse(args);
31249
- const isCommunityComponent = !isLcscId3(params.id);
31499
+ async function handleLibraryInstall(args) {
31500
+ const params = LibraryInstallParamsSchema.parse(args);
31501
+ const isCommunityComponent = !isLcscId(params.id);
31250
31502
  if (isCommunityComponent && !params.project_path) {
31251
31503
  return {
31252
31504
  content: [{
@@ -31262,9 +31514,10 @@ async function handleFetchLibrary(args) {
31262
31514
  };
31263
31515
  }
31264
31516
  try {
31265
- const result = await libraryService.install(params.id, {
31517
+ const result = await getLibraryService().install(params.id, {
31266
31518
  projectPath: params.project_path,
31267
- include3d: params.include_3d
31519
+ include3d: params.include_3d,
31520
+ force: params.force
31268
31521
  });
31269
31522
  return {
31270
31523
  content: [{
@@ -31272,22 +31525,23 @@ async function handleFetchLibrary(args) {
31272
31525
  text: JSON.stringify({
31273
31526
  success: true,
31274
31527
  id: params.id,
31275
- source: result.source,
31276
- storage_mode: result.storageMode,
31277
- category: result.category,
31278
- symbol_name: result.symbolName,
31528
+ installed: true,
31279
31529
  symbol_ref: result.symbolRef,
31280
31530
  footprint_ref: result.footprintRef,
31281
- footprint_type: result.footprintType,
31282
- datasheet: result.datasheet,
31531
+ category: result.category,
31283
31532
  files: {
31284
31533
  symbol_library: result.files.symbolLibrary,
31285
31534
  footprint: result.files.footprint,
31286
31535
  model_3d: result.files.model3d
31287
31536
  },
31288
- symbol_action: result.symbolAction,
31289
- validation_data: result.validationData
31290
- }, null, 2)
31537
+ validation: result.validationData ? {
31538
+ pin_pad_match: result.validationData.checks.pin_pad_count_match,
31539
+ pin_count: result.validationData.symbol.pin_count,
31540
+ pad_count: result.validationData.footprint.pad_count,
31541
+ has_power_pins: result.validationData.checks.has_power_pins,
31542
+ has_ground_pins: result.validationData.checks.has_ground_pins
31543
+ } : undefined
31544
+ })
31291
31545
  }]
31292
31546
  };
31293
31547
  } catch (error2) {
@@ -31297,76 +31551,202 @@ async function handleFetchLibrary(args) {
31297
31551
  text: JSON.stringify({
31298
31552
  success: false,
31299
31553
  error: error2 instanceof Error ? error2.message : "Unknown error",
31300
- id: params.id,
31301
- source: isCommunityComponent ? "easyeda_community" : "lcsc"
31554
+ id: params.id
31302
31555
  })
31303
31556
  }],
31304
31557
  isError: true
31305
31558
  };
31306
31559
  }
31307
31560
  }
31308
- async function handleGet3DModel(args) {
31309
- const params = Model3DParamsSchema.parse(args);
31310
- const model = await easyedaClient.get3DModel(params.uuid, params.format);
31311
- if (!model) {
31561
+ async function handleLibraryGetComponent(args) {
31562
+ const params = LibraryGetComponentParamsSchema.parse(args);
31563
+ try {
31564
+ const status = await getLibraryService().getStatus();
31565
+ if (!status.installed) {
31566
+ return {
31567
+ content: [{
31568
+ type: "text",
31569
+ text: JSON.stringify({
31570
+ success: false,
31571
+ error: "JLC-MCP libraries not installed. Run library_install first.",
31572
+ id: params.id
31573
+ })
31574
+ }],
31575
+ isError: true
31576
+ };
31577
+ }
31578
+ const installed = await getLibraryService().listInstalled();
31579
+ const component = installed.find((c) => c.lcscId === params.id);
31580
+ if (!component) {
31581
+ return {
31582
+ content: [{
31583
+ type: "text",
31584
+ text: JSON.stringify({
31585
+ success: false,
31586
+ error: `Component ${params.id} is not installed`,
31587
+ id: params.id,
31588
+ hint: "Use library_install to add this component first"
31589
+ })
31590
+ }],
31591
+ isError: true
31592
+ };
31593
+ }
31594
+ return {
31595
+ content: [{
31596
+ type: "text",
31597
+ text: JSON.stringify({
31598
+ success: true,
31599
+ id: params.id,
31600
+ installed: true,
31601
+ symbol_ref: component.symbolRef,
31602
+ footprint_ref: component.footprintRef,
31603
+ category: component.category,
31604
+ symbol_library: component.library,
31605
+ name: component.name,
31606
+ has_3d_model: component.has3dModel
31607
+ })
31608
+ }]
31609
+ };
31610
+ } catch (error2) {
31312
31611
  return {
31313
31612
  content: [{
31314
31613
  type: "text",
31315
- text: `3D model ${params.uuid} not found`
31614
+ text: JSON.stringify({
31615
+ success: false,
31616
+ error: error2 instanceof Error ? error2.message : "Unknown error",
31617
+ id: params.id
31618
+ })
31316
31619
  }],
31317
31620
  isError: true
31318
31621
  };
31319
31622
  }
31623
+ }
31624
+
31625
+ // src/tools/batch.ts
31626
+ var libraryBatchInstallTool = {
31627
+ name: "library_batch_install",
31628
+ description: `Install multiple components to KiCad libraries in a single call.
31629
+
31630
+ Accepts up to 10 LCSC part numbers. Components are installed in parallel.
31631
+ Returns a summary of installed, skipped (already installed), and failed components.
31632
+
31633
+ Use this when you need to install a bill of materials or multiple components at once.`,
31634
+ inputSchema: {
31635
+ type: "object",
31636
+ properties: {
31637
+ ids: {
31638
+ type: "array",
31639
+ items: { type: "string" },
31640
+ maxItems: 10,
31641
+ description: 'Array of LCSC part numbers (e.g., ["C2040", "C5446", "C14663"])'
31642
+ },
31643
+ force: {
31644
+ type: "boolean",
31645
+ description: "Reinstall even if already exists (default: false)"
31646
+ },
31647
+ include_3d: {
31648
+ type: "boolean",
31649
+ description: "Include 3D models if available (default: false)"
31650
+ }
31651
+ },
31652
+ required: ["ids"]
31653
+ }
31654
+ };
31655
+ var BatchInstallParamsSchema = exports_external.object({
31656
+ ids: exports_external.array(LCSCPartNumberSchema).min(1).max(10),
31657
+ force: exports_external.boolean().optional(),
31658
+ include_3d: exports_external.boolean().optional()
31659
+ });
31660
+ async function handleLibraryBatchInstall(args) {
31661
+ const params = BatchInstallParamsSchema.parse(args);
31662
+ const results = [];
31663
+ let installed = 0;
31664
+ let skipped = 0;
31665
+ let failed = 0;
31666
+ const installPromises = params.ids.map(async (id) => {
31667
+ try {
31668
+ const result = await getLibraryService().install(id, {
31669
+ include3d: params.include_3d,
31670
+ force: params.force
31671
+ });
31672
+ if (result.symbolAction === "exists") {
31673
+ return {
31674
+ id,
31675
+ status: "skipped",
31676
+ symbol_ref: result.symbolRef,
31677
+ footprint_ref: result.footprintRef,
31678
+ reason: "already installed"
31679
+ };
31680
+ }
31681
+ return {
31682
+ id,
31683
+ status: "installed",
31684
+ symbol_ref: result.symbolRef,
31685
+ footprint_ref: result.footprintRef
31686
+ };
31687
+ } catch (error2) {
31688
+ return {
31689
+ id,
31690
+ status: "failed",
31691
+ error: error2 instanceof Error ? error2.message : "Unknown error"
31692
+ };
31693
+ }
31694
+ });
31695
+ const installResults = await Promise.all(installPromises);
31696
+ for (const result of installResults) {
31697
+ results.push(result);
31698
+ if (result.status === "installed")
31699
+ installed++;
31700
+ else if (result.status === "skipped")
31701
+ skipped++;
31702
+ else
31703
+ failed++;
31704
+ }
31320
31705
  return {
31321
31706
  content: [{
31322
31707
  type: "text",
31323
- text: `3D model downloaded (${model.length} bytes, ${params.format.toUpperCase()} format)
31324
-
31325
- Base64 data:
31326
- ${model.toString("base64").slice(0, 500)}...`
31708
+ text: JSON.stringify({
31709
+ success: failed < params.ids.length,
31710
+ summary: {
31711
+ total: params.ids.length,
31712
+ installed,
31713
+ skipped,
31714
+ failed
31715
+ },
31716
+ results
31717
+ })
31327
31718
  }]
31328
31719
  };
31329
31720
  }
31330
31721
 
31331
31722
  // src/tools/library-update.ts
31332
- import { existsSync as existsSync4, readdirSync } from "fs";
31723
+ import { existsSync as existsSync5, readdirSync } from "fs";
31333
31724
  import { readFile as readFile5 } from "fs/promises";
31334
- import { homedir as homedir3, platform as platform3 } from "os";
31335
- import { join as join6 } from "path";
31336
- var KICAD_VERSIONS = ["9.0", "8.0"];
31725
+ import { homedir as homedir4, platform as platform4 } from "os";
31726
+ import { join as join7 } from "path";
31337
31727
  var LIBRARY_NAMESPACE3 = "jlc_mcp";
31338
- function detectKicadVersion3() {
31339
- const home = homedir3();
31340
- const baseDir = join6(home, "Documents", "KiCad");
31341
- for (const version2 of KICAD_VERSIONS) {
31342
- if (existsSync4(join6(baseDir, version2))) {
31343
- return version2;
31344
- }
31345
- }
31346
- return "9.0";
31347
- }
31348
31728
  function getLibraryPaths(projectPath) {
31349
31729
  if (projectPath) {
31350
- const librariesDir = join6(projectPath, "libraries");
31730
+ const librariesDir = join7(projectPath, "libraries");
31351
31731
  return {
31352
- symbolsDir: join6(librariesDir, "symbols"),
31353
- footprintDir: join6(librariesDir, "footprints", getFootprintDirName()),
31354
- models3dDir: join6(librariesDir, "3dmodels", get3DModelsDirName())
31732
+ symbolsDir: join7(librariesDir, "symbols"),
31733
+ footprintDir: join7(librariesDir, "footprints", getFootprintDirName()),
31734
+ models3dDir: join7(librariesDir, "3dmodels", get3DModelsDirName())
31355
31735
  };
31356
31736
  }
31357
- const home = homedir3();
31358
- const version2 = detectKicadVersion3();
31359
- const plat = platform3();
31737
+ const home = homedir4();
31738
+ const version2 = detectKicadVersion();
31739
+ const plat = platform4();
31360
31740
  let base;
31361
31741
  if (plat === "linux") {
31362
- base = join6(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31742
+ base = join7(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31363
31743
  } else {
31364
- base = join6(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31744
+ base = join7(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE3);
31365
31745
  }
31366
31746
  return {
31367
- symbolsDir: join6(base, "symbols"),
31368
- footprintDir: join6(base, "footprints", getFootprintDirName()),
31369
- models3dDir: join6(base, "3dmodels", get3DModelsDirName())
31747
+ symbolsDir: join7(base, "symbols"),
31748
+ footprintDir: join7(base, "footprints", getFootprintDirName()),
31749
+ models3dDir: join7(base, "3dmodels", get3DModelsDirName())
31370
31750
  };
31371
31751
  }
31372
31752
  function extractLcscIdsFromLibrary(content) {
@@ -31387,12 +31767,12 @@ function generateEmptyLibrary() {
31387
31767
  `;
31388
31768
  }
31389
31769
  function findJlcLibraries(symbolsDir) {
31390
- if (!existsSync4(symbolsDir)) {
31770
+ if (!existsSync5(symbolsDir)) {
31391
31771
  return [];
31392
31772
  }
31393
31773
  try {
31394
31774
  const files = readdirSync(symbolsDir);
31395
- return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) => join6(symbolsDir, f));
31775
+ return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) => join7(symbolsDir, f));
31396
31776
  } catch {
31397
31777
  return [];
31398
31778
  }
@@ -31459,7 +31839,7 @@ async function handleUpdateLibrary(args) {
31459
31839
  libraries_to_create: allCategories.map((cat) => ({
31460
31840
  category: cat,
31461
31841
  filename: getLibraryFilename(cat),
31462
- path: join6(paths.symbolsDir, getLibraryFilename(cat))
31842
+ path: join7(paths.symbolsDir, getLibraryFilename(cat))
31463
31843
  }))
31464
31844
  }, null, 2)
31465
31845
  }]
@@ -31472,7 +31852,7 @@ async function handleUpdateLibrary(args) {
31472
31852
  const createdLibraries = [];
31473
31853
  for (const category of allCategories) {
31474
31854
  const filename = getLibraryFilename(category);
31475
- const filepath = join6(paths.symbolsDir, filename);
31855
+ const filepath = join7(paths.symbolsDir, filename);
31476
31856
  await writeText(filepath, emptyContent);
31477
31857
  createdLibraries.push(filepath);
31478
31858
  }
@@ -31549,7 +31929,7 @@ async function handleUpdateLibrary(args) {
31549
31929
  if (!params.dry_run && footprintResult.type === "generated") {
31550
31930
  await ensureDir(paths.footprintDir);
31551
31931
  const footprintName = footprintResult.name + "_" + lcscId;
31552
- const footprintPath = join6(paths.footprintDir, `${footprintName}.kicad_mod`);
31932
+ const footprintPath = join7(paths.footprintDir, `${footprintName}.kicad_mod`);
31553
31933
  await writeText(footprintPath, footprintResult.content);
31554
31934
  }
31555
31935
  results.push({
@@ -31576,7 +31956,7 @@ async function handleUpdateLibrary(args) {
31576
31956
  await ensureDir(paths.symbolsDir);
31577
31957
  for (const [category, entries] of categorySymbols) {
31578
31958
  const filename = getLibraryFilename(category);
31579
- const filepath = join6(paths.symbolsDir, filename);
31959
+ const filepath = join7(paths.symbolsDir, filename);
31580
31960
  const header = `(kicad_symbol_lib
31581
31961
  (version 20241209)
31582
31962
  (generator "jlc-mcp")
@@ -31604,53 +31984,41 @@ async function handleUpdateLibrary(args) {
31604
31984
  success: true,
31605
31985
  dry_run: params.dry_run,
31606
31986
  summary: {
31607
- total_components: allLcscIds.size,
31987
+ total: allLcscIds.size,
31608
31988
  updated: successful.length,
31609
- failed: failed.length,
31610
- libraries_generated: categorySymbols.size
31989
+ failed: failed.length
31611
31990
  },
31612
31991
  by_category: Object.fromEntries(byCategory),
31613
31992
  footprint_stats: footprintStats,
31614
- libraries_written: params.dry_run ? [] : Array.from(categorySymbols.keys()).map((cat) => join6(paths.symbolsDir, getLibraryFilename(cat))),
31615
- failed_components: failed.map((f) => ({
31993
+ failed_sample: failed.slice(0, 5).map((f) => ({
31616
31994
  lcsc_id: f.lcscId,
31617
31995
  error: f.error
31618
- }))
31619
- }, null, 2)
31996
+ })),
31997
+ has_more_failures: failed.length > 5
31998
+ })
31620
31999
  }]
31621
32000
  };
31622
32001
  }
31623
32002
 
31624
32003
  // src/tools/library-fix.ts
31625
- import { existsSync as existsSync6 } from "fs";
32004
+ import { existsSync as existsSync7 } from "fs";
31626
32005
  import { readFile as readFile7 } from "fs/promises";
31627
- import { homedir as homedir5 } from "os";
31628
- import { join as join8 } from "path";
31629
- var KICAD_VERSIONS4 = ["9.0", "8.0"];
31630
- function detectKicadVersion5() {
31631
- const home = homedir5();
31632
- const baseDir = join8(home, "Documents", "KiCad");
31633
- for (const version2 of KICAD_VERSIONS4) {
31634
- if (existsSync6(join8(baseDir, version2))) {
31635
- return version2;
31636
- }
31637
- }
31638
- return "9.0";
31639
- }
32006
+ import { homedir as homedir6 } from "os";
32007
+ import { join as join9 } from "path";
31640
32008
  function getLibraryPaths2(projectPath) {
31641
32009
  if (projectPath) {
31642
- const librariesDir = join8(projectPath, "libraries");
32010
+ const librariesDir = join9(projectPath, "libraries");
31643
32011
  return {
31644
- symbolsDir: join8(librariesDir, "symbols"),
31645
- footprintDir: join8(librariesDir, "footprints", getFootprintDirName())
32012
+ symbolsDir: join9(librariesDir, "symbols"),
32013
+ footprintDir: join9(librariesDir, "footprints", getFootprintDirName())
31646
32014
  };
31647
32015
  }
31648
- const home = homedir5();
31649
- const version2 = detectKicadVersion5();
31650
- const base = join8(home, "Documents", "KiCad", version2);
32016
+ const home = homedir6();
32017
+ const version2 = detectKicadVersion();
32018
+ const base = join9(home, "Documents", "KiCad", version2);
31651
32019
  return {
31652
- symbolsDir: join8(base, "symbols"),
31653
- footprintDir: join8(base, "footprints", getFootprintDirName())
32020
+ symbolsDir: join9(base, "symbols"),
32021
+ footprintDir: join9(base, "footprints", getFootprintDirName())
31654
32022
  };
31655
32023
  }
31656
32024
  var PinElectricalType = exports_external.enum([
@@ -31818,8 +32186,8 @@ async function handleFixLibrary(args) {
31818
32186
  }
31819
32187
  const category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
31820
32188
  const symbolLibraryFilename = getLibraryFilename(category);
31821
- const symbolFile = join8(paths.symbolsDir, symbolLibraryFilename);
31822
- if (!existsSync6(symbolFile) && !params.force) {
32189
+ const symbolFile = join9(paths.symbolsDir, symbolLibraryFilename);
32190
+ if (!existsSync7(symbolFile) && !params.force) {
31823
32191
  return {
31824
32192
  content: [{
31825
32193
  type: "text",
@@ -31833,7 +32201,7 @@ async function handleFixLibrary(args) {
31833
32201
  isError: true
31834
32202
  };
31835
32203
  }
31836
- if (existsSync6(symbolFile) && !params.force) {
32204
+ if (existsSync7(symbolFile) && !params.force) {
31837
32205
  const existingContent = await readFile7(symbolFile, "utf-8");
31838
32206
  if (!symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
31839
32207
  return {
@@ -31920,14 +32288,14 @@ async function handleFixLibrary(args) {
31920
32288
  const footprintName = footprintResult.name + "_" + params.lcsc_id;
31921
32289
  footprintRef = getFootprintReference2(footprintName);
31922
32290
  await ensureDir(paths.footprintDir);
31923
- const footprintPath = join8(paths.footprintDir, `${footprintName}.kicad_mod`);
32291
+ const footprintPath = join9(paths.footprintDir, `${footprintName}.kicad_mod`);
31924
32292
  await writeText(footprintPath, footprintResult.content);
31925
32293
  }
31926
32294
  component.info.package = footprintRef;
31927
32295
  await ensureDir(paths.symbolsDir);
31928
32296
  let symbolContent;
31929
32297
  let symbolAction;
31930
- if (existsSync6(symbolFile)) {
32298
+ if (existsSync7(symbolFile)) {
31931
32299
  const existingContent = await readFile7(symbolFile, "utf-8");
31932
32300
  symbolContent = symbolConverter.replaceInLibrary(existingContent, component);
31933
32301
  symbolAction = "replaced";
@@ -31956,494 +32324,31 @@ async function handleFixLibrary(args) {
31956
32324
  };
31957
32325
  }
31958
32326
 
31959
- // src/tools/easyeda.ts
31960
- import { join as join9 } from "path";
31961
- import { execSync as execSync2 } from "child_process";
31962
- import { tmpdir } from "os";
31963
- var easyedaSearchTool = {
31964
- name: "easyeda_search",
31965
- description: "Search EasyEDA community library for user-contributed symbols and footprints. Use this for parts not in LCSC official library (e.g., XIAO, Arduino modules, custom breakouts). Returns results with UUIDs and optionally opens an HTML preview.",
31966
- inputSchema: {
31967
- type: "object",
31968
- properties: {
31969
- query: {
31970
- type: "string",
31971
- description: 'Search term (e.g., "XIAO RP2040", "ESP32-C3 module")'
31972
- },
31973
- source: {
31974
- type: "string",
31975
- enum: ["user", "lcsc", "easyeda", "all"],
31976
- description: 'Filter by source. "user" for community-contributed (default)'
31977
- },
31978
- limit: {
31979
- type: "number",
31980
- description: "Max results to return (default: 20)"
31981
- },
31982
- open_preview: {
31983
- type: "boolean",
31984
- description: "Generate and open HTML preview in browser",
31985
- default: true
31986
- }
31987
- },
31988
- required: ["query"]
31989
- }
31990
- };
31991
- var easyedaGet3DModelTool = {
31992
- name: "easyeda_get_3d_model",
31993
- description: "Download 3D model for an EasyEDA community component. Requires the model UUID from easyeda_get.",
31994
- inputSchema: {
31995
- type: "object",
31996
- properties: {
31997
- uuid: {
31998
- type: "string",
31999
- description: "3D model UUID from easyeda_get result"
32000
- },
32001
- format: {
32002
- type: "string",
32003
- enum: ["step", "obj"],
32004
- description: 'Model format: "step" or "obj" (default: step)'
32005
- }
32006
- },
32007
- required: ["uuid"]
32008
- }
32009
- };
32010
- var EasyedaSearchParamsSchema = exports_external.object({
32011
- query: exports_external.string().min(1),
32012
- source: exports_external.enum(["user", "lcsc", "easyeda", "all"]).optional(),
32013
- limit: exports_external.number().min(1).max(100).optional(),
32014
- open_preview: exports_external.boolean().optional()
32015
- });
32016
- var EasyedaGet3DModelParamsSchema = exports_external.object({
32017
- uuid: exports_external.string().min(1),
32018
- format: exports_external.enum(["step", "obj"]).default("step")
32019
- });
32020
- async function handleEasyedaSearch(args) {
32021
- const params = EasyedaSearchParamsSchema.parse(args);
32022
- const openPreview = params.open_preview ?? true;
32023
- const results = await easyedaCommunityClient.search({
32024
- query: params.query,
32025
- source: params.source,
32026
- limit: params.limit || 20
32027
- });
32028
- if (results.length === 0) {
32029
- return {
32030
- content: [
32031
- {
32032
- type: "text",
32033
- text: `No results found for "${params.query}"`
32034
- }
32035
- ]
32036
- };
32037
- }
32038
- let output = `Found ${results.length} results for "${params.query}":
32039
-
32040
- `;
32041
- output += `| # | Title | Package | Owner | UUID |
32042
- `;
32043
- output += `|---|-------|---------|-------|------|
32044
- `;
32045
- for (let i = 0;i < results.length; i++) {
32046
- const r = results[i];
32047
- output += `| ${i + 1} | ${r.title} | ${r.package} | ${r.owner.nickname || r.owner.username} | ${r.uuid} |
32048
- `;
32049
- }
32050
- output += "\nUse `library_fetch` with the UUID to add component to global JLC-MCP libraries.";
32051
- output += "\nUse `easyeda_fetch` with the UUID to add to project-local EasyEDA library.";
32052
- if (openPreview) {
32053
- const { filepath, browserOpened } = await generateHtmlPreview(params.query, results);
32054
- if (browserOpened) {
32055
- output += `
32056
-
32057
- HTML preview opened in browser.`;
32058
- } else {
32059
- output += `
32060
-
32061
- Could not open browser automatically.`;
32062
- }
32063
- output += `
32064
- Preview file: ${filepath}`;
32065
- }
32066
- return {
32067
- content: [
32068
- {
32069
- type: "text",
32070
- text: output
32071
- }
32072
- ]
32073
- };
32074
- }
32075
- async function handleEasyedaGet3DModel(args) {
32076
- const params = EasyedaGet3DModelParamsSchema.parse(args);
32077
- const model = await easyedaCommunityClient.get3DModel(params.uuid, params.format);
32078
- if (!model) {
32079
- return {
32080
- content: [
32081
- {
32082
- type: "text",
32083
- text: `3D model ${params.uuid} not found`
32084
- }
32085
- ],
32086
- isError: true
32087
- };
32088
- }
32089
- return {
32090
- content: [
32091
- {
32092
- type: "text",
32093
- text: `3D model downloaded (${model.length} bytes, ${params.format.toUpperCase()} format)
32094
-
32095
- Base64 data:
32096
- ${model.toString("base64").slice(0, 500)}...`
32097
- }
32098
- ]
32099
- };
32100
- }
32101
- async function generateHtmlPreview(query, results) {
32102
- const timestamp = Date.now();
32103
- const filename = `easyeda-search-${timestamp}.html`;
32104
- const filepath = join9(tmpdir(), filename);
32105
- const noImageSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 150" style="background:#2a2a2a"><text x="100" y="75" text-anchor="middle" fill="#666" font-size="12">No Preview</text></svg>`;
32106
- const noImageDataUri = `data:image/svg+xml,${encodeURIComponent(noImageSvg)}`;
32107
- const cardsPromises = results.slice(0, 10).map(async (r) => {
32108
- const symbolImageUrl = `https://image.easyeda.com/components/${r.uuid}.png`;
32109
- let footprintSvg = "";
32110
- try {
32111
- const component = await easyedaCommunityClient.getComponent(r.uuid);
32112
- if (component) {
32113
- const rawData = component.rawData;
32114
- const fpDataStr = rawData?.packageDetail?.dataStr || rawData?.dataStr;
32115
- if (fpDataStr?.shape) {
32116
- footprintSvg = generateFootprintSvg(fpDataStr);
32117
- }
32118
- }
32119
- } catch {}
32120
- const footprintDataUri = footprintSvg ? `data:image/svg+xml,${encodeURIComponent(footprintSvg)}` : noImageDataUri;
32121
- return `
32122
- <div class="card">
32123
- <div class="images">
32124
- <div class="image-box">
32125
- <div class="image-label">Symbol</div>
32126
- <img src="${symbolImageUrl}" alt="Symbol" onerror="this.src='${noImageDataUri}'">
32127
- </div>
32128
- <div class="image-box">
32129
- <div class="image-label">Footprint</div>
32130
- <img src="${footprintDataUri}" alt="Footprint">
32131
- </div>
32132
- </div>
32133
- <h3>${escapeHtml(r.title)}</h3>
32134
- <div class="meta">
32135
- <div><strong>Package:</strong> ${escapeHtml(r.package || "N/A")}</div>
32136
- <div><strong>Owner:</strong> ${escapeHtml(r.owner.nickname || r.owner.username)}</div>
32137
- ${r.manufacturer ? `<div><strong>Mfr:</strong> ${escapeHtml(r.manufacturer)}</div>` : ""}
32138
- </div>
32139
- <div class="uuid" onclick="navigator.clipboard.writeText('${r.uuid}'); this.classList.add('copied'); setTimeout(() => this.classList.remove('copied'), 1000);">
32140
- ${r.uuid}
32141
- </div>
32142
- </div>`;
32143
- });
32144
- const cards = (await Promise.all(cardsPromises)).join(`
32145
- `);
32146
- const html = `<!DOCTYPE html>
32147
- <html lang="en">
32148
- <head>
32149
- <meta charset="UTF-8">
32150
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
32151
- <title>EasyEDA Search: ${escapeHtml(query)}</title>
32152
- <style>
32153
- * { box-sizing: border-box; }
32154
- body {
32155
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
32156
- max-width: 1400px;
32157
- margin: 0 auto;
32158
- padding: 20px;
32159
- background: #f9f9f9;
32160
- color: #333;
32161
- }
32162
- h1 { margin-bottom: 8px; }
32163
- .subtitle { color: #666; margin-bottom: 20px; }
32164
- .grid {
32165
- display: grid;
32166
- grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
32167
- gap: 16px;
32168
- }
32169
- .card {
32170
- background: white;
32171
- border: 1px solid #ddd;
32172
- border-radius: 8px;
32173
- padding: 16px;
32174
- transition: box-shadow 0.2s;
32175
- }
32176
- .card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
32177
- .card .images {
32178
- display: flex;
32179
- gap: 8px;
32180
- margin-bottom: 12px;
32181
- }
32182
- .card .image-box {
32183
- flex: 1;
32184
- min-width: 0;
32185
- }
32186
- .card .image-label {
32187
- font-size: 10px;
32188
- color: #888;
32189
- text-transform: uppercase;
32190
- text-align: center;
32191
- margin-bottom: 4px;
32192
- }
32193
- .card img {
32194
- width: 100%;
32195
- height: 120px;
32196
- object-fit: contain;
32197
- border-radius: 4px;
32198
- border: 1px solid #eee;
32199
- }
32200
- .card h3 {
32201
- margin: 0 0 8px;
32202
- font-size: 15px;
32203
- line-height: 1.3;
32204
- overflow: hidden;
32205
- text-overflow: ellipsis;
32206
- white-space: nowrap;
32207
- }
32208
- .card .meta {
32209
- color: #666;
32210
- font-size: 12px;
32211
- line-height: 1.6;
32212
- }
32213
- .card .meta div { margin-bottom: 2px; }
32214
- .card .uuid {
32215
- font-family: 'SF Mono', Monaco, 'Courier New', monospace;
32216
- font-size: 10px;
32217
- color: #888;
32218
- background: #f5f5f5;
32219
- padding: 6px 8px;
32220
- border-radius: 4px;
32221
- margin-top: 12px;
32222
- cursor: pointer;
32223
- word-break: break-all;
32224
- transition: background 0.2s;
32225
- }
32226
- .card .uuid:hover { background: #e8e8e8; }
32227
- .card .uuid.copied { background: #d4edda; color: #155724; }
32228
- .instructions {
32229
- background: #e8f4fd;
32230
- border: 1px solid #b8daff;
32231
- border-radius: 8px;
32232
- padding: 16px;
32233
- margin-bottom: 20px;
32234
- font-size: 14px;
32235
- }
32236
- .instructions code {
32237
- background: #fff;
32238
- padding: 2px 6px;
32239
- border-radius: 4px;
32240
- font-family: 'SF Mono', Monaco, monospace;
32241
- }
32242
- </style>
32243
- </head>
32244
- <body>
32245
- <h1>EasyEDA Search: "${escapeHtml(query)}"</h1>
32246
- <p class="subtitle">Found ${results.length} results. Click UUID to copy to clipboard.</p>
32247
-
32248
- <div class="instructions">
32249
- <strong>How to use:</strong><br>
32250
- 1. Click on a UUID to copy it<br>
32251
- 2. Use <code>library_fetch</code> with the UUID to add to global JLC-MCP libraries<br>
32252
- 3. Or use <code>easyeda_fetch</code> for project-local EasyEDA library
32253
- </div>
32254
-
32255
- <div class="grid">
32256
- ${cards}
32257
- </div>
32258
- </body>
32259
- </html>`;
32260
- __require("fs").writeFileSync(filepath, html, "utf-8");
32261
- const browserOpened = openInBrowser(filepath);
32262
- return { filepath, browserOpened };
32263
- }
32264
- function openInBrowser(filepath) {
32265
- const platform4 = process.platform;
32266
- try {
32267
- switch (platform4) {
32268
- case "darwin":
32269
- execSync2(`open "${filepath}"`, { stdio: "ignore" });
32270
- return true;
32271
- case "win32":
32272
- execSync2(`start "" "${filepath}"`, { stdio: "ignore", shell: "cmd.exe" });
32273
- return true;
32274
- case "linux":
32275
- default:
32276
- execSync2(`xdg-open "${filepath}"`, { stdio: "ignore" });
32277
- return true;
32278
- }
32279
- } catch {
32280
- const fallbacks = ["xdg-open", "sensible-browser", "x-www-browser", "gnome-open"];
32281
- for (const cmd of fallbacks) {
32282
- try {
32283
- execSync2(`${cmd} "${filepath}"`, { stdio: "ignore" });
32284
- return true;
32285
- } catch {}
32286
- }
32287
- return false;
32288
- }
32289
- }
32290
- function escapeHtml(str) {
32291
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
32292
- }
32293
- function generateFootprintSvg(dataStr) {
32294
- if (!dataStr.shape || dataStr.shape.length === 0) {
32295
- return "";
32296
- }
32297
- const bbox = dataStr.BBox || { x: 0, y: 0, width: 100, height: 100 };
32298
- const padding = 5;
32299
- const viewBox = `${bbox.x - padding} ${bbox.y - padding} ${bbox.width + padding * 2} ${bbox.height + padding * 2}`;
32300
- const regions = [];
32301
- const tracks = [];
32302
- const pads = [];
32303
- const holes = [];
32304
- const texts = [];
32305
- for (const shape of dataStr.shape) {
32306
- if (typeof shape !== "string")
32307
- continue;
32308
- if (shape.startsWith("SOLIDREGION~")) {
32309
- const svg = renderSolidRegion(shape);
32310
- if (svg)
32311
- regions.push(svg);
32312
- } else if (shape.startsWith("TRACK~")) {
32313
- const svg = renderTrackShape(shape);
32314
- if (svg)
32315
- tracks.push(svg);
32316
- } else if (shape.startsWith("PAD~")) {
32317
- const result = renderPadShape(shape);
32318
- if (result) {
32319
- pads.push(result.pad);
32320
- if (result.hole)
32321
- holes.push(result.hole);
32322
- }
32323
- } else if (shape.startsWith("TEXT~")) {
32324
- const svg = renderTextShape(shape);
32325
- if (svg)
32326
- texts.push(svg);
32327
- }
32328
- }
32329
- const allElements = [...regions, ...tracks, ...pads, ...holes, ...texts];
32330
- if (allElements.length === 0) {
32331
- return "";
32332
- }
32333
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" style="background:#000000">
32334
- <style>
32335
- .pad { fill: #CC0000; stroke: none; }
32336
- .pad-hole { fill: #666666; }
32337
- .track { fill: none; stroke: #FFFF00; stroke-linecap: round; stroke-linejoin: round; }
32338
- .region { fill: #CC0000; opacity: 0.6; }
32339
- .text-path { fill: none; stroke: #FFFF00; stroke-width: 0.4; stroke-linecap: round; stroke-linejoin: round; }
32340
- </style>
32341
- ${allElements.join(`
32342
- `)}
32343
- </svg>`;
32344
- }
32345
- function renderPadShape(padData) {
32346
- const fields = padData.split("~");
32347
- const shapeType = fields[1];
32348
- const cx = parseFloat(fields[2]) || 0;
32349
- const cy = parseFloat(fields[3]) || 0;
32350
- if (shapeType === "POLYGON") {
32351
- const holeDia2 = parseFloat(fields[9]) || 0;
32352
- const pointsStr = fields[10] || "";
32353
- if (!pointsStr)
32354
- return null;
32355
- const coords = pointsStr.split(" ").map(Number);
32356
- if (coords.length < 4)
32357
- return null;
32358
- let pathD = `M ${coords[0]} ${coords[1]}`;
32359
- for (let i = 2;i < coords.length; i += 2) {
32360
- pathD += ` L ${coords[i]} ${coords[i + 1]}`;
32361
- }
32362
- pathD += " Z";
32363
- return {
32364
- pad: `<path class="pad" d="${pathD}"/>`,
32365
- hole: holeDia2 > 0 ? `<circle class="pad-hole" cx="${cx}" cy="${cy}" r="${holeDia2}"/>` : null
32366
- };
32367
- }
32368
- const width = parseFloat(fields[4]) || 0;
32369
- const height = parseFloat(fields[5]) || 0;
32370
- const holeDia = parseFloat(fields[9]) || 0;
32371
- let padSvg = "";
32372
- if (shapeType === "ELLIPSE" || shapeType === "OVAL" || shapeType === "ROUND") {
32373
- const rx = width / 2;
32374
- const ry = height / 2;
32375
- padSvg = `<ellipse class="pad" cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}"/>`;
32376
- } else {
32377
- const rectX = cx - width / 2;
32378
- const rectY = cy - height / 2;
32379
- padSvg = `<rect class="pad" x="${rectX}" y="${rectY}" width="${width}" height="${height}"/>`;
32380
- }
32381
- return {
32382
- pad: padSvg,
32383
- hole: holeDia > 0 ? `<circle class="pad-hole" cx="${cx}" cy="${cy}" r="${holeDia}"/>` : null
32384
- };
32385
- }
32386
- function renderTrackShape(trackData) {
32387
- const fields = trackData.split("~");
32388
- const strokeWidth = parseFloat(fields[1]) || 0.5;
32389
- const pointsStr = fields[4] || "";
32390
- if (!pointsStr)
32391
- return null;
32392
- const coords = pointsStr.split(" ").map(Number);
32393
- if (coords.length < 4)
32394
- return null;
32395
- let pathD = `M ${coords[0]} ${coords[1]}`;
32396
- for (let i = 2;i < coords.length; i += 2) {
32397
- pathD += ` L ${coords[i]} ${coords[i + 1]}`;
32398
- }
32399
- return `<path class="track" d="${pathD}" stroke-width="${strokeWidth}"/>`;
32400
- }
32401
- function renderSolidRegion(regionData) {
32402
- const fields = regionData.split("~");
32403
- const pathD = fields[3] || "";
32404
- if (!pathD || !pathD.startsWith("M"))
32405
- return null;
32406
- return `<path class="region" d="${pathD}"/>`;
32407
- }
32408
- function renderTextShape(textData) {
32409
- const fields = textData.split("~");
32410
- const svgPath = fields[11] || "";
32411
- if (!svgPath || !svgPath.startsWith("M"))
32412
- return null;
32413
- return `<path class="text-path" d="${svgPath}"/>`;
32414
- }
32415
-
32416
32327
  // src/tools/index.ts
32417
32328
  var tools = [
32418
- searchComponentsTool,
32419
- getComponentTool,
32420
- getSymbolKicadTool,
32421
- getFootprintKicadTool,
32422
- fetchLibraryTool,
32329
+ componentSearchTool,
32330
+ libraryInstallTool,
32331
+ libraryBatchInstallTool,
32332
+ libraryGetComponentTool,
32423
32333
  updateLibraryTool,
32424
- fixLibraryTool,
32425
- get3DModelTool,
32426
- easyedaSearchTool,
32427
- easyedaGet3DModelTool
32334
+ fixLibraryTool
32428
32335
  ];
32429
32336
  var toolHandlers = {
32430
- component_search: handleSearchComponents,
32431
- component_get: handleGetComponent,
32432
- library_get_symbol: handleGetSymbolKicad,
32433
- library_get_footprint: handleGetFootprintKicad,
32434
- library_fetch: handleFetchLibrary,
32435
- library_update: handleUpdateLibrary,
32436
- library_fix: handleFixLibrary,
32437
- library_get_3d_model: handleGet3DModel,
32438
- easyeda_search: handleEasyedaSearch,
32439
- easyeda_get_3d_model: handleEasyedaGet3DModel
32337
+ [componentSearchTool.name]: handleComponentSearch,
32338
+ [libraryInstallTool.name]: handleLibraryInstall,
32339
+ [libraryBatchInstallTool.name]: handleLibraryBatchInstall,
32340
+ [libraryGetComponentTool.name]: handleLibraryGetComponent,
32341
+ [updateLibraryTool.name]: handleUpdateLibrary,
32342
+ [fixLibraryTool.name]: handleFixLibrary
32440
32343
  };
32441
32344
 
32442
32345
  // src/index.ts
32346
+ var require2 = createRequire(import.meta.url);
32347
+ var { version: version2 } = require2("../package.json");
32443
32348
  var logger2 = createLogger("jlc-mcp");
32444
32349
  var server = new Server({
32445
32350
  name: "jlc-mcp",
32446
- version: "0.1.0"
32351
+ version: version2
32447
32352
  }, {
32448
32353
  capabilities: {
32449
32354
  tools: {}
@@ -32484,9 +32389,9 @@ async function main() {
32484
32389
  });
32485
32390
  process.exit(1);
32486
32391
  }
32487
- const { symLibTable, fpLibTable, libraryStubs, version: version2 } = registration;
32392
+ const { symLibTable, fpLibTable, libraryStubs, version: version3 } = registration;
32488
32393
  if (symLibTable.created || symLibTable.modified || fpLibTable.created || fpLibTable.modified) {
32489
- logger2.info(`JLC libraries registered in KiCad ${version2}`, {
32394
+ logger2.info(`JLC libraries registered in KiCad ${version3}`, {
32490
32395
  symLibTable: symLibTable.created ? `created with ${symLibTable.entriesAdded} entries` : symLibTable.modified ? `added ${symLibTable.entriesAdded} entries` : "already configured",
32491
32396
  fpLibTable: fpLibTable.created ? "created" : fpLibTable.modified ? "updated" : "already configured",
32492
32397
  stubsCreated: libraryStubs.symbolsCreated.length + libraryStubs.directoriesCreated.length