@jlcpcb/mcp 0.2.0 → 0.3.0
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/CHANGELOG.md +44 -0
- package/README.md +19 -29
- package/dist/index.js +794 -942
- package/package.json +1 -1
- package/src/index.ts +5 -1
- package/src/schemas.ts +14 -0
- package/src/services.ts +34 -0
- package/src/tools/batch.ts +123 -0
- package/src/tools/index.ts +36 -63
- package/src/tools/library-fix.ts +1 -18
- package/src/tools/library-update.ts +8 -27
- package/src/tools/library.ts +108 -149
- package/src/tools/search.ts +68 -10
- package/src/tools/details.ts +0 -66
- package/src/tools/easyeda.ts +0 -582
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
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;
|
|
@@ -26,7 +25,6 @@ var __export = (target, all) => {
|
|
|
26
25
|
set: (newValue) => all[name] = () => newValue
|
|
27
26
|
});
|
|
28
27
|
};
|
|
29
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
28
|
|
|
31
29
|
// ../../node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js
|
|
32
30
|
var require_code = __commonJS((exports) => {
|
|
@@ -11947,6 +11945,9 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
11947
11945
|
exports.default = formatsPlugin;
|
|
11948
11946
|
});
|
|
11949
11947
|
|
|
11948
|
+
// src/index.ts
|
|
11949
|
+
import { createRequire } from "module";
|
|
11950
|
+
|
|
11950
11951
|
// ../../node_modules/zod/v3/external.js
|
|
11951
11952
|
var exports_external = {};
|
|
11952
11953
|
__export(exports_external, {
|
|
@@ -21890,22 +21891,25 @@ class StdioServerTransport {
|
|
|
21890
21891
|
}
|
|
21891
21892
|
// ../core/dist/index.js
|
|
21892
21893
|
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
21894
|
import { existsSync as existsSync2 } from "fs";
|
|
21897
21895
|
import { join as join3 } from "path";
|
|
21898
|
-
import {
|
|
21896
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
|
|
21897
|
+
import { existsSync as existsSync3 } from "fs";
|
|
21898
|
+
import { join as join4 } from "path";
|
|
21899
|
+
import { homedir as homedir2, platform as platform2 } from "os";
|
|
21899
21900
|
import { readdir, readFile, writeFile, mkdir, access, stat } from "fs/promises";
|
|
21900
21901
|
import { join, dirname, extname, basename } from "path";
|
|
21902
|
+
import { existsSync } from "fs";
|
|
21903
|
+
import { homedir, platform } from "os";
|
|
21904
|
+
import { join as join2 } from "path";
|
|
21901
21905
|
import { execSync } from "child_process";
|
|
21902
|
-
import { existsSync as
|
|
21903
|
-
import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
|
|
21904
|
-
import { homedir as
|
|
21905
|
-
import { join as
|
|
21906
|
+
import { existsSync as existsSync4 } from "fs";
|
|
21907
|
+
import { readFile as readFile4, readdir as readdir2, unlink } from "fs/promises";
|
|
21908
|
+
import { homedir as homedir3, platform as platform3 } from "os";
|
|
21909
|
+
import { join as join5 } from "path";
|
|
21906
21910
|
import { createServer } from "http";
|
|
21907
21911
|
import { readFileSync } from "fs";
|
|
21908
|
-
import { join as
|
|
21912
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
21909
21913
|
import { fileURLToPath } from "url";
|
|
21910
21914
|
var __defProp2 = Object.defineProperty;
|
|
21911
21915
|
var __export2 = (target, all) => {
|
|
@@ -21934,9 +21938,16 @@ function containsKeyword(text, keyword) {
|
|
|
21934
21938
|
return false;
|
|
21935
21939
|
}
|
|
21936
21940
|
function getLibraryCategory(prefix, category, description) {
|
|
21937
|
-
const
|
|
21938
|
-
if (
|
|
21939
|
-
|
|
21941
|
+
const cleanedPrefix = prefix.replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
|
|
21942
|
+
if (cleanedPrefix.length >= 2) {
|
|
21943
|
+
const twoCharPrefix = cleanedPrefix.slice(0, 2);
|
|
21944
|
+
if (PREFIX_CATEGORY_MAP[twoCharPrefix]) {
|
|
21945
|
+
return PREFIX_CATEGORY_MAP[twoCharPrefix];
|
|
21946
|
+
}
|
|
21947
|
+
}
|
|
21948
|
+
const singleCharPrefix = cleanedPrefix.slice(0, 1);
|
|
21949
|
+
if (PREFIX_CATEGORY_MAP[singleCharPrefix]) {
|
|
21950
|
+
return PREFIX_CATEGORY_MAP[singleCharPrefix];
|
|
21940
21951
|
}
|
|
21941
21952
|
const searchText = [category || "", description || ""].join(" ");
|
|
21942
21953
|
if (searchText.trim()) {
|
|
@@ -22000,6 +22011,7 @@ var init_category_router = __esm(() => {
|
|
|
22000
22011
|
X: "Crystals",
|
|
22001
22012
|
J: "Connectors",
|
|
22002
22013
|
P: "Connectors",
|
|
22014
|
+
RJ: "Connectors",
|
|
22003
22015
|
K: "Misc",
|
|
22004
22016
|
F: "Misc"
|
|
22005
22017
|
};
|
|
@@ -22245,6 +22257,48 @@ var init_category_router = __esm(() => {
|
|
|
22245
22257
|
"ceramic resonator"
|
|
22246
22258
|
]
|
|
22247
22259
|
},
|
|
22260
|
+
{
|
|
22261
|
+
category: "Connectors",
|
|
22262
|
+
keywords: [
|
|
22263
|
+
"connector",
|
|
22264
|
+
"header",
|
|
22265
|
+
"socket",
|
|
22266
|
+
"terminal",
|
|
22267
|
+
"terminal block",
|
|
22268
|
+
"jack",
|
|
22269
|
+
"plug",
|
|
22270
|
+
"receptacle",
|
|
22271
|
+
"usb-c",
|
|
22272
|
+
"usb type-c",
|
|
22273
|
+
"micro usb",
|
|
22274
|
+
"mini usb",
|
|
22275
|
+
"usb-a",
|
|
22276
|
+
"usb-b",
|
|
22277
|
+
"hdmi connector",
|
|
22278
|
+
"rj45",
|
|
22279
|
+
"rj11",
|
|
22280
|
+
"barrel jack",
|
|
22281
|
+
"dc jack",
|
|
22282
|
+
"audio jack",
|
|
22283
|
+
"jst",
|
|
22284
|
+
"jst-xh",
|
|
22285
|
+
"jst-ph",
|
|
22286
|
+
"molex",
|
|
22287
|
+
"dupont",
|
|
22288
|
+
"pin header",
|
|
22289
|
+
"female header",
|
|
22290
|
+
"fpc",
|
|
22291
|
+
"ffc",
|
|
22292
|
+
"sim card",
|
|
22293
|
+
"sd card",
|
|
22294
|
+
"microsd",
|
|
22295
|
+
"pogo pin",
|
|
22296
|
+
"spring contact",
|
|
22297
|
+
"test point",
|
|
22298
|
+
"ethernet connector",
|
|
22299
|
+
"modular connector"
|
|
22300
|
+
]
|
|
22301
|
+
},
|
|
22248
22302
|
{
|
|
22249
22303
|
category: "Transistors",
|
|
22250
22304
|
keywords: [
|
|
@@ -22345,46 +22399,6 @@ var init_category_router = __esm(() => {
|
|
|
22345
22399
|
"chip resistor"
|
|
22346
22400
|
]
|
|
22347
22401
|
},
|
|
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
22402
|
{
|
|
22389
22403
|
category: "ICs",
|
|
22390
22404
|
keywords: [
|
|
@@ -22478,9 +22492,9 @@ function addLibraryToTable(tableContent, libraryName, libraryPath, type, descrip
|
|
|
22478
22492
|
`;
|
|
22479
22493
|
}
|
|
22480
22494
|
async function ensureSymLibTable(projectDir, symbolLibPath, libraryName = DEFAULT_LIBRARY_NAME, description = DEFAULT_LIBRARY_DESCRIPTION) {
|
|
22481
|
-
const tablePath =
|
|
22495
|
+
const tablePath = join3(projectDir, "sym-lib-table");
|
|
22482
22496
|
const relativePath = symbolLibPath.startsWith(projectDir) ? "${KIPRJMOD}" + symbolLibPath.slice(projectDir.length) : symbolLibPath;
|
|
22483
|
-
if (!
|
|
22497
|
+
if (!existsSync2(tablePath)) {
|
|
22484
22498
|
const content = generateSymLibTable(relativePath, libraryName, description);
|
|
22485
22499
|
await writeFile2(tablePath, content, "utf-8");
|
|
22486
22500
|
return { created: true, modified: false, path: tablePath };
|
|
@@ -22494,9 +22508,9 @@ async function ensureSymLibTable(projectDir, symbolLibPath, libraryName = DEFAUL
|
|
|
22494
22508
|
return { created: false, modified: true, path: tablePath };
|
|
22495
22509
|
}
|
|
22496
22510
|
async function ensureFpLibTable(projectDir, footprintLibPath, libraryName = DEFAULT_LIBRARY_NAME, description = DEFAULT_LIBRARY_DESCRIPTION) {
|
|
22497
|
-
const tablePath =
|
|
22511
|
+
const tablePath = join3(projectDir, "fp-lib-table");
|
|
22498
22512
|
const relativePath = footprintLibPath.startsWith(projectDir) ? "${KIPRJMOD}" + footprintLibPath.slice(projectDir.length) : footprintLibPath;
|
|
22499
|
-
if (!
|
|
22513
|
+
if (!existsSync2(tablePath)) {
|
|
22500
22514
|
const content = generateFpLibTable(relativePath, libraryName, description);
|
|
22501
22515
|
await writeFile2(tablePath, content, "utf-8");
|
|
22502
22516
|
return { created: true, modified: false, path: tablePath };
|
|
@@ -22520,37 +22534,37 @@ __export2(exports_global_lib_table, {
|
|
|
22520
22534
|
ensureGlobalLibraryTables: () => ensureGlobalLibraryTables,
|
|
22521
22535
|
ensureGlobalEasyEDALibrary: () => ensureGlobalEasyEDALibrary
|
|
22522
22536
|
});
|
|
22523
|
-
function
|
|
22524
|
-
const home =
|
|
22525
|
-
const baseDir =
|
|
22537
|
+
function detectKicadVersion2() {
|
|
22538
|
+
const home = homedir2();
|
|
22539
|
+
const baseDir = join4(home, "Documents", "KiCad");
|
|
22526
22540
|
for (const version2 of KICAD_VERSIONS2) {
|
|
22527
|
-
if (
|
|
22541
|
+
if (existsSync3(join4(baseDir, version2))) {
|
|
22528
22542
|
return version2;
|
|
22529
22543
|
}
|
|
22530
22544
|
}
|
|
22531
22545
|
return "9.0";
|
|
22532
22546
|
}
|
|
22533
|
-
function
|
|
22534
|
-
const home =
|
|
22535
|
-
const plat =
|
|
22547
|
+
function getKicadConfigDir2(version2) {
|
|
22548
|
+
const home = homedir2();
|
|
22549
|
+
const plat = platform2();
|
|
22536
22550
|
if (plat === "darwin") {
|
|
22537
|
-
return
|
|
22551
|
+
return join4(home, "Library", "Preferences", "kicad", version2);
|
|
22538
22552
|
} else if (plat === "win32") {
|
|
22539
|
-
return
|
|
22553
|
+
return join4(process.env.APPDATA || join4(home, "AppData", "Roaming"), "kicad", version2);
|
|
22540
22554
|
} else {
|
|
22541
|
-
return
|
|
22555
|
+
return join4(home, ".config", "kicad", version2);
|
|
22542
22556
|
}
|
|
22543
22557
|
}
|
|
22544
22558
|
function get3rdPartyBaseDir(version2) {
|
|
22545
|
-
const home =
|
|
22546
|
-
const plat =
|
|
22559
|
+
const home = homedir2();
|
|
22560
|
+
const plat = platform2();
|
|
22547
22561
|
if (plat === "linux") {
|
|
22548
|
-
return
|
|
22562
|
+
return join4(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE);
|
|
22549
22563
|
}
|
|
22550
|
-
return
|
|
22564
|
+
return join4(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE);
|
|
22551
22565
|
}
|
|
22552
22566
|
function getSymbolLibPath(category, version2) {
|
|
22553
|
-
return
|
|
22567
|
+
return join4(get3rdPartyBaseDir(version2), "symbols", getLibraryFilename(category));
|
|
22554
22568
|
}
|
|
22555
22569
|
function getSymbolLibUri(category) {
|
|
22556
22570
|
return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${getLibraryFilename(category)}`;
|
|
@@ -22591,11 +22605,11 @@ function generateFpLibTable2() {
|
|
|
22591
22605
|
`;
|
|
22592
22606
|
}
|
|
22593
22607
|
async function ensureGlobalSymLibTable(version2) {
|
|
22594
|
-
const configDir =
|
|
22595
|
-
const tablePath =
|
|
22608
|
+
const configDir = getKicadConfigDir2(version2);
|
|
22609
|
+
const tablePath = join4(configDir, "sym-lib-table");
|
|
22596
22610
|
const categories = getAllCategories();
|
|
22597
22611
|
await mkdir2(configDir, { recursive: true });
|
|
22598
|
-
if (!
|
|
22612
|
+
if (!existsSync3(tablePath)) {
|
|
22599
22613
|
const content2 = generateSymLibTable2();
|
|
22600
22614
|
await writeFile3(tablePath, content2, "utf-8");
|
|
22601
22615
|
return {
|
|
@@ -22635,11 +22649,11 @@ async function ensureGlobalSymLibTable(version2) {
|
|
|
22635
22649
|
};
|
|
22636
22650
|
}
|
|
22637
22651
|
async function ensureGlobalFpLibTable(version2) {
|
|
22638
|
-
const configDir =
|
|
22639
|
-
const tablePath =
|
|
22652
|
+
const configDir = getKicadConfigDir2(version2);
|
|
22653
|
+
const tablePath = join4(configDir, "fp-lib-table");
|
|
22640
22654
|
const name = LIBRARY_PREFIX2;
|
|
22641
22655
|
await mkdir2(configDir, { recursive: true });
|
|
22642
|
-
if (!
|
|
22656
|
+
if (!existsSync3(tablePath)) {
|
|
22643
22657
|
const content2 = generateFpLibTable2();
|
|
22644
22658
|
await writeFile3(tablePath, content2, "utf-8");
|
|
22645
22659
|
return {
|
|
@@ -22673,13 +22687,13 @@ async function ensureGlobalFpLibTable(version2) {
|
|
|
22673
22687
|
}
|
|
22674
22688
|
async function ensureLibraryStubs(version2) {
|
|
22675
22689
|
const baseDir = get3rdPartyBaseDir(version2);
|
|
22676
|
-
const symbolsDir =
|
|
22677
|
-
const footprintsDir =
|
|
22678
|
-
const models3dDir =
|
|
22690
|
+
const symbolsDir = join4(baseDir, "symbols");
|
|
22691
|
+
const footprintsDir = join4(baseDir, "footprints", getFootprintDirName());
|
|
22692
|
+
const models3dDir = join4(baseDir, "3dmodels", get3DModelsDirName());
|
|
22679
22693
|
const directoriesCreated = [];
|
|
22680
22694
|
const symbolsCreated = [];
|
|
22681
22695
|
for (const dir of [symbolsDir, footprintsDir, models3dDir]) {
|
|
22682
|
-
if (!
|
|
22696
|
+
if (!existsSync3(dir)) {
|
|
22683
22697
|
await mkdir2(dir, { recursive: true });
|
|
22684
22698
|
directoriesCreated.push(dir);
|
|
22685
22699
|
}
|
|
@@ -22688,7 +22702,7 @@ async function ensureLibraryStubs(version2) {
|
|
|
22688
22702
|
const emptyContent = generateEmptySymbolLibrary();
|
|
22689
22703
|
for (const category of categories) {
|
|
22690
22704
|
const filePath = getSymbolLibPath(category, version2);
|
|
22691
|
-
if (!
|
|
22705
|
+
if (!existsSync3(filePath)) {
|
|
22692
22706
|
await writeFile3(filePath, emptyContent, "utf-8");
|
|
22693
22707
|
symbolsCreated.push(filePath);
|
|
22694
22708
|
}
|
|
@@ -22702,11 +22716,11 @@ function getEasyEDAFootprintLibUri() {
|
|
|
22702
22716
|
return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/footprints/${EASYEDA_FOOTPRINT_LIBRARY_NAME}`;
|
|
22703
22717
|
}
|
|
22704
22718
|
async function ensureGlobalEasyEDALibrary() {
|
|
22705
|
-
const version2 =
|
|
22706
|
-
const configDir =
|
|
22719
|
+
const version2 = detectKicadVersion2();
|
|
22720
|
+
const configDir = getKicadConfigDir2(version2);
|
|
22707
22721
|
await mkdir2(configDir, { recursive: true });
|
|
22708
|
-
const symTablePath =
|
|
22709
|
-
if (
|
|
22722
|
+
const symTablePath = join4(configDir, "sym-lib-table");
|
|
22723
|
+
if (existsSync3(symTablePath)) {
|
|
22710
22724
|
let content = await readFile3(symTablePath, "utf-8");
|
|
22711
22725
|
if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
|
|
22712
22726
|
const uri = getEasyEDASymbolLibUri();
|
|
@@ -22722,8 +22736,8 @@ async function ensureGlobalEasyEDALibrary() {
|
|
|
22722
22736
|
`;
|
|
22723
22737
|
await writeFile3(symTablePath, content, "utf-8");
|
|
22724
22738
|
}
|
|
22725
|
-
const fpTablePath =
|
|
22726
|
-
if (
|
|
22739
|
+
const fpTablePath = join4(configDir, "fp-lib-table");
|
|
22740
|
+
if (existsSync3(fpTablePath)) {
|
|
22727
22741
|
let content = await readFile3(fpTablePath, "utf-8");
|
|
22728
22742
|
if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
|
|
22729
22743
|
const uri = getEasyEDAFootprintLibUri();
|
|
@@ -22742,7 +22756,7 @@ async function ensureGlobalEasyEDALibrary() {
|
|
|
22742
22756
|
}
|
|
22743
22757
|
async function ensureGlobalLibraryTables() {
|
|
22744
22758
|
const errors22 = [];
|
|
22745
|
-
const version2 =
|
|
22759
|
+
const version2 = detectKicadVersion2();
|
|
22746
22760
|
let symLibTable = {
|
|
22747
22761
|
path: "",
|
|
22748
22762
|
created: false,
|
|
@@ -26770,6 +26784,9 @@ var coerce2 = {
|
|
|
26770
26784
|
};
|
|
26771
26785
|
var NEVER3 = INVALID2;
|
|
26772
26786
|
var LCSCPartNumberSchema = exports_external2.string().regex(/^C\d+$/, "Invalid LCSC part number format (expected C followed by digits)");
|
|
26787
|
+
var EasyEDAUuidSchema = exports_external2.string().regex(/^[a-f0-9]{32}$/i, "Invalid EasyEDA UUID (expected 32-character hex string)").transform((s) => s.toLowerCase());
|
|
26788
|
+
var ComponentIdSchema = exports_external2.union([LCSCPartNumberSchema, EasyEDAUuidSchema]);
|
|
26789
|
+
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
26790
|
var PackageSchema = exports_external2.string().min(1, "Package cannot be empty");
|
|
26774
26791
|
var PriceTierSchema = exports_external2.object({
|
|
26775
26792
|
quantity: exports_external2.number().positive(),
|
|
@@ -26845,6 +26862,9 @@ var KiCadPadShapeSchema = exports_external2.enum([
|
|
|
26845
26862
|
"roundrect",
|
|
26846
26863
|
"custom"
|
|
26847
26864
|
]);
|
|
26865
|
+
function isLcscId(id) {
|
|
26866
|
+
return LCSCPartNumberSchema.safeParse(id).success;
|
|
26867
|
+
}
|
|
26848
26868
|
async function ensureDir(path) {
|
|
26849
26869
|
await mkdir(path, { recursive: true });
|
|
26850
26870
|
}
|
|
@@ -26926,7 +26946,58 @@ var logger = new Logger({ prefix: "ai-eda" });
|
|
|
26926
26946
|
function createLogger(packageName) {
|
|
26927
26947
|
return logger.child(packageName);
|
|
26928
26948
|
}
|
|
26949
|
+
var KICAD_LAYERS = {
|
|
26950
|
+
F_CU: "F.Cu",
|
|
26951
|
+
B_CU: "B.Cu",
|
|
26952
|
+
IN1_CU: "In1.Cu",
|
|
26953
|
+
IN2_CU: "In2.Cu",
|
|
26954
|
+
IN3_CU: "In3.Cu",
|
|
26955
|
+
IN4_CU: "In4.Cu",
|
|
26956
|
+
F_ADHES: "F.Adhes",
|
|
26957
|
+
F_PASTE: "F.Paste",
|
|
26958
|
+
F_SILKS: "F.SilkS",
|
|
26959
|
+
F_MASK: "F.Mask",
|
|
26960
|
+
F_CRTYD: "F.CrtYd",
|
|
26961
|
+
F_FAB: "F.Fab",
|
|
26962
|
+
B_ADHES: "B.Adhes",
|
|
26963
|
+
B_PASTE: "B.Paste",
|
|
26964
|
+
B_SILKS: "B.SilkS",
|
|
26965
|
+
B_MASK: "B.Mask",
|
|
26966
|
+
B_CRTYD: "B.CrtYd",
|
|
26967
|
+
B_FAB: "B.Fab",
|
|
26968
|
+
EDGE_CUTS: "Edge.Cuts",
|
|
26969
|
+
MARGIN: "Margin",
|
|
26970
|
+
DWGS_USER: "Dwgs.User",
|
|
26971
|
+
CMTS_USER: "Cmts.User",
|
|
26972
|
+
ECO1_USER: "Eco1.User",
|
|
26973
|
+
ECO2_USER: "Eco2.User"
|
|
26974
|
+
};
|
|
26975
|
+
var KICAD_DEFAULTS = {
|
|
26976
|
+
TEXT_SIZE: 1.27,
|
|
26977
|
+
TEXT_THICKNESS: 0.15,
|
|
26978
|
+
WIRE_WIDTH: 0.25,
|
|
26979
|
+
GRID_SCHEMATIC: 2.54,
|
|
26980
|
+
GRID_PCB: 0.25,
|
|
26981
|
+
PIN_LENGTH: 1.27,
|
|
26982
|
+
PIN_NAME_OFFSET: 0
|
|
26983
|
+
};
|
|
26984
|
+
var KICAD_SYMBOL_VERSION = "20241209";
|
|
26985
|
+
var KICAD_FOOTPRINT_VERSION = "20241209";
|
|
26986
|
+
var KICAD_VERSIONS = ["9.0", "8.0"];
|
|
26987
|
+
function detectKicadVersion() {
|
|
26988
|
+
const home = homedir();
|
|
26989
|
+
const baseDir = join2(home, "Documents", "KiCad");
|
|
26990
|
+
for (const version2 of KICAD_VERSIONS) {
|
|
26991
|
+
if (existsSync(join2(baseDir, version2))) {
|
|
26992
|
+
return version2;
|
|
26993
|
+
}
|
|
26994
|
+
}
|
|
26995
|
+
return "9.0";
|
|
26996
|
+
}
|
|
26929
26997
|
var logger3 = createLogger("jlc-api");
|
|
26998
|
+
function stripTemperaturePrefix(description) {
|
|
26999
|
+
return description.replace(/^-?\d+℃~[+-]?\d+℃\s*/, "");
|
|
27000
|
+
}
|
|
26930
27001
|
var JLCPCB_SEARCH_API = "https://jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList/v2";
|
|
26931
27002
|
|
|
26932
27003
|
class JLCClient {
|
|
@@ -26972,7 +27043,7 @@ class JLCClient {
|
|
|
26972
27043
|
package: c.componentSpecificationEn || "",
|
|
26973
27044
|
price: c.componentPrices?.[0]?.productPrice,
|
|
26974
27045
|
stock: c.stockCount,
|
|
26975
|
-
description: c.describe,
|
|
27046
|
+
description: stripTemperaturePrefix(c.describe),
|
|
26976
27047
|
productUrl: c.lcscGoodsUrl,
|
|
26977
27048
|
datasheetPdf: c.dataManualUrl,
|
|
26978
27049
|
category: c.componentTypeEn,
|
|
@@ -27212,6 +27283,25 @@ function parseSymbolPath(data) {
|
|
|
27212
27283
|
return null;
|
|
27213
27284
|
}
|
|
27214
27285
|
}
|
|
27286
|
+
function parseSymbolText(data) {
|
|
27287
|
+
try {
|
|
27288
|
+
const f = data.split("~");
|
|
27289
|
+
const isPinPart = f[f.length - 1] === "pinpart" || f.some((field) => field === "pinpart");
|
|
27290
|
+
return {
|
|
27291
|
+
x: safeParseFloat(f[2]),
|
|
27292
|
+
y: safeParseFloat(f[3]),
|
|
27293
|
+
rotation: safeParseFloat(f[4]),
|
|
27294
|
+
color: f[5] || "#000000",
|
|
27295
|
+
fontSize: safeParseFloat(f[7], 7),
|
|
27296
|
+
textType: f[11] || "",
|
|
27297
|
+
text: f[12] || "",
|
|
27298
|
+
id: f[15] || f[13] || "",
|
|
27299
|
+
isPinPart
|
|
27300
|
+
};
|
|
27301
|
+
} catch {
|
|
27302
|
+
return null;
|
|
27303
|
+
}
|
|
27304
|
+
}
|
|
27215
27305
|
function parsePad(data) {
|
|
27216
27306
|
try {
|
|
27217
27307
|
const f = data.split("~");
|
|
@@ -27379,6 +27469,7 @@ function parseSymbolShapes(shapes) {
|
|
|
27379
27469
|
const polylines = [];
|
|
27380
27470
|
const polygons = [];
|
|
27381
27471
|
const paths = [];
|
|
27472
|
+
const texts = [];
|
|
27382
27473
|
for (const line of shapes) {
|
|
27383
27474
|
if (typeof line !== "string")
|
|
27384
27475
|
continue;
|
|
@@ -27432,10 +27523,15 @@ function parseSymbolShapes(shapes) {
|
|
|
27432
27523
|
paths.push(path);
|
|
27433
27524
|
break;
|
|
27434
27525
|
}
|
|
27435
|
-
case "T":
|
|
27526
|
+
case "T": {
|
|
27527
|
+
const text = parseSymbolText(line);
|
|
27528
|
+
if (text && text.text.trim())
|
|
27529
|
+
texts.push(text);
|
|
27436
27530
|
break;
|
|
27531
|
+
}
|
|
27437
27532
|
}
|
|
27438
27533
|
}
|
|
27534
|
+
associatePinNamesFromTexts(pins, texts);
|
|
27439
27535
|
return {
|
|
27440
27536
|
pins,
|
|
27441
27537
|
rectangles,
|
|
@@ -27444,8 +27540,29 @@ function parseSymbolShapes(shapes) {
|
|
|
27444
27540
|
arcs,
|
|
27445
27541
|
polylines,
|
|
27446
27542
|
polygons,
|
|
27447
|
-
paths
|
|
27448
|
-
|
|
27543
|
+
paths,
|
|
27544
|
+
texts
|
|
27545
|
+
};
|
|
27546
|
+
}
|
|
27547
|
+
function associatePinNamesFromTexts(pins, texts) {
|
|
27548
|
+
const pinpartTexts = texts.filter((t) => t.isPinPart && t.text.trim());
|
|
27549
|
+
for (const text of pinpartTexts) {
|
|
27550
|
+
const textContent = text.text.trim();
|
|
27551
|
+
const jMatch = textContent.match(/^J(\d+)(?:\/(.+))?$/i);
|
|
27552
|
+
if (jMatch) {
|
|
27553
|
+
const pinNum = jMatch[1];
|
|
27554
|
+
const pinFunction = jMatch[2] || textContent;
|
|
27555
|
+
const pin = pins.find((p) => p.number === pinNum);
|
|
27556
|
+
if (pin && (!pin.name || pin.name === pinNum)) {
|
|
27557
|
+
pin.name = pinFunction || textContent;
|
|
27558
|
+
}
|
|
27559
|
+
continue;
|
|
27560
|
+
}
|
|
27561
|
+
const numMatch = textContent.match(/^(\d+)$/);
|
|
27562
|
+
if (numMatch) {
|
|
27563
|
+
continue;
|
|
27564
|
+
}
|
|
27565
|
+
}
|
|
27449
27566
|
}
|
|
27450
27567
|
function parseFootprintShapes(shapes) {
|
|
27451
27568
|
const pads = [];
|
|
@@ -27633,6 +27750,7 @@ class EasyEDAClient {
|
|
|
27633
27750
|
polylines: symbolData.polylines,
|
|
27634
27751
|
polygons: symbolData.polygons,
|
|
27635
27752
|
paths: symbolData.paths,
|
|
27753
|
+
texts: symbolData.texts,
|
|
27636
27754
|
origin: symbolOrigin
|
|
27637
27755
|
},
|
|
27638
27756
|
footprint: {
|
|
@@ -27806,6 +27924,7 @@ class EasyEDACommunityClient {
|
|
|
27806
27924
|
polylines: symbolData.polylines,
|
|
27807
27925
|
polygons: symbolData.polygons,
|
|
27808
27926
|
paths: symbolData.paths,
|
|
27927
|
+
texts: symbolData.texts,
|
|
27809
27928
|
origin: symbolOrigin,
|
|
27810
27929
|
head: dataStr.head || {}
|
|
27811
27930
|
},
|
|
@@ -27832,14 +27951,14 @@ class EasyEDACommunityClient {
|
|
|
27832
27951
|
}
|
|
27833
27952
|
}
|
|
27834
27953
|
var easyedaCommunityClient = new EasyEDACommunityClient;
|
|
27835
|
-
function
|
|
27954
|
+
function isLcscId2(id) {
|
|
27836
27955
|
return /^C\d+$/.test(id);
|
|
27837
27956
|
}
|
|
27838
27957
|
function createComponentService() {
|
|
27839
27958
|
return {
|
|
27840
27959
|
async search(query, options = {}) {
|
|
27841
27960
|
const { source = "lcsc", limit = 20, inStock, basicOnly } = options;
|
|
27842
|
-
if (source === "easyeda-community") {
|
|
27961
|
+
if (source === "community" || source === "easyeda-community") {
|
|
27843
27962
|
const results = await easyedaCommunityClient.search({
|
|
27844
27963
|
query,
|
|
27845
27964
|
limit
|
|
@@ -27856,7 +27975,7 @@ function createComponentService() {
|
|
|
27856
27975
|
return jlcClient.search(query, { limit, inStock, basicOnly });
|
|
27857
27976
|
},
|
|
27858
27977
|
async fetch(id) {
|
|
27859
|
-
if (
|
|
27978
|
+
if (isLcscId2(id)) {
|
|
27860
27979
|
return easyedaClient.getComponentData(id);
|
|
27861
27980
|
}
|
|
27862
27981
|
return null;
|
|
@@ -27884,43 +28003,6 @@ function createComponentService() {
|
|
|
27884
28003
|
}
|
|
27885
28004
|
};
|
|
27886
28005
|
}
|
|
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
28006
|
var PATTERNS = {
|
|
27925
28007
|
resistor: /(\d+(?:\.\d+)?)\s*([kKmMgGrR]?)\s*(?:ohm|Ohm|OHM|Ω|R)?/i,
|
|
27926
28008
|
capacitor: /(\d+(?:\.\d+)?)\s*([pnuμmM]?)[Ff]?\s*(?:[\/\s]*(\d+)\s*[Vv])?/i,
|
|
@@ -28330,6 +28412,29 @@ function parseSvgArcPath(path) {
|
|
|
28330
28412
|
return null;
|
|
28331
28413
|
}
|
|
28332
28414
|
}
|
|
28415
|
+
function interpolateArc(params, segmentsPerQuarter = 4) {
|
|
28416
|
+
const center = svgArcToCenter(params);
|
|
28417
|
+
if (!center) {
|
|
28418
|
+
return [{ x: params.x2, y: params.y2 }];
|
|
28419
|
+
}
|
|
28420
|
+
const { cx, cy, rx, ry, startAngle, deltaAngle } = center;
|
|
28421
|
+
const phiRad = params.phi * Math.PI / 180;
|
|
28422
|
+
const cosPhi = Math.cos(phiRad);
|
|
28423
|
+
const sinPhi = Math.sin(phiRad);
|
|
28424
|
+
const arcDegrees = Math.abs(deltaAngle) * (180 / Math.PI);
|
|
28425
|
+
const numSegments = Math.max(2, Math.ceil(arcDegrees / 90 * segmentsPerQuarter));
|
|
28426
|
+
const points = [];
|
|
28427
|
+
for (let i = 1;i <= numSegments; i++) {
|
|
28428
|
+
const t = i / numSegments;
|
|
28429
|
+
const angle = startAngle + deltaAngle * t;
|
|
28430
|
+
const px = rx * Math.cos(angle);
|
|
28431
|
+
const py = ry * Math.sin(angle);
|
|
28432
|
+
const x = cosPhi * px - sinPhi * py + cx;
|
|
28433
|
+
const y = sinPhi * px + cosPhi * py + cy;
|
|
28434
|
+
points.push({ x, y });
|
|
28435
|
+
}
|
|
28436
|
+
return points;
|
|
28437
|
+
}
|
|
28333
28438
|
var CATEGORY_TO_PREFIX = {
|
|
28334
28439
|
Resistors: "R",
|
|
28335
28440
|
Capacitors: "C",
|
|
@@ -28392,7 +28497,12 @@ class SymbolConverter {
|
|
|
28392
28497
|
return output;
|
|
28393
28498
|
}
|
|
28394
28499
|
hasRenderableShapes(symbol) {
|
|
28395
|
-
|
|
28500
|
+
const pinNamePattern = /^J\d+(?:\/|$)/i;
|
|
28501
|
+
const hasDecorativeText = (symbol.texts || []).some((t) => {
|
|
28502
|
+
const text = t.text.trim();
|
|
28503
|
+
return text && !pinNamePattern.test(text);
|
|
28504
|
+
});
|
|
28505
|
+
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
28506
|
}
|
|
28397
28507
|
generateFromShapes(component, name) {
|
|
28398
28508
|
const { info, symbol } = component;
|
|
@@ -28426,6 +28536,18 @@ class SymbolConverter {
|
|
|
28426
28536
|
if (pathOutput)
|
|
28427
28537
|
output += pathOutput;
|
|
28428
28538
|
}
|
|
28539
|
+
const pinNamePattern = /^J\d+(?:\/|$)/i;
|
|
28540
|
+
const decorativeTexts = (symbol.texts || []).filter((t) => {
|
|
28541
|
+
const text = t.text.trim();
|
|
28542
|
+
if (!text)
|
|
28543
|
+
return false;
|
|
28544
|
+
return !pinNamePattern.test(text);
|
|
28545
|
+
});
|
|
28546
|
+
for (const text of decorativeTexts) {
|
|
28547
|
+
const textOutput = this.convertText(text, origin);
|
|
28548
|
+
if (textOutput)
|
|
28549
|
+
output += textOutput;
|
|
28550
|
+
}
|
|
28429
28551
|
output += ` )
|
|
28430
28552
|
`;
|
|
28431
28553
|
output += ` (symbol "${name}_1_1"
|
|
@@ -28610,6 +28732,24 @@ class SymbolConverter {
|
|
|
28610
28732
|
`;
|
|
28611
28733
|
return output;
|
|
28612
28734
|
}
|
|
28735
|
+
convertText(text, origin) {
|
|
28736
|
+
if (!text.text || text.text.trim() === "")
|
|
28737
|
+
return null;
|
|
28738
|
+
const x = this.convertX(text.x, origin.x);
|
|
28739
|
+
const y = this.convertY(text.y, origin.y);
|
|
28740
|
+
const fontSize = roundTo(Math.max(text.fontSize * EE_TO_MM * 0.8, 0.5), 3);
|
|
28741
|
+
const rotation = (360 - text.rotation) % 360;
|
|
28742
|
+
const content = text.text.replace(/"/g, "'").replace(/\\/g, "");
|
|
28743
|
+
return ` (text "${content}"
|
|
28744
|
+
(at ${x} ${y} ${rotation})
|
|
28745
|
+
(effects
|
|
28746
|
+
(font
|
|
28747
|
+
(size ${fontSize} ${fontSize})
|
|
28748
|
+
)
|
|
28749
|
+
)
|
|
28750
|
+
)
|
|
28751
|
+
`;
|
|
28752
|
+
}
|
|
28613
28753
|
parseSvgPath(pathStr, origin) {
|
|
28614
28754
|
const points = [];
|
|
28615
28755
|
let firstPoint = null;
|
|
@@ -29815,7 +29955,7 @@ class FootprintConverter {
|
|
|
29815
29955
|
return this.generatePad({ ...pad, shape: "RECT", points: "" }, origin);
|
|
29816
29956
|
}
|
|
29817
29957
|
let holeRadius = pad.holeRadius;
|
|
29818
|
-
if (holeRadius === 0 && pad.
|
|
29958
|
+
if (holeRadius === 0 && pad.layerId === 11 && pad.shape === "POLYGON") {
|
|
29819
29959
|
holeRadius = this.calculateDrillRadiusFromPolygon(points, pad.centerX, pad.centerY);
|
|
29820
29960
|
}
|
|
29821
29961
|
const isSmd = holeRadius === 0;
|
|
@@ -30157,12 +30297,26 @@ ${justify ? ` (justify ${justify})
|
|
|
30157
30297
|
break;
|
|
30158
30298
|
case "A":
|
|
30159
30299
|
if (args.length >= 7) {
|
|
30300
|
+
const arcParams = {
|
|
30301
|
+
x1: currentX,
|
|
30302
|
+
y1: currentY,
|
|
30303
|
+
rx: args[0],
|
|
30304
|
+
ry: args[1],
|
|
30305
|
+
phi: args[2],
|
|
30306
|
+
largeArc: args[3] === 1,
|
|
30307
|
+
sweep: args[4] === 1,
|
|
30308
|
+
x2: args[5],
|
|
30309
|
+
y2: args[6]
|
|
30310
|
+
};
|
|
30311
|
+
const arcPoints = interpolateArc(arcParams, 4);
|
|
30312
|
+
for (const pt of arcPoints) {
|
|
30313
|
+
points.push({
|
|
30314
|
+
x: convertX(pt.x, origin.x),
|
|
30315
|
+
y: convertY(pt.y, origin.y)
|
|
30316
|
+
});
|
|
30317
|
+
}
|
|
30160
30318
|
currentX = args[5];
|
|
30161
30319
|
currentY = args[6];
|
|
30162
|
-
points.push({
|
|
30163
|
-
x: convertX(currentX, origin.x),
|
|
30164
|
-
y: convertY(currentY, origin.y)
|
|
30165
|
-
});
|
|
30166
30320
|
}
|
|
30167
30321
|
break;
|
|
30168
30322
|
case "Z":
|
|
@@ -30336,7 +30490,6 @@ init_category_router();
|
|
|
30336
30490
|
var FOOTPRINT_LIBRARY_NAME = getFootprintDirName();
|
|
30337
30491
|
var MODELS_3D_NAME = get3DModelsDirName();
|
|
30338
30492
|
var LIBRARY_NAMESPACE2 = "jlc_mcp";
|
|
30339
|
-
var KICAD_VERSIONS3 = ["9.0", "8.0"];
|
|
30340
30493
|
var EASYEDA_LIBRARY_NAME2 = "EasyEDA";
|
|
30341
30494
|
var EASYEDA_SYMBOL_LIBRARY_NAME2 = "EasyEDA.kicad_sym";
|
|
30342
30495
|
var EASYEDA_FOOTPRINT_LIBRARY_NAME2 = "EasyEDA.pretty";
|
|
@@ -30345,58 +30498,48 @@ var EASYEDA_LOCAL_LIBRARY_NAME = "EasyEDA-local";
|
|
|
30345
30498
|
var EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME = "EasyEDA-local.kicad_sym";
|
|
30346
30499
|
var EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME = "EasyEDA-local.pretty";
|
|
30347
30500
|
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
30501
|
function getGlobalLibraryPaths() {
|
|
30359
|
-
const home =
|
|
30360
|
-
const version2 =
|
|
30361
|
-
const plat =
|
|
30502
|
+
const home = homedir3();
|
|
30503
|
+
const version2 = detectKicadVersion();
|
|
30504
|
+
const plat = platform3();
|
|
30362
30505
|
let base;
|
|
30363
30506
|
if (plat === "linux") {
|
|
30364
|
-
base =
|
|
30507
|
+
base = join5(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE2);
|
|
30365
30508
|
} else {
|
|
30366
|
-
base =
|
|
30509
|
+
base = join5(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE2);
|
|
30367
30510
|
}
|
|
30368
30511
|
return {
|
|
30369
30512
|
base,
|
|
30370
|
-
symbolsDir:
|
|
30371
|
-
footprintsDir:
|
|
30372
|
-
models3dDir:
|
|
30373
|
-
footprintDir:
|
|
30374
|
-
models3dFullDir:
|
|
30513
|
+
symbolsDir: join5(base, "symbols"),
|
|
30514
|
+
footprintsDir: join5(base, "footprints"),
|
|
30515
|
+
models3dDir: join5(base, "3dmodels"),
|
|
30516
|
+
footprintDir: join5(base, "footprints", FOOTPRINT_LIBRARY_NAME),
|
|
30517
|
+
models3dFullDir: join5(base, "3dmodels", MODELS_3D_NAME)
|
|
30375
30518
|
};
|
|
30376
30519
|
}
|
|
30377
30520
|
function getProjectLibraryPaths(projectPath) {
|
|
30378
|
-
const librariesDir =
|
|
30521
|
+
const librariesDir = join5(projectPath, "libraries");
|
|
30379
30522
|
return {
|
|
30380
30523
|
base: librariesDir,
|
|
30381
|
-
symbolsDir:
|
|
30382
|
-
footprintsDir:
|
|
30383
|
-
models3dDir:
|
|
30384
|
-
footprintDir:
|
|
30385
|
-
models3dFullDir:
|
|
30524
|
+
symbolsDir: join5(librariesDir, "symbols"),
|
|
30525
|
+
footprintsDir: join5(librariesDir, "footprints"),
|
|
30526
|
+
models3dDir: join5(librariesDir, "3dmodels"),
|
|
30527
|
+
footprintDir: join5(librariesDir, "footprints", FOOTPRINT_LIBRARY_NAME),
|
|
30528
|
+
models3dFullDir: join5(librariesDir, "3dmodels", MODELS_3D_NAME)
|
|
30386
30529
|
};
|
|
30387
30530
|
}
|
|
30388
|
-
function
|
|
30531
|
+
function isLcscId3(id) {
|
|
30389
30532
|
return /^C\d+$/.test(id);
|
|
30390
30533
|
}
|
|
30391
|
-
function
|
|
30392
|
-
const home =
|
|
30393
|
-
const plat =
|
|
30534
|
+
function getKicadConfigDir3(version2) {
|
|
30535
|
+
const home = homedir3();
|
|
30536
|
+
const plat = platform3();
|
|
30394
30537
|
if (plat === "darwin") {
|
|
30395
|
-
return
|
|
30538
|
+
return join5(home, "Library", "Preferences", "kicad", version2);
|
|
30396
30539
|
} else if (plat === "win32") {
|
|
30397
|
-
return
|
|
30540
|
+
return join5(process.env.APPDATA || join5(home, "AppData", "Roaming"), "kicad", version2);
|
|
30398
30541
|
} else {
|
|
30399
|
-
return
|
|
30542
|
+
return join5(home, ".config", "kicad", version2);
|
|
30400
30543
|
}
|
|
30401
30544
|
}
|
|
30402
30545
|
async function parseSymbolLibrary(filePath, libraryName, category, models3dDir) {
|
|
@@ -30422,8 +30565,8 @@ async function parseSymbolLibrary(filePath, libraryName, category, models3dDir)
|
|
|
30422
30565
|
const name = nameParts.length > 1 ? nameParts[1] : fullSymbolRef;
|
|
30423
30566
|
const fpMatch = symbolBlock.match(/\(property\s+"Footprint"\s+"([^"]+)"/);
|
|
30424
30567
|
const footprintRef = fpMatch ? fpMatch[1] : "";
|
|
30425
|
-
const model3dPath =
|
|
30426
|
-
const has3dModel =
|
|
30568
|
+
const model3dPath = join5(models3dDir, `${name}.step`);
|
|
30569
|
+
const has3dModel = existsSync4(model3dPath);
|
|
30427
30570
|
if (lcscId) {
|
|
30428
30571
|
components.push({
|
|
30429
30572
|
lcscId,
|
|
@@ -30439,6 +30582,42 @@ async function parseSymbolLibrary(filePath, libraryName, category, models3dDir)
|
|
|
30439
30582
|
} catch {}
|
|
30440
30583
|
return components;
|
|
30441
30584
|
}
|
|
30585
|
+
function removeSymbolFromLibrary(content, symbolName) {
|
|
30586
|
+
const lines = content.split(`
|
|
30587
|
+
`);
|
|
30588
|
+
const result = [];
|
|
30589
|
+
let depth = 0;
|
|
30590
|
+
let inTargetSymbol = false;
|
|
30591
|
+
let symbolStartDepth = 0;
|
|
30592
|
+
for (const line of lines) {
|
|
30593
|
+
const openCount = (line.match(/\(/g) || []).length;
|
|
30594
|
+
const closeCount = (line.match(/\)/g) || []).length;
|
|
30595
|
+
const symbolMatch = line.match(/^\s*\(symbol\s+"([^"]+)"/);
|
|
30596
|
+
if (symbolMatch && !inTargetSymbol) {
|
|
30597
|
+
const fullRef = symbolMatch[1];
|
|
30598
|
+
const nameOnly = fullRef.includes(":") ? fullRef.split(":")[1] : fullRef;
|
|
30599
|
+
if (nameOnly === symbolName || nameOnly.startsWith(`${symbolName}_`)) {
|
|
30600
|
+
inTargetSymbol = true;
|
|
30601
|
+
symbolStartDepth = depth;
|
|
30602
|
+
depth += openCount - closeCount;
|
|
30603
|
+
continue;
|
|
30604
|
+
}
|
|
30605
|
+
}
|
|
30606
|
+
if (inTargetSymbol) {
|
|
30607
|
+
depth += openCount - closeCount;
|
|
30608
|
+
if (depth <= symbolStartDepth) {
|
|
30609
|
+
inTargetSymbol = false;
|
|
30610
|
+
}
|
|
30611
|
+
continue;
|
|
30612
|
+
}
|
|
30613
|
+
depth += openCount - closeCount;
|
|
30614
|
+
result.push(line);
|
|
30615
|
+
}
|
|
30616
|
+
return result.join(`
|
|
30617
|
+
`).replace(/\n{3,}/g, `
|
|
30618
|
+
|
|
30619
|
+
`);
|
|
30620
|
+
}
|
|
30442
30621
|
function adaptCommunityComponent(component) {
|
|
30443
30622
|
const symbolHead = component.symbol.head;
|
|
30444
30623
|
const cPara = symbolHead?.c_para || {};
|
|
@@ -30461,6 +30640,7 @@ function adaptCommunityComponent(component) {
|
|
|
30461
30640
|
polylines: component.symbol.polylines,
|
|
30462
30641
|
polygons: component.symbol.polygons,
|
|
30463
30642
|
paths: component.symbol.paths,
|
|
30643
|
+
texts: component.symbol.texts || [],
|
|
30464
30644
|
origin: component.symbol.origin
|
|
30465
30645
|
},
|
|
30466
30646
|
footprint: {
|
|
@@ -30484,11 +30664,11 @@ function adaptCommunityComponent(component) {
|
|
|
30484
30664
|
function createLibraryService() {
|
|
30485
30665
|
return {
|
|
30486
30666
|
async install(id, options = {}) {
|
|
30487
|
-
const isCommunityComponent = !
|
|
30667
|
+
const isCommunityComponent = !isLcscId3(id);
|
|
30488
30668
|
const isGlobal = !options.projectPath;
|
|
30489
30669
|
const paths = isGlobal ? getGlobalLibraryPaths() : getProjectLibraryPaths(options.projectPath);
|
|
30490
30670
|
let component = null;
|
|
30491
|
-
if (
|
|
30671
|
+
if (isLcscId3(id)) {
|
|
30492
30672
|
component = await easyedaClient.getComponentData(id);
|
|
30493
30673
|
} else {
|
|
30494
30674
|
const communityComponent = await easyedaCommunityClient.getComponent(id);
|
|
@@ -30537,18 +30717,18 @@ function createLibraryService() {
|
|
|
30537
30717
|
let easyedaModelsDir;
|
|
30538
30718
|
if (isGlobal) {
|
|
30539
30719
|
symbolsDir = paths.symbolsDir;
|
|
30540
|
-
footprintDir =
|
|
30541
|
-
easyedaModelsDir =
|
|
30720
|
+
footprintDir = join5(paths.footprintsDir, fpLibDir);
|
|
30721
|
+
easyedaModelsDir = join5(paths.models3dDir, models3dDirName);
|
|
30542
30722
|
} else {
|
|
30543
|
-
const librariesDir =
|
|
30544
|
-
symbolsDir =
|
|
30545
|
-
footprintDir =
|
|
30546
|
-
easyedaModelsDir =
|
|
30723
|
+
const librariesDir = join5(options.projectPath, "libraries");
|
|
30724
|
+
symbolsDir = join5(librariesDir, "symbols");
|
|
30725
|
+
footprintDir = join5(librariesDir, "footprints", fpLibDir);
|
|
30726
|
+
easyedaModelsDir = join5(librariesDir, "3dmodels", models3dDirName);
|
|
30547
30727
|
}
|
|
30548
30728
|
models3dDir = easyedaModelsDir;
|
|
30549
30729
|
await ensureDir(symbolsDir);
|
|
30550
30730
|
await ensureDir(footprintDir);
|
|
30551
|
-
symbolFile =
|
|
30731
|
+
symbolFile = join5(symbolsDir, symLibFile);
|
|
30552
30732
|
symbolName = component.info.name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
30553
30733
|
const include3d = options.include3d ?? true;
|
|
30554
30734
|
let modelRelativePath;
|
|
@@ -30557,7 +30737,7 @@ function createLibraryService() {
|
|
|
30557
30737
|
const model = await easyedaCommunityClient.get3DModel(component.model3d.uuid, "step");
|
|
30558
30738
|
if (model) {
|
|
30559
30739
|
const modelFilename = `${symbolName}.step`;
|
|
30560
|
-
modelPath =
|
|
30740
|
+
modelPath = join5(models3dDir, modelFilename);
|
|
30561
30741
|
await writeBinary(modelPath, model);
|
|
30562
30742
|
if (isGlobal) {
|
|
30563
30743
|
modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${models3dDirName}/${modelFilename}`;
|
|
@@ -30571,7 +30751,7 @@ function createLibraryService() {
|
|
|
30571
30751
|
include3DModel: !!modelRelativePath,
|
|
30572
30752
|
modelPath: modelRelativePath
|
|
30573
30753
|
});
|
|
30574
|
-
footprintPath =
|
|
30754
|
+
footprintPath = join5(footprintDir, `${symbolName}.kicad_mod`);
|
|
30575
30755
|
footprintRef = `${libName}:${symbolName}`;
|
|
30576
30756
|
await writeText(footprintPath, footprint);
|
|
30577
30757
|
component.info.package = footprintRef;
|
|
@@ -30586,20 +30766,20 @@ function createLibraryService() {
|
|
|
30586
30766
|
} else {
|
|
30587
30767
|
category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
|
|
30588
30768
|
const symbolLibraryFilename = getLibraryFilename(category);
|
|
30589
|
-
symbolFile =
|
|
30769
|
+
symbolFile = join5(paths.symbolsDir, symbolLibraryFilename);
|
|
30590
30770
|
footprintDir = paths.footprintDir;
|
|
30591
30771
|
models3dDir = paths.models3dFullDir;
|
|
30592
30772
|
await ensureDir(paths.symbolsDir);
|
|
30593
30773
|
await ensureDir(paths.footprintDir);
|
|
30594
30774
|
symbolName = symbolConverter.getSymbolName(component);
|
|
30595
|
-
const include3d = options.include3d ??
|
|
30775
|
+
const include3d = options.include3d ?? true;
|
|
30596
30776
|
let modelRelativePath;
|
|
30597
30777
|
if (include3d && component.model3d) {
|
|
30598
30778
|
await ensureDir(models3dDir);
|
|
30599
30779
|
const model = await easyedaClient.get3DModel(component.model3d.uuid, "step");
|
|
30600
30780
|
if (model) {
|
|
30601
30781
|
const modelFilename = `${symbolName}.step`;
|
|
30602
|
-
modelPath =
|
|
30782
|
+
modelPath = join5(models3dDir, modelFilename);
|
|
30603
30783
|
await writeBinary(modelPath, model);
|
|
30604
30784
|
modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${MODELS_3D_NAME}/${modelFilename}`;
|
|
30605
30785
|
}
|
|
@@ -30611,7 +30791,7 @@ function createLibraryService() {
|
|
|
30611
30791
|
if (footprintResult.type === "reference") {
|
|
30612
30792
|
footprintRef = footprintResult.reference;
|
|
30613
30793
|
} else {
|
|
30614
|
-
footprintPath =
|
|
30794
|
+
footprintPath = join5(footprintDir, `${footprintResult.name}.kicad_mod`);
|
|
30615
30795
|
footprintRef = getFootprintReference(footprintResult.name);
|
|
30616
30796
|
await writeText(footprintPath, footprintResult.content);
|
|
30617
30797
|
}
|
|
@@ -30624,7 +30804,7 @@ function createLibraryService() {
|
|
|
30624
30804
|
}
|
|
30625
30805
|
let symbolContent;
|
|
30626
30806
|
let symbolAction;
|
|
30627
|
-
if (
|
|
30807
|
+
if (existsSync4(symbolFile)) {
|
|
30628
30808
|
const existingContent = await readFile4(symbolFile, "utf-8");
|
|
30629
30809
|
if (symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
|
|
30630
30810
|
if (options.force) {
|
|
@@ -30716,9 +30896,9 @@ function createLibraryService() {
|
|
|
30716
30896
|
if (options.category && options.category !== category)
|
|
30717
30897
|
continue;
|
|
30718
30898
|
const libraryFilename = getLibraryFilename(category);
|
|
30719
|
-
const libraryPath =
|
|
30899
|
+
const libraryPath = join5(paths.symbolsDir, libraryFilename);
|
|
30720
30900
|
const libraryName = `JLC-MCP-${category}`;
|
|
30721
|
-
if (
|
|
30901
|
+
if (existsSync4(libraryPath)) {
|
|
30722
30902
|
const components = await parseSymbolLibrary(libraryPath, libraryName, category, paths.models3dFullDir);
|
|
30723
30903
|
allComponents.push(...components);
|
|
30724
30904
|
}
|
|
@@ -30733,13 +30913,13 @@ function createLibraryService() {
|
|
|
30733
30913
|
await ensureGlobalLibraryTables2();
|
|
30734
30914
|
},
|
|
30735
30915
|
async getStatus() {
|
|
30736
|
-
const version2 =
|
|
30916
|
+
const version2 = detectKicadVersion();
|
|
30737
30917
|
const paths = getGlobalLibraryPaths();
|
|
30738
|
-
const configDir =
|
|
30739
|
-
const symLibTablePath =
|
|
30740
|
-
const fpLibTablePath =
|
|
30918
|
+
const configDir = getKicadConfigDir3(version2);
|
|
30919
|
+
const symLibTablePath = join5(configDir, "sym-lib-table");
|
|
30920
|
+
const fpLibTablePath = join5(configDir, "fp-lib-table");
|
|
30741
30921
|
let installed = false;
|
|
30742
|
-
if (
|
|
30922
|
+
if (existsSync4(paths.symbolsDir)) {
|
|
30743
30923
|
try {
|
|
30744
30924
|
const files = await readdir2(paths.symbolsDir);
|
|
30745
30925
|
installed = files.some((f) => f.endsWith(".kicad_sym"));
|
|
@@ -30748,7 +30928,7 @@ function createLibraryService() {
|
|
|
30748
30928
|
}
|
|
30749
30929
|
}
|
|
30750
30930
|
let linked = false;
|
|
30751
|
-
if (
|
|
30931
|
+
if (existsSync4(symLibTablePath)) {
|
|
30752
30932
|
try {
|
|
30753
30933
|
const content = await readFile4(symLibTablePath, "utf-8");
|
|
30754
30934
|
linked = content.includes("JLC-MCP");
|
|
@@ -30774,8 +30954,8 @@ function createLibraryService() {
|
|
|
30774
30954
|
},
|
|
30775
30955
|
async isEasyEDAInstalled(componentName) {
|
|
30776
30956
|
const paths = getGlobalLibraryPaths();
|
|
30777
|
-
const easyedaLibPath =
|
|
30778
|
-
if (!
|
|
30957
|
+
const easyedaLibPath = join5(paths.symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME2);
|
|
30958
|
+
if (!existsSync4(easyedaLibPath)) {
|
|
30779
30959
|
return false;
|
|
30780
30960
|
}
|
|
30781
30961
|
try {
|
|
@@ -30786,6 +30966,85 @@ function createLibraryService() {
|
|
|
30786
30966
|
} catch {
|
|
30787
30967
|
return false;
|
|
30788
30968
|
}
|
|
30969
|
+
},
|
|
30970
|
+
async regenerate(options = {}) {
|
|
30971
|
+
const include3d = options.include3d ?? true;
|
|
30972
|
+
const installed = await this.listInstalled({ projectPath: options.projectPath });
|
|
30973
|
+
const results = {
|
|
30974
|
+
total: installed.length,
|
|
30975
|
+
success: 0,
|
|
30976
|
+
failed: 0,
|
|
30977
|
+
components: []
|
|
30978
|
+
};
|
|
30979
|
+
for (let i = 0;i < installed.length; i++) {
|
|
30980
|
+
const component = installed[i];
|
|
30981
|
+
options.onProgress?.(i + 1, installed.length, component, "start");
|
|
30982
|
+
try {
|
|
30983
|
+
await this.install(component.lcscId, {
|
|
30984
|
+
projectPath: options.projectPath,
|
|
30985
|
+
include3d,
|
|
30986
|
+
force: true
|
|
30987
|
+
});
|
|
30988
|
+
results.success++;
|
|
30989
|
+
results.components.push({
|
|
30990
|
+
id: component.lcscId,
|
|
30991
|
+
name: component.name,
|
|
30992
|
+
status: "success"
|
|
30993
|
+
});
|
|
30994
|
+
options.onProgress?.(i + 1, installed.length, component, "success");
|
|
30995
|
+
} catch (error2) {
|
|
30996
|
+
const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
|
|
30997
|
+
results.failed++;
|
|
30998
|
+
results.components.push({
|
|
30999
|
+
id: component.lcscId,
|
|
31000
|
+
name: component.name,
|
|
31001
|
+
status: "failed",
|
|
31002
|
+
error: errorMessage
|
|
31003
|
+
});
|
|
31004
|
+
options.onProgress?.(i + 1, installed.length, component, "error", errorMessage);
|
|
31005
|
+
}
|
|
31006
|
+
}
|
|
31007
|
+
return results;
|
|
31008
|
+
},
|
|
31009
|
+
async remove(lcscId, options = {}) {
|
|
31010
|
+
const paths = options.projectPath ? getProjectLibraryPaths(options.projectPath) : getGlobalLibraryPaths();
|
|
31011
|
+
const installed = await this.listInstalled({ projectPath: options.projectPath });
|
|
31012
|
+
const component = installed.find((c) => c.lcscId === lcscId);
|
|
31013
|
+
if (!component) {
|
|
31014
|
+
throw new Error(`Component ${lcscId} not found in library`);
|
|
31015
|
+
}
|
|
31016
|
+
const result = {
|
|
31017
|
+
success: false,
|
|
31018
|
+
lcscId,
|
|
31019
|
+
symbolRemoved: false,
|
|
31020
|
+
footprintRemoved: false,
|
|
31021
|
+
model3dRemoved: false
|
|
31022
|
+
};
|
|
31023
|
+
const libraryFilename = getLibraryFilename(component.category);
|
|
31024
|
+
const symbolLibPath = join5(paths.symbolsDir, libraryFilename);
|
|
31025
|
+
if (existsSync4(symbolLibPath)) {
|
|
31026
|
+
const content = await readFile4(symbolLibPath, "utf-8");
|
|
31027
|
+
const newContent = removeSymbolFromLibrary(content, component.name);
|
|
31028
|
+
await writeText(symbolLibPath, newContent);
|
|
31029
|
+
result.symbolRemoved = true;
|
|
31030
|
+
}
|
|
31031
|
+
if (component.footprintRef?.startsWith("JLC-MCP:")) {
|
|
31032
|
+
const fpName = component.footprintRef.split(":")[1];
|
|
31033
|
+
const footprintPath = join5(paths.footprintDir, `${fpName}.kicad_mod`);
|
|
31034
|
+
if (existsSync4(footprintPath)) {
|
|
31035
|
+
await unlink(footprintPath);
|
|
31036
|
+
result.footprintRemoved = true;
|
|
31037
|
+
}
|
|
31038
|
+
}
|
|
31039
|
+
if (component.has3dModel) {
|
|
31040
|
+
const modelPath = join5(paths.models3dFullDir, `${component.name}.step`);
|
|
31041
|
+
if (existsSync4(modelPath)) {
|
|
31042
|
+
await unlink(modelPath);
|
|
31043
|
+
result.model3dRemoved = true;
|
|
31044
|
+
}
|
|
31045
|
+
}
|
|
31046
|
+
result.success = result.symbolRemoved;
|
|
31047
|
+
return result;
|
|
30789
31048
|
}
|
|
30790
31049
|
};
|
|
30791
31050
|
}
|
|
@@ -30801,16 +31060,16 @@ function getHtmlPage() {
|
|
|
30801
31060
|
return htmlCache;
|
|
30802
31061
|
try {
|
|
30803
31062
|
const possiblePaths = [
|
|
30804
|
-
|
|
30805
|
-
|
|
30806
|
-
|
|
30807
|
-
|
|
30808
|
-
|
|
30809
|
-
|
|
30810
|
-
|
|
30811
|
-
|
|
30812
|
-
|
|
30813
|
-
|
|
31063
|
+
join6(__dirname2, "assets/search.html"),
|
|
31064
|
+
join6(__dirname2, "../dist/assets/search.html"),
|
|
31065
|
+
join6(__dirname2, "../assets/search.html"),
|
|
31066
|
+
join6(__dirname2, "../assets/search-built.html"),
|
|
31067
|
+
join6(process.cwd(), "dist/assets/search.html"),
|
|
31068
|
+
join6(process.cwd(), "packages/core/dist/assets/search.html"),
|
|
31069
|
+
join6(__dirname2, "../../dist/assets/search.html"),
|
|
31070
|
+
join6(__dirname2, "../../../core/dist/assets/search.html"),
|
|
31071
|
+
join6(__dirname2, "../../../../packages/core/dist/assets/search.html"),
|
|
31072
|
+
join6(__dirname2, "../../node_modules/@jlcpcb/core/dist/assets/search.html")
|
|
30814
31073
|
];
|
|
30815
31074
|
for (const path of possiblePaths) {
|
|
30816
31075
|
try {
|
|
@@ -31001,17 +31260,51 @@ function startHttpServer(options = {}) {
|
|
|
31001
31260
|
});
|
|
31002
31261
|
return port;
|
|
31003
31262
|
}
|
|
31263
|
+
var EE_TO_MM3 = 10 * 0.0254;
|
|
31264
|
+
var EE_TO_MM4 = 10 * 0.0254;
|
|
31265
|
+
var EE_TO_MM5 = 10 * 0.0254;
|
|
31266
|
+
|
|
31267
|
+
// src/services.ts
|
|
31268
|
+
var componentService = null;
|
|
31269
|
+
var libraryService = null;
|
|
31270
|
+
function getComponentService() {
|
|
31271
|
+
if (!componentService) {
|
|
31272
|
+
componentService = createComponentService();
|
|
31273
|
+
}
|
|
31274
|
+
return componentService;
|
|
31275
|
+
}
|
|
31276
|
+
function getLibraryService() {
|
|
31277
|
+
if (!libraryService) {
|
|
31278
|
+
libraryService = createLibraryService();
|
|
31279
|
+
}
|
|
31280
|
+
return libraryService;
|
|
31281
|
+
}
|
|
31004
31282
|
|
|
31005
31283
|
// src/tools/search.ts
|
|
31006
|
-
var
|
|
31284
|
+
var componentSearchTool = {
|
|
31007
31285
|
name: "component_search",
|
|
31008
|
-
description:
|
|
31286
|
+
description: `Search for electronic components by keyword.
|
|
31287
|
+
|
|
31288
|
+
Sources:
|
|
31289
|
+
- "lcsc" (default): Search JLCPCB/LCSC official parts library. Returns components with LCSC IDs available for JLC assembly.
|
|
31290
|
+
- "community": Search EasyEDA community library for user-contributed parts (Arduino modules, XIAO, custom breakouts).
|
|
31291
|
+
|
|
31292
|
+
Returns full component details for selection including:
|
|
31293
|
+
- LCSC ID, manufacturer part number, manufacturer name
|
|
31294
|
+
- Description, package, datasheet URL
|
|
31295
|
+
- Price, stock, library type (basic/extended)
|
|
31296
|
+
- Key attributes (resistance, voltage, etc.)`,
|
|
31009
31297
|
inputSchema: {
|
|
31010
31298
|
type: "object",
|
|
31011
31299
|
properties: {
|
|
31012
31300
|
query: {
|
|
31013
31301
|
type: "string",
|
|
31014
|
-
description: 'Search query (e.g., "ESP32", "STM32F103", "0805 100nF")'
|
|
31302
|
+
description: 'Search query (e.g., "ESP32", "STM32F103", "0805 100nF", "XIAO RP2040")'
|
|
31303
|
+
},
|
|
31304
|
+
source: {
|
|
31305
|
+
type: "string",
|
|
31306
|
+
enum: ["lcsc", "community"],
|
|
31307
|
+
description: 'Search source: "lcsc" for official parts (default), "community" for EasyEDA user-contributed'
|
|
31015
31308
|
},
|
|
31016
31309
|
limit: {
|
|
31017
31310
|
type: "number",
|
|
@@ -31019,11 +31312,11 @@ var searchComponentsTool = {
|
|
|
31019
31312
|
},
|
|
31020
31313
|
in_stock: {
|
|
31021
31314
|
type: "boolean",
|
|
31022
|
-
description: "Only show in-stock items (default: false)"
|
|
31315
|
+
description: "Only show in-stock items (default: false, LCSC only)"
|
|
31023
31316
|
},
|
|
31024
31317
|
basic_only: {
|
|
31025
31318
|
type: "boolean",
|
|
31026
|
-
description: "Only show JLCPCB Basic Parts Library components (lower assembly cost,
|
|
31319
|
+
description: "Only show JLCPCB Basic Parts Library components (lower assembly cost, LCSC only)"
|
|
31027
31320
|
}
|
|
31028
31321
|
},
|
|
31029
31322
|
required: ["query"]
|
|
@@ -31031,112 +31324,64 @@ var searchComponentsTool = {
|
|
|
31031
31324
|
};
|
|
31032
31325
|
var SearchParamsSchema = exports_external.object({
|
|
31033
31326
|
query: exports_external.string().min(1),
|
|
31327
|
+
source: exports_external.enum(["lcsc", "community"]).default("lcsc"),
|
|
31034
31328
|
limit: exports_external.number().min(1).max(50).default(10),
|
|
31035
31329
|
in_stock: exports_external.boolean().optional(),
|
|
31036
31330
|
basic_only: exports_external.boolean().optional()
|
|
31037
31331
|
});
|
|
31038
|
-
async function
|
|
31332
|
+
async function handleComponentSearch(args) {
|
|
31039
31333
|
const params = SearchParamsSchema.parse(args);
|
|
31040
|
-
const results = await
|
|
31334
|
+
const results = await getComponentService().search(params.query, {
|
|
31335
|
+
source: params.source,
|
|
31041
31336
|
limit: params.limit,
|
|
31042
31337
|
inStock: params.in_stock,
|
|
31043
31338
|
basicOnly: params.basic_only
|
|
31044
31339
|
});
|
|
31045
|
-
|
|
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) {
|
|
31340
|
+
if (results.length === 0) {
|
|
31075
31341
|
return {
|
|
31076
31342
|
content: [{
|
|
31077
31343
|
type: "text",
|
|
31078
|
-
text:
|
|
31079
|
-
|
|
31080
|
-
|
|
31344
|
+
text: JSON.stringify({
|
|
31345
|
+
success: true,
|
|
31346
|
+
query: params.query,
|
|
31347
|
+
source: params.source,
|
|
31348
|
+
count: 0,
|
|
31349
|
+
results: [],
|
|
31350
|
+
hint: params.source === "lcsc" ? 'Try searching with source: "community" for user-contributed parts' : 'Try searching with source: "lcsc" for official JLCPCB parts'
|
|
31351
|
+
})
|
|
31352
|
+
}]
|
|
31081
31353
|
};
|
|
31082
31354
|
}
|
|
31083
|
-
const
|
|
31084
|
-
|
|
31085
|
-
|
|
31086
|
-
|
|
31087
|
-
|
|
31088
|
-
|
|
31089
|
-
|
|
31090
|
-
|
|
31091
|
-
|
|
31092
|
-
|
|
31093
|
-
|
|
31094
|
-
|
|
31095
|
-
|
|
31096
|
-
model_3d_uuid: component.model3d?.uuid
|
|
31097
|
-
};
|
|
31355
|
+
const compactResults = results.map((r) => ({
|
|
31356
|
+
lcsc_id: r.lcscId,
|
|
31357
|
+
name: r.name,
|
|
31358
|
+
manufacturer: r.manufacturer,
|
|
31359
|
+
description: r.description,
|
|
31360
|
+
package: r.package,
|
|
31361
|
+
datasheet: r.datasheetPdf,
|
|
31362
|
+
stock: r.stock,
|
|
31363
|
+
price: r.price,
|
|
31364
|
+
library_type: r.libraryType,
|
|
31365
|
+
category: r.category,
|
|
31366
|
+
attributes: r.attributes
|
|
31367
|
+
}));
|
|
31098
31368
|
return {
|
|
31099
31369
|
content: [{
|
|
31100
31370
|
type: "text",
|
|
31101
|
-
text: JSON.stringify(
|
|
31371
|
+
text: JSON.stringify({
|
|
31372
|
+
success: true,
|
|
31373
|
+
query: params.query,
|
|
31374
|
+
source: params.source,
|
|
31375
|
+
count: results.length,
|
|
31376
|
+
results: compactResults
|
|
31377
|
+
})
|
|
31102
31378
|
}]
|
|
31103
31379
|
};
|
|
31104
31380
|
}
|
|
31105
|
-
|
|
31106
31381
|
// src/tools/library.ts
|
|
31107
|
-
var
|
|
31108
|
-
|
|
31109
|
-
|
|
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.
|
|
31382
|
+
var libraryInstallTool = {
|
|
31383
|
+
name: "library_install",
|
|
31384
|
+
description: `Install a component to KiCad libraries.
|
|
31140
31385
|
|
|
31141
31386
|
Accepts:
|
|
31142
31387
|
- LCSC part numbers (e.g., C2040) → global JLC-MCP libraries
|
|
@@ -31146,11 +31391,7 @@ LCSC components are routed to category-based global libraries:
|
|
|
31146
31391
|
- JLC-MCP-Resistors.kicad_sym, JLC-MCP-Capacitors.kicad_sym, JLC-MCP-ICs.kicad_sym, etc.
|
|
31147
31392
|
- Stored at ~/Documents/KiCad/{version}/3rdparty/jlc_mcp/
|
|
31148
31393
|
|
|
31149
|
-
|
|
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.`,
|
|
31394
|
+
Returns symbol_ref and footprint_ref for use with schematic placement.`,
|
|
31154
31395
|
inputSchema: {
|
|
31155
31396
|
type: "object",
|
|
31156
31397
|
properties: {
|
|
@@ -31164,89 +31405,47 @@ Returns symbol_ref and footprint_ref for immediate use with add_schematic_compon
|
|
|
31164
31405
|
},
|
|
31165
31406
|
include_3d: {
|
|
31166
31407
|
type: "boolean",
|
|
31167
|
-
description: "Include 3D model if available (default: false
|
|
31408
|
+
description: "Include 3D model if available (default: false)"
|
|
31409
|
+
},
|
|
31410
|
+
force: {
|
|
31411
|
+
type: "boolean",
|
|
31412
|
+
description: "Reinstall even if already exists (default: false)"
|
|
31168
31413
|
}
|
|
31169
31414
|
},
|
|
31170
31415
|
required: ["id"]
|
|
31171
31416
|
}
|
|
31172
31417
|
};
|
|
31173
|
-
var
|
|
31174
|
-
name: "
|
|
31175
|
-
description:
|
|
31418
|
+
var libraryGetComponentTool = {
|
|
31419
|
+
name: "library_get_component",
|
|
31420
|
+
description: `Get metadata for an installed component's symbol and footprint.
|
|
31421
|
+
|
|
31422
|
+
Returns symbol reference, footprint reference, file paths, and pin/pad counts.
|
|
31423
|
+
Does NOT return full file contents to minimize token usage.
|
|
31424
|
+
|
|
31425
|
+
Use this to verify installation or get references for schematic placement.`,
|
|
31176
31426
|
inputSchema: {
|
|
31177
31427
|
type: "object",
|
|
31178
31428
|
properties: {
|
|
31179
|
-
|
|
31180
|
-
type: "string",
|
|
31181
|
-
description: "3D model UUID from component_get result"
|
|
31182
|
-
},
|
|
31183
|
-
format: {
|
|
31429
|
+
id: {
|
|
31184
31430
|
type: "string",
|
|
31185
|
-
|
|
31186
|
-
description: 'Model format: "step" or "obj" (default: step)'
|
|
31431
|
+
description: "LCSC part number (e.g., C2040)"
|
|
31187
31432
|
}
|
|
31188
31433
|
},
|
|
31189
|
-
required: ["
|
|
31434
|
+
required: ["id"]
|
|
31190
31435
|
}
|
|
31191
31436
|
};
|
|
31192
|
-
var
|
|
31193
|
-
lcsc_id: exports_external.string().regex(/^C\d+$/, "Invalid LCSC part number")
|
|
31194
|
-
});
|
|
31195
|
-
var FetchLibraryParamsSchema = exports_external.object({
|
|
31437
|
+
var LibraryInstallParamsSchema = exports_external.object({
|
|
31196
31438
|
id: exports_external.string().min(1),
|
|
31197
|
-
project_path:
|
|
31198
|
-
include_3d: exports_external.boolean().optional()
|
|
31439
|
+
project_path: SafePathSchema.optional(),
|
|
31440
|
+
include_3d: exports_external.boolean().optional(),
|
|
31441
|
+
force: exports_external.boolean().optional()
|
|
31199
31442
|
});
|
|
31200
|
-
var
|
|
31201
|
-
|
|
31202
|
-
format: exports_external.enum(["step", "obj"]).default("step")
|
|
31443
|
+
var LibraryGetComponentParamsSchema = exports_external.object({
|
|
31444
|
+
id: LCSCPartNumberSchema
|
|
31203
31445
|
});
|
|
31204
|
-
async function
|
|
31205
|
-
const params =
|
|
31206
|
-
const
|
|
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);
|
|
31446
|
+
async function handleLibraryInstall(args) {
|
|
31447
|
+
const params = LibraryInstallParamsSchema.parse(args);
|
|
31448
|
+
const isCommunityComponent = !isLcscId(params.id);
|
|
31250
31449
|
if (isCommunityComponent && !params.project_path) {
|
|
31251
31450
|
return {
|
|
31252
31451
|
content: [{
|
|
@@ -31262,9 +31461,10 @@ async function handleFetchLibrary(args) {
|
|
|
31262
31461
|
};
|
|
31263
31462
|
}
|
|
31264
31463
|
try {
|
|
31265
|
-
const result = await
|
|
31464
|
+
const result = await getLibraryService().install(params.id, {
|
|
31266
31465
|
projectPath: params.project_path,
|
|
31267
|
-
include3d: params.include_3d
|
|
31466
|
+
include3d: params.include_3d,
|
|
31467
|
+
force: params.force
|
|
31268
31468
|
});
|
|
31269
31469
|
return {
|
|
31270
31470
|
content: [{
|
|
@@ -31272,22 +31472,23 @@ async function handleFetchLibrary(args) {
|
|
|
31272
31472
|
text: JSON.stringify({
|
|
31273
31473
|
success: true,
|
|
31274
31474
|
id: params.id,
|
|
31275
|
-
|
|
31276
|
-
storage_mode: result.storageMode,
|
|
31277
|
-
category: result.category,
|
|
31278
|
-
symbol_name: result.symbolName,
|
|
31475
|
+
installed: true,
|
|
31279
31476
|
symbol_ref: result.symbolRef,
|
|
31280
31477
|
footprint_ref: result.footprintRef,
|
|
31281
|
-
|
|
31282
|
-
datasheet: result.datasheet,
|
|
31478
|
+
category: result.category,
|
|
31283
31479
|
files: {
|
|
31284
31480
|
symbol_library: result.files.symbolLibrary,
|
|
31285
31481
|
footprint: result.files.footprint,
|
|
31286
31482
|
model_3d: result.files.model3d
|
|
31287
31483
|
},
|
|
31288
|
-
|
|
31289
|
-
|
|
31290
|
-
|
|
31484
|
+
validation: result.validationData ? {
|
|
31485
|
+
pin_pad_match: result.validationData.checks.pin_pad_count_match,
|
|
31486
|
+
pin_count: result.validationData.symbol.pin_count,
|
|
31487
|
+
pad_count: result.validationData.footprint.pad_count,
|
|
31488
|
+
has_power_pins: result.validationData.checks.has_power_pins,
|
|
31489
|
+
has_ground_pins: result.validationData.checks.has_ground_pins
|
|
31490
|
+
} : undefined
|
|
31491
|
+
})
|
|
31291
31492
|
}]
|
|
31292
31493
|
};
|
|
31293
31494
|
} catch (error2) {
|
|
@@ -31297,76 +31498,202 @@ async function handleFetchLibrary(args) {
|
|
|
31297
31498
|
text: JSON.stringify({
|
|
31298
31499
|
success: false,
|
|
31299
31500
|
error: error2 instanceof Error ? error2.message : "Unknown error",
|
|
31300
|
-
id: params.id
|
|
31301
|
-
source: isCommunityComponent ? "easyeda_community" : "lcsc"
|
|
31501
|
+
id: params.id
|
|
31302
31502
|
})
|
|
31303
31503
|
}],
|
|
31304
31504
|
isError: true
|
|
31305
31505
|
};
|
|
31306
31506
|
}
|
|
31307
31507
|
}
|
|
31308
|
-
async function
|
|
31309
|
-
const params =
|
|
31310
|
-
|
|
31311
|
-
|
|
31508
|
+
async function handleLibraryGetComponent(args) {
|
|
31509
|
+
const params = LibraryGetComponentParamsSchema.parse(args);
|
|
31510
|
+
try {
|
|
31511
|
+
const status = await getLibraryService().getStatus();
|
|
31512
|
+
if (!status.installed) {
|
|
31513
|
+
return {
|
|
31514
|
+
content: [{
|
|
31515
|
+
type: "text",
|
|
31516
|
+
text: JSON.stringify({
|
|
31517
|
+
success: false,
|
|
31518
|
+
error: "JLC-MCP libraries not installed. Run library_install first.",
|
|
31519
|
+
id: params.id
|
|
31520
|
+
})
|
|
31521
|
+
}],
|
|
31522
|
+
isError: true
|
|
31523
|
+
};
|
|
31524
|
+
}
|
|
31525
|
+
const installed = await getLibraryService().listInstalled();
|
|
31526
|
+
const component = installed.find((c) => c.lcscId === params.id);
|
|
31527
|
+
if (!component) {
|
|
31528
|
+
return {
|
|
31529
|
+
content: [{
|
|
31530
|
+
type: "text",
|
|
31531
|
+
text: JSON.stringify({
|
|
31532
|
+
success: false,
|
|
31533
|
+
error: `Component ${params.id} is not installed`,
|
|
31534
|
+
id: params.id,
|
|
31535
|
+
hint: "Use library_install to add this component first"
|
|
31536
|
+
})
|
|
31537
|
+
}],
|
|
31538
|
+
isError: true
|
|
31539
|
+
};
|
|
31540
|
+
}
|
|
31541
|
+
return {
|
|
31542
|
+
content: [{
|
|
31543
|
+
type: "text",
|
|
31544
|
+
text: JSON.stringify({
|
|
31545
|
+
success: true,
|
|
31546
|
+
id: params.id,
|
|
31547
|
+
installed: true,
|
|
31548
|
+
symbol_ref: component.symbolRef,
|
|
31549
|
+
footprint_ref: component.footprintRef,
|
|
31550
|
+
category: component.category,
|
|
31551
|
+
symbol_library: component.library,
|
|
31552
|
+
name: component.name,
|
|
31553
|
+
has_3d_model: component.has3dModel
|
|
31554
|
+
})
|
|
31555
|
+
}]
|
|
31556
|
+
};
|
|
31557
|
+
} catch (error2) {
|
|
31312
31558
|
return {
|
|
31313
31559
|
content: [{
|
|
31314
31560
|
type: "text",
|
|
31315
|
-
text:
|
|
31561
|
+
text: JSON.stringify({
|
|
31562
|
+
success: false,
|
|
31563
|
+
error: error2 instanceof Error ? error2.message : "Unknown error",
|
|
31564
|
+
id: params.id
|
|
31565
|
+
})
|
|
31316
31566
|
}],
|
|
31317
31567
|
isError: true
|
|
31318
31568
|
};
|
|
31319
31569
|
}
|
|
31570
|
+
}
|
|
31571
|
+
|
|
31572
|
+
// src/tools/batch.ts
|
|
31573
|
+
var libraryBatchInstallTool = {
|
|
31574
|
+
name: "library_batch_install",
|
|
31575
|
+
description: `Install multiple components to KiCad libraries in a single call.
|
|
31576
|
+
|
|
31577
|
+
Accepts up to 10 LCSC part numbers. Components are installed in parallel.
|
|
31578
|
+
Returns a summary of installed, skipped (already installed), and failed components.
|
|
31579
|
+
|
|
31580
|
+
Use this when you need to install a bill of materials or multiple components at once.`,
|
|
31581
|
+
inputSchema: {
|
|
31582
|
+
type: "object",
|
|
31583
|
+
properties: {
|
|
31584
|
+
ids: {
|
|
31585
|
+
type: "array",
|
|
31586
|
+
items: { type: "string" },
|
|
31587
|
+
maxItems: 10,
|
|
31588
|
+
description: 'Array of LCSC part numbers (e.g., ["C2040", "C5446", "C14663"])'
|
|
31589
|
+
},
|
|
31590
|
+
force: {
|
|
31591
|
+
type: "boolean",
|
|
31592
|
+
description: "Reinstall even if already exists (default: false)"
|
|
31593
|
+
},
|
|
31594
|
+
include_3d: {
|
|
31595
|
+
type: "boolean",
|
|
31596
|
+
description: "Include 3D models if available (default: false)"
|
|
31597
|
+
}
|
|
31598
|
+
},
|
|
31599
|
+
required: ["ids"]
|
|
31600
|
+
}
|
|
31601
|
+
};
|
|
31602
|
+
var BatchInstallParamsSchema = exports_external.object({
|
|
31603
|
+
ids: exports_external.array(LCSCPartNumberSchema).min(1).max(10),
|
|
31604
|
+
force: exports_external.boolean().optional(),
|
|
31605
|
+
include_3d: exports_external.boolean().optional()
|
|
31606
|
+
});
|
|
31607
|
+
async function handleLibraryBatchInstall(args) {
|
|
31608
|
+
const params = BatchInstallParamsSchema.parse(args);
|
|
31609
|
+
const results = [];
|
|
31610
|
+
let installed = 0;
|
|
31611
|
+
let skipped = 0;
|
|
31612
|
+
let failed = 0;
|
|
31613
|
+
const installPromises = params.ids.map(async (id) => {
|
|
31614
|
+
try {
|
|
31615
|
+
const result = await getLibraryService().install(id, {
|
|
31616
|
+
include3d: params.include_3d,
|
|
31617
|
+
force: params.force
|
|
31618
|
+
});
|
|
31619
|
+
if (result.symbolAction === "exists") {
|
|
31620
|
+
return {
|
|
31621
|
+
id,
|
|
31622
|
+
status: "skipped",
|
|
31623
|
+
symbol_ref: result.symbolRef,
|
|
31624
|
+
footprint_ref: result.footprintRef,
|
|
31625
|
+
reason: "already installed"
|
|
31626
|
+
};
|
|
31627
|
+
}
|
|
31628
|
+
return {
|
|
31629
|
+
id,
|
|
31630
|
+
status: "installed",
|
|
31631
|
+
symbol_ref: result.symbolRef,
|
|
31632
|
+
footprint_ref: result.footprintRef
|
|
31633
|
+
};
|
|
31634
|
+
} catch (error2) {
|
|
31635
|
+
return {
|
|
31636
|
+
id,
|
|
31637
|
+
status: "failed",
|
|
31638
|
+
error: error2 instanceof Error ? error2.message : "Unknown error"
|
|
31639
|
+
};
|
|
31640
|
+
}
|
|
31641
|
+
});
|
|
31642
|
+
const installResults = await Promise.all(installPromises);
|
|
31643
|
+
for (const result of installResults) {
|
|
31644
|
+
results.push(result);
|
|
31645
|
+
if (result.status === "installed")
|
|
31646
|
+
installed++;
|
|
31647
|
+
else if (result.status === "skipped")
|
|
31648
|
+
skipped++;
|
|
31649
|
+
else
|
|
31650
|
+
failed++;
|
|
31651
|
+
}
|
|
31320
31652
|
return {
|
|
31321
31653
|
content: [{
|
|
31322
31654
|
type: "text",
|
|
31323
|
-
text:
|
|
31324
|
-
|
|
31325
|
-
|
|
31326
|
-
|
|
31655
|
+
text: JSON.stringify({
|
|
31656
|
+
success: failed < params.ids.length,
|
|
31657
|
+
summary: {
|
|
31658
|
+
total: params.ids.length,
|
|
31659
|
+
installed,
|
|
31660
|
+
skipped,
|
|
31661
|
+
failed
|
|
31662
|
+
},
|
|
31663
|
+
results
|
|
31664
|
+
})
|
|
31327
31665
|
}]
|
|
31328
31666
|
};
|
|
31329
31667
|
}
|
|
31330
31668
|
|
|
31331
31669
|
// src/tools/library-update.ts
|
|
31332
|
-
import { existsSync as
|
|
31670
|
+
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
31333
31671
|
import { readFile as readFile5 } from "fs/promises";
|
|
31334
|
-
import { homedir as
|
|
31335
|
-
import { join as
|
|
31336
|
-
var KICAD_VERSIONS = ["9.0", "8.0"];
|
|
31672
|
+
import { homedir as homedir4, platform as platform4 } from "os";
|
|
31673
|
+
import { join as join7 } from "path";
|
|
31337
31674
|
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
31675
|
function getLibraryPaths(projectPath) {
|
|
31349
31676
|
if (projectPath) {
|
|
31350
|
-
const librariesDir =
|
|
31677
|
+
const librariesDir = join7(projectPath, "libraries");
|
|
31351
31678
|
return {
|
|
31352
|
-
symbolsDir:
|
|
31353
|
-
footprintDir:
|
|
31354
|
-
models3dDir:
|
|
31679
|
+
symbolsDir: join7(librariesDir, "symbols"),
|
|
31680
|
+
footprintDir: join7(librariesDir, "footprints", getFootprintDirName()),
|
|
31681
|
+
models3dDir: join7(librariesDir, "3dmodels", get3DModelsDirName())
|
|
31355
31682
|
};
|
|
31356
31683
|
}
|
|
31357
|
-
const home =
|
|
31358
|
-
const version2 =
|
|
31359
|
-
const plat =
|
|
31684
|
+
const home = homedir4();
|
|
31685
|
+
const version2 = detectKicadVersion();
|
|
31686
|
+
const plat = platform4();
|
|
31360
31687
|
let base;
|
|
31361
31688
|
if (plat === "linux") {
|
|
31362
|
-
base =
|
|
31689
|
+
base = join7(home, ".local", "share", "kicad", version2, "3rdparty", LIBRARY_NAMESPACE3);
|
|
31363
31690
|
} else {
|
|
31364
|
-
base =
|
|
31691
|
+
base = join7(home, "Documents", "KiCad", version2, "3rdparty", LIBRARY_NAMESPACE3);
|
|
31365
31692
|
}
|
|
31366
31693
|
return {
|
|
31367
|
-
symbolsDir:
|
|
31368
|
-
footprintDir:
|
|
31369
|
-
models3dDir:
|
|
31694
|
+
symbolsDir: join7(base, "symbols"),
|
|
31695
|
+
footprintDir: join7(base, "footprints", getFootprintDirName()),
|
|
31696
|
+
models3dDir: join7(base, "3dmodels", get3DModelsDirName())
|
|
31370
31697
|
};
|
|
31371
31698
|
}
|
|
31372
31699
|
function extractLcscIdsFromLibrary(content) {
|
|
@@ -31387,12 +31714,12 @@ function generateEmptyLibrary() {
|
|
|
31387
31714
|
`;
|
|
31388
31715
|
}
|
|
31389
31716
|
function findJlcLibraries(symbolsDir) {
|
|
31390
|
-
if (!
|
|
31717
|
+
if (!existsSync5(symbolsDir)) {
|
|
31391
31718
|
return [];
|
|
31392
31719
|
}
|
|
31393
31720
|
try {
|
|
31394
31721
|
const files = readdirSync(symbolsDir);
|
|
31395
|
-
return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) =>
|
|
31722
|
+
return files.filter((f) => f.startsWith("JLC-MCP-") && f.endsWith(".kicad_sym")).map((f) => join7(symbolsDir, f));
|
|
31396
31723
|
} catch {
|
|
31397
31724
|
return [];
|
|
31398
31725
|
}
|
|
@@ -31459,7 +31786,7 @@ async function handleUpdateLibrary(args) {
|
|
|
31459
31786
|
libraries_to_create: allCategories.map((cat) => ({
|
|
31460
31787
|
category: cat,
|
|
31461
31788
|
filename: getLibraryFilename(cat),
|
|
31462
|
-
path:
|
|
31789
|
+
path: join7(paths.symbolsDir, getLibraryFilename(cat))
|
|
31463
31790
|
}))
|
|
31464
31791
|
}, null, 2)
|
|
31465
31792
|
}]
|
|
@@ -31472,7 +31799,7 @@ async function handleUpdateLibrary(args) {
|
|
|
31472
31799
|
const createdLibraries = [];
|
|
31473
31800
|
for (const category of allCategories) {
|
|
31474
31801
|
const filename = getLibraryFilename(category);
|
|
31475
|
-
const filepath =
|
|
31802
|
+
const filepath = join7(paths.symbolsDir, filename);
|
|
31476
31803
|
await writeText(filepath, emptyContent);
|
|
31477
31804
|
createdLibraries.push(filepath);
|
|
31478
31805
|
}
|
|
@@ -31549,7 +31876,7 @@ async function handleUpdateLibrary(args) {
|
|
|
31549
31876
|
if (!params.dry_run && footprintResult.type === "generated") {
|
|
31550
31877
|
await ensureDir(paths.footprintDir);
|
|
31551
31878
|
const footprintName = footprintResult.name + "_" + lcscId;
|
|
31552
|
-
const footprintPath =
|
|
31879
|
+
const footprintPath = join7(paths.footprintDir, `${footprintName}.kicad_mod`);
|
|
31553
31880
|
await writeText(footprintPath, footprintResult.content);
|
|
31554
31881
|
}
|
|
31555
31882
|
results.push({
|
|
@@ -31576,7 +31903,7 @@ async function handleUpdateLibrary(args) {
|
|
|
31576
31903
|
await ensureDir(paths.symbolsDir);
|
|
31577
31904
|
for (const [category, entries] of categorySymbols) {
|
|
31578
31905
|
const filename = getLibraryFilename(category);
|
|
31579
|
-
const filepath =
|
|
31906
|
+
const filepath = join7(paths.symbolsDir, filename);
|
|
31580
31907
|
const header = `(kicad_symbol_lib
|
|
31581
31908
|
(version 20241209)
|
|
31582
31909
|
(generator "jlc-mcp")
|
|
@@ -31604,53 +31931,41 @@ async function handleUpdateLibrary(args) {
|
|
|
31604
31931
|
success: true,
|
|
31605
31932
|
dry_run: params.dry_run,
|
|
31606
31933
|
summary: {
|
|
31607
|
-
|
|
31934
|
+
total: allLcscIds.size,
|
|
31608
31935
|
updated: successful.length,
|
|
31609
|
-
failed: failed.length
|
|
31610
|
-
libraries_generated: categorySymbols.size
|
|
31936
|
+
failed: failed.length
|
|
31611
31937
|
},
|
|
31612
31938
|
by_category: Object.fromEntries(byCategory),
|
|
31613
31939
|
footprint_stats: footprintStats,
|
|
31614
|
-
|
|
31615
|
-
failed_components: failed.map((f) => ({
|
|
31940
|
+
failed_sample: failed.slice(0, 5).map((f) => ({
|
|
31616
31941
|
lcsc_id: f.lcscId,
|
|
31617
31942
|
error: f.error
|
|
31618
|
-
}))
|
|
31619
|
-
|
|
31943
|
+
})),
|
|
31944
|
+
has_more_failures: failed.length > 5
|
|
31945
|
+
})
|
|
31620
31946
|
}]
|
|
31621
31947
|
};
|
|
31622
31948
|
}
|
|
31623
31949
|
|
|
31624
31950
|
// src/tools/library-fix.ts
|
|
31625
|
-
import { existsSync as
|
|
31951
|
+
import { existsSync as existsSync7 } from "fs";
|
|
31626
31952
|
import { readFile as readFile7 } from "fs/promises";
|
|
31627
|
-
import { homedir as
|
|
31628
|
-
import { join as
|
|
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
|
-
}
|
|
31953
|
+
import { homedir as homedir6 } from "os";
|
|
31954
|
+
import { join as join9 } from "path";
|
|
31640
31955
|
function getLibraryPaths2(projectPath) {
|
|
31641
31956
|
if (projectPath) {
|
|
31642
|
-
const librariesDir =
|
|
31957
|
+
const librariesDir = join9(projectPath, "libraries");
|
|
31643
31958
|
return {
|
|
31644
|
-
symbolsDir:
|
|
31645
|
-
footprintDir:
|
|
31959
|
+
symbolsDir: join9(librariesDir, "symbols"),
|
|
31960
|
+
footprintDir: join9(librariesDir, "footprints", getFootprintDirName())
|
|
31646
31961
|
};
|
|
31647
31962
|
}
|
|
31648
|
-
const home =
|
|
31649
|
-
const version2 =
|
|
31650
|
-
const base =
|
|
31963
|
+
const home = homedir6();
|
|
31964
|
+
const version2 = detectKicadVersion();
|
|
31965
|
+
const base = join9(home, "Documents", "KiCad", version2);
|
|
31651
31966
|
return {
|
|
31652
|
-
symbolsDir:
|
|
31653
|
-
footprintDir:
|
|
31967
|
+
symbolsDir: join9(base, "symbols"),
|
|
31968
|
+
footprintDir: join9(base, "footprints", getFootprintDirName())
|
|
31654
31969
|
};
|
|
31655
31970
|
}
|
|
31656
31971
|
var PinElectricalType = exports_external.enum([
|
|
@@ -31818,8 +32133,8 @@ async function handleFixLibrary(args) {
|
|
|
31818
32133
|
}
|
|
31819
32134
|
const category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
|
|
31820
32135
|
const symbolLibraryFilename = getLibraryFilename(category);
|
|
31821
|
-
const symbolFile =
|
|
31822
|
-
if (!
|
|
32136
|
+
const symbolFile = join9(paths.symbolsDir, symbolLibraryFilename);
|
|
32137
|
+
if (!existsSync7(symbolFile) && !params.force) {
|
|
31823
32138
|
return {
|
|
31824
32139
|
content: [{
|
|
31825
32140
|
type: "text",
|
|
@@ -31833,7 +32148,7 @@ async function handleFixLibrary(args) {
|
|
|
31833
32148
|
isError: true
|
|
31834
32149
|
};
|
|
31835
32150
|
}
|
|
31836
|
-
if (
|
|
32151
|
+
if (existsSync7(symbolFile) && !params.force) {
|
|
31837
32152
|
const existingContent = await readFile7(symbolFile, "utf-8");
|
|
31838
32153
|
if (!symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
|
|
31839
32154
|
return {
|
|
@@ -31920,14 +32235,14 @@ async function handleFixLibrary(args) {
|
|
|
31920
32235
|
const footprintName = footprintResult.name + "_" + params.lcsc_id;
|
|
31921
32236
|
footprintRef = getFootprintReference2(footprintName);
|
|
31922
32237
|
await ensureDir(paths.footprintDir);
|
|
31923
|
-
const footprintPath =
|
|
32238
|
+
const footprintPath = join9(paths.footprintDir, `${footprintName}.kicad_mod`);
|
|
31924
32239
|
await writeText(footprintPath, footprintResult.content);
|
|
31925
32240
|
}
|
|
31926
32241
|
component.info.package = footprintRef;
|
|
31927
32242
|
await ensureDir(paths.symbolsDir);
|
|
31928
32243
|
let symbolContent;
|
|
31929
32244
|
let symbolAction;
|
|
31930
|
-
if (
|
|
32245
|
+
if (existsSync7(symbolFile)) {
|
|
31931
32246
|
const existingContent = await readFile7(symbolFile, "utf-8");
|
|
31932
32247
|
symbolContent = symbolConverter.replaceInLibrary(existingContent, component);
|
|
31933
32248
|
symbolAction = "replaced";
|
|
@@ -31956,494 +32271,31 @@ async function handleFixLibrary(args) {
|
|
|
31956
32271
|
};
|
|
31957
32272
|
}
|
|
31958
32273
|
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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
32274
|
// src/tools/index.ts
|
|
32417
32275
|
var tools = [
|
|
32418
|
-
|
|
32419
|
-
|
|
32420
|
-
|
|
32421
|
-
|
|
32422
|
-
fetchLibraryTool,
|
|
32276
|
+
componentSearchTool,
|
|
32277
|
+
libraryInstallTool,
|
|
32278
|
+
libraryBatchInstallTool,
|
|
32279
|
+
libraryGetComponentTool,
|
|
32423
32280
|
updateLibraryTool,
|
|
32424
|
-
fixLibraryTool
|
|
32425
|
-
get3DModelTool,
|
|
32426
|
-
easyedaSearchTool,
|
|
32427
|
-
easyedaGet3DModelTool
|
|
32281
|
+
fixLibraryTool
|
|
32428
32282
|
];
|
|
32429
32283
|
var toolHandlers = {
|
|
32430
|
-
|
|
32431
|
-
|
|
32432
|
-
|
|
32433
|
-
|
|
32434
|
-
|
|
32435
|
-
|
|
32436
|
-
library_fix: handleFixLibrary,
|
|
32437
|
-
library_get_3d_model: handleGet3DModel,
|
|
32438
|
-
easyeda_search: handleEasyedaSearch,
|
|
32439
|
-
easyeda_get_3d_model: handleEasyedaGet3DModel
|
|
32284
|
+
[componentSearchTool.name]: handleComponentSearch,
|
|
32285
|
+
[libraryInstallTool.name]: handleLibraryInstall,
|
|
32286
|
+
[libraryBatchInstallTool.name]: handleLibraryBatchInstall,
|
|
32287
|
+
[libraryGetComponentTool.name]: handleLibraryGetComponent,
|
|
32288
|
+
[updateLibraryTool.name]: handleUpdateLibrary,
|
|
32289
|
+
[fixLibraryTool.name]: handleFixLibrary
|
|
32440
32290
|
};
|
|
32441
32291
|
|
|
32442
32292
|
// src/index.ts
|
|
32293
|
+
var require2 = createRequire(import.meta.url);
|
|
32294
|
+
var { version: version2 } = require2("../package.json");
|
|
32443
32295
|
var logger2 = createLogger("jlc-mcp");
|
|
32444
32296
|
var server = new Server({
|
|
32445
32297
|
name: "jlc-mcp",
|
|
32446
|
-
version:
|
|
32298
|
+
version: version2
|
|
32447
32299
|
}, {
|
|
32448
32300
|
capabilities: {
|
|
32449
32301
|
tools: {}
|
|
@@ -32484,9 +32336,9 @@ async function main() {
|
|
|
32484
32336
|
});
|
|
32485
32337
|
process.exit(1);
|
|
32486
32338
|
}
|
|
32487
|
-
const { symLibTable, fpLibTable, libraryStubs, version:
|
|
32339
|
+
const { symLibTable, fpLibTable, libraryStubs, version: version3 } = registration;
|
|
32488
32340
|
if (symLibTable.created || symLibTable.modified || fpLibTable.created || fpLibTable.modified) {
|
|
32489
|
-
logger2.info(`JLC libraries registered in KiCad ${
|
|
32341
|
+
logger2.info(`JLC libraries registered in KiCad ${version3}`, {
|
|
32490
32342
|
symLibTable: symLibTable.created ? `created with ${symLibTable.entriesAdded} entries` : symLibTable.modified ? `added ${symLibTable.entriesAdded} entries` : "already configured",
|
|
32491
32343
|
fpLibTable: fpLibTable.created ? "created" : fpLibTable.modified ? "updated" : "already configured",
|
|
32492
32344
|
stubsCreated: libraryStubs.symbolsCreated.length + libraryStubs.directoriesCreated.length
|