@jlcpcb/core 0.1.1 → 0.2.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/assets/search.html +528 -0
  3. package/dist/browser/index.d.ts +8 -0
  4. package/dist/browser/index.d.ts.map +1 -0
  5. package/dist/browser/kicad-renderer.d.ts +13 -0
  6. package/dist/browser/kicad-renderer.d.ts.map +1 -0
  7. package/dist/browser/sexpr-parser.d.ts +84 -0
  8. package/dist/browser/sexpr-parser.d.ts.map +1 -0
  9. package/dist/converter/footprint.d.ts +12 -2
  10. package/dist/converter/footprint.d.ts.map +1 -1
  11. package/dist/converter/global-lib-table.d.ts +2 -2
  12. package/dist/converter/global-lib-table.d.ts.map +1 -1
  13. package/dist/http/index.d.ts +5 -0
  14. package/dist/http/index.d.ts.map +1 -0
  15. package/dist/http/routes.d.ts +12 -0
  16. package/dist/http/routes.d.ts.map +1 -0
  17. package/dist/http/server.d.ts +18 -0
  18. package/dist/http/server.d.ts.map +1 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +474 -39
  22. package/dist/parsers/easyeda-shapes.d.ts.map +1 -1
  23. package/dist/services/library-service.d.ts +1 -0
  24. package/dist/services/library-service.d.ts.map +1 -1
  25. package/package.json +5 -3
  26. package/scripts/build-search-page.ts +68 -0
  27. package/src/assets/search-built.html +528 -0
  28. package/src/assets/search.html +458 -0
  29. package/src/browser/index.ts +389 -0
  30. package/src/browser/kicad-renderer.ts +813 -0
  31. package/src/browser/sexpr-parser.ts +333 -0
  32. package/src/converter/footprint.ts +184 -27
  33. package/src/converter/global-lib-table.ts +71 -0
  34. package/src/http/index.ts +5 -0
  35. package/src/http/routes.ts +266 -0
  36. package/src/http/server.ts +83 -0
  37. package/src/index.ts +3 -0
  38. package/src/parsers/easyeda-shapes.ts +2 -1
  39. package/src/services/library-service.ts +73 -22
package/dist/index.js CHANGED
@@ -627,7 +627,8 @@ var init_lib_table = () => {};
627
627
  // src/converter/global-lib-table.ts
628
628
  var exports_global_lib_table = {};
629
629
  __export(exports_global_lib_table, {
630
- ensureGlobalLibraryTables: () => ensureGlobalLibraryTables
630
+ ensureGlobalLibraryTables: () => ensureGlobalLibraryTables,
631
+ ensureGlobalEasyEDALibrary: () => ensureGlobalEasyEDALibrary
631
632
  });
632
633
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
633
634
  import { existsSync as existsSync2 } from "fs";
@@ -808,6 +809,51 @@ async function ensureLibraryStubs(version) {
808
809
  }
809
810
  return { symbolsCreated, directoriesCreated };
810
811
  }
812
+ function getEasyEDASymbolLibUri() {
813
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${EASYEDA_SYMBOL_LIBRARY_NAME}`;
814
+ }
815
+ function getEasyEDAFootprintLibUri() {
816
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/footprints/${EASYEDA_FOOTPRINT_LIBRARY_NAME}`;
817
+ }
818
+ async function ensureGlobalEasyEDALibrary() {
819
+ const version = detectKicadVersion();
820
+ const configDir = getKicadConfigDir(version);
821
+ await mkdir2(configDir, { recursive: true });
822
+ const symTablePath = join3(configDir, "sym-lib-table");
823
+ if (existsSync2(symTablePath)) {
824
+ let content = await readFile3(symTablePath, "utf-8");
825
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
826
+ const uri = getEasyEDASymbolLibUri();
827
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "sym", EASYEDA_LIBRARY_DESCRIPTION);
828
+ await writeFile3(symTablePath, content, "utf-8");
829
+ }
830
+ } else {
831
+ const uri = getEasyEDASymbolLibUri();
832
+ const content = `(sym_lib_table
833
+ (version 7)
834
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
835
+ )
836
+ `;
837
+ await writeFile3(symTablePath, content, "utf-8");
838
+ }
839
+ const fpTablePath = join3(configDir, "fp-lib-table");
840
+ if (existsSync2(fpTablePath)) {
841
+ let content = await readFile3(fpTablePath, "utf-8");
842
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
843
+ const uri = getEasyEDAFootprintLibUri();
844
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "fp", EASYEDA_LIBRARY_DESCRIPTION);
845
+ await writeFile3(fpTablePath, content, "utf-8");
846
+ }
847
+ } else {
848
+ const uri = getEasyEDAFootprintLibUri();
849
+ const content = `(fp_lib_table
850
+ (version 7)
851
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
852
+ )
853
+ `;
854
+ await writeFile3(fpTablePath, content, "utf-8");
855
+ }
856
+ }
811
857
  async function ensureGlobalLibraryTables() {
812
858
  const errors2 = [];
813
859
  const version = detectKicadVersion();
@@ -855,7 +901,7 @@ async function ensureGlobalLibraryTables() {
855
901
  errors: errors2
856
902
  };
857
903
  }
858
- var KICAD_VERSIONS2, LIBRARY_PREFIX2 = "JLC-MCP", LIBRARY_NAMESPACE = "jlc_mcp", KICAD_3RD_PARTY_VAR = "${KICAD9_3RD_PARTY}", LIBRARY_DESCRIPTION = "Autogenerated by JLC-MCP";
904
+ var KICAD_VERSIONS2, LIBRARY_PREFIX2 = "JLC-MCP", LIBRARY_NAMESPACE = "jlc_mcp", KICAD_3RD_PARTY_VAR = "${KICAD9_3RD_PARTY}", LIBRARY_DESCRIPTION = "Autogenerated by JLC-MCP", EASYEDA_LIBRARY_NAME = "EasyEDA", EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym", EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty", EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
859
905
  var init_global_lib_table = __esm(() => {
860
906
  init_category_router();
861
907
  init_lib_table();
@@ -5725,7 +5771,7 @@ function parseFootprintShapes(shapes) {
5725
5771
  }
5726
5772
  }
5727
5773
  }
5728
- const type = pads.some((p) => p.holeRadius > 0) ? "tht" : "smd";
5774
+ const type = pads.some((p) => p.holeRadius > 0 || p.shape === "POLYGON" && p.isPlated) ? "tht" : "smd";
5729
5775
  return {
5730
5776
  name: "Unknown",
5731
5777
  type,
@@ -8022,8 +8068,8 @@ var KI_PAD_LAYER_SMD = {
8022
8068
  11: '"*.Cu" "*.Paste" "*.Mask"'
8023
8069
  };
8024
8070
  var KI_PAD_LAYER_THT = {
8025
- 1: '"F.Cu" "F.Mask"',
8026
- 2: '"B.Cu" "B.Mask"',
8071
+ 1: '"*.Cu" "*.Mask"',
8072
+ 2: '"*.Cu" "*.Mask"',
8027
8073
  11: '"*.Cu" "*.Mask"'
8028
8074
  };
8029
8075
  var KI_PAD_SHAPE = {
@@ -8233,15 +8279,20 @@ class FootprintConverter {
8233
8279
  `;
8234
8280
  return output;
8235
8281
  }
8236
- generatePolygonPad(pad, origin, layers) {
8282
+ generatePolygonPad(pad, origin, _layers) {
8237
8283
  const x = convertX(pad.centerX, origin.x);
8238
8284
  const y = convertY(pad.centerY, origin.y);
8239
8285
  const points = parsePoints(pad.points);
8240
8286
  if (points.length < 3) {
8241
8287
  return this.generatePad({ ...pad, shape: "RECT", points: "" }, origin);
8242
8288
  }
8243
- const isSmd = pad.holeRadius === 0;
8289
+ let holeRadius = pad.holeRadius;
8290
+ if (holeRadius === 0 && pad.isPlated && pad.shape === "POLYGON") {
8291
+ holeRadius = this.calculateDrillRadiusFromPolygon(points, pad.centerX, pad.centerY);
8292
+ }
8293
+ const isSmd = holeRadius === 0;
8244
8294
  const padType = isSmd ? "smd" : "thru_hole";
8295
+ const layers = getPadLayers(pad.layerId, isSmd);
8245
8296
  const polyPoints = points.map((p) => ({
8246
8297
  x: roundTo(toMM(p.x - pad.centerX), 2),
8247
8298
  y: roundTo(toMM(p.y - pad.centerY), 2)
@@ -8253,7 +8304,7 @@ class FootprintConverter {
8253
8304
  output += ` (size 0.01 0.01)
8254
8305
  `;
8255
8306
  if (!isSmd) {
8256
- const drillDiameter = roundTo(toMM(pad.holeRadius * 2), 4);
8307
+ const drillDiameter = roundTo(toMM(holeRadius * 2), 4);
8257
8308
  if (pad.holeLength && pad.holeLength > 0) {
8258
8309
  const holeH = roundTo(toMM(pad.holeLength), 4);
8259
8310
  output += ` (drill oval ${drillDiameter} ${holeH})
@@ -8287,6 +8338,25 @@ class FootprintConverter {
8287
8338
  `;
8288
8339
  return output;
8289
8340
  }
8341
+ calculateDrillRadiusFromPolygon(points, centerX, centerY) {
8342
+ if (points.length < 3)
8343
+ return 0;
8344
+ let minX = Infinity, maxX = -Infinity;
8345
+ let minY = Infinity, maxY = -Infinity;
8346
+ for (const pt of points) {
8347
+ const relX = pt.x - centerX;
8348
+ const relY = pt.y - centerY;
8349
+ minX = Math.min(minX, relX);
8350
+ maxX = Math.max(maxX, relX);
8351
+ minY = Math.min(minY, relY);
8352
+ maxY = Math.max(maxY, relY);
8353
+ }
8354
+ const width = maxX - minX;
8355
+ const height = maxY - minY;
8356
+ const minDim = Math.min(width, height);
8357
+ const drillDiameter = minDim * 0.6;
8358
+ return drillDiameter / 2;
8359
+ }
8290
8360
  generateHole(hole, origin) {
8291
8361
  const x = convertX(hole.centerX, origin.x);
8292
8362
  const y = convertY(hole.centerY, origin.y);
@@ -8413,9 +8483,9 @@ class FootprintConverter {
8413
8483
  const fontSize = roundTo(toMM(text.fontSize), 2);
8414
8484
  const rotation = text.rotation || 0;
8415
8485
  let justify = "";
8416
- if (x < -0.5) {
8486
+ if (text.type === "L") {
8417
8487
  justify = "left";
8418
- } else if (x > 0.5) {
8488
+ } else if (text.type === "R") {
8419
8489
  justify = "right";
8420
8490
  }
8421
8491
  return ` (fp_text user "${this.escapeString(text.text)}"
@@ -8432,6 +8502,9 @@ ${justify ? ` (justify ${justify})
8432
8502
  `;
8433
8503
  }
8434
8504
  generateSolidRegion(region, origin) {
8505
+ if (region.layerId === 99 || region.layerId === 100 || region.layerId === 101) {
8506
+ return "";
8507
+ }
8435
8508
  const layer = getLayer(region.layerId);
8436
8509
  const points = this.parseSvgPathToPoints(region.path, origin);
8437
8510
  if (points.length < 3)
@@ -8464,15 +8537,111 @@ ${justify ? ` (justify ${justify})
8464
8537
  }
8465
8538
  parseSvgPathToPoints(path, origin) {
8466
8539
  const points = [];
8467
- const commandRegex = /([ML])\s*([\d.-]+)[,\s]+([\d.-]+)/gi;
8540
+ let currentX = 0, currentY = 0;
8541
+ let startX = 0, startY = 0;
8542
+ const commandRegex = /([MLHVCSQTAZ])\s*([^MLHVCSQTAZ]*)/gi;
8468
8543
  let match;
8469
8544
  while ((match = commandRegex.exec(path)) !== null) {
8470
- const x = parseFloat(match[2]);
8471
- const y = parseFloat(match[3]);
8472
- points.push({
8473
- x: convertX(x, origin.x),
8474
- y: convertY(y, origin.y)
8475
- });
8545
+ const cmd = match[1].toUpperCase();
8546
+ const args = match[2].trim().split(/[\s,]+/).map(parseFloat).filter((n) => !isNaN(n));
8547
+ switch (cmd) {
8548
+ case "M":
8549
+ if (args.length >= 2) {
8550
+ currentX = args[0];
8551
+ currentY = args[1];
8552
+ if (points.length === 0) {
8553
+ startX = currentX;
8554
+ startY = currentY;
8555
+ }
8556
+ points.push({
8557
+ x: convertX(currentX, origin.x),
8558
+ y: convertY(currentY, origin.y)
8559
+ });
8560
+ }
8561
+ break;
8562
+ case "L":
8563
+ if (args.length >= 2) {
8564
+ currentX = args[0];
8565
+ currentY = args[1];
8566
+ points.push({
8567
+ x: convertX(currentX, origin.x),
8568
+ y: convertY(currentY, origin.y)
8569
+ });
8570
+ }
8571
+ break;
8572
+ case "H":
8573
+ if (args.length >= 1) {
8574
+ currentX = args[0];
8575
+ points.push({
8576
+ x: convertX(currentX, origin.x),
8577
+ y: convertY(currentY, origin.y)
8578
+ });
8579
+ }
8580
+ break;
8581
+ case "V":
8582
+ if (args.length >= 1) {
8583
+ currentY = args[0];
8584
+ points.push({
8585
+ x: convertX(currentX, origin.x),
8586
+ y: convertY(currentY, origin.y)
8587
+ });
8588
+ }
8589
+ break;
8590
+ case "C":
8591
+ if (args.length >= 6) {
8592
+ currentX = args[4];
8593
+ currentY = args[5];
8594
+ points.push({
8595
+ x: convertX(currentX, origin.x),
8596
+ y: convertY(currentY, origin.y)
8597
+ });
8598
+ }
8599
+ break;
8600
+ case "S":
8601
+ if (args.length >= 4) {
8602
+ currentX = args[2];
8603
+ currentY = args[3];
8604
+ points.push({
8605
+ x: convertX(currentX, origin.x),
8606
+ y: convertY(currentY, origin.y)
8607
+ });
8608
+ }
8609
+ break;
8610
+ case "Q":
8611
+ if (args.length >= 4) {
8612
+ currentX = args[2];
8613
+ currentY = args[3];
8614
+ points.push({
8615
+ x: convertX(currentX, origin.x),
8616
+ y: convertY(currentY, origin.y)
8617
+ });
8618
+ }
8619
+ break;
8620
+ case "T":
8621
+ if (args.length >= 2) {
8622
+ currentX = args[0];
8623
+ currentY = args[1];
8624
+ points.push({
8625
+ x: convertX(currentX, origin.x),
8626
+ y: convertY(currentY, origin.y)
8627
+ });
8628
+ }
8629
+ break;
8630
+ case "A":
8631
+ if (args.length >= 7) {
8632
+ currentX = args[5];
8633
+ currentY = args[6];
8634
+ points.push({
8635
+ x: convertX(currentX, origin.x),
8636
+ y: convertY(currentY, origin.y)
8637
+ });
8638
+ }
8639
+ break;
8640
+ case "Z":
8641
+ currentX = startX;
8642
+ currentY = startY;
8643
+ break;
8644
+ }
8476
8645
  }
8477
8646
  return points;
8478
8647
  }
@@ -8642,10 +8811,14 @@ var FOOTPRINT_LIBRARY_NAME = getFootprintDirName();
8642
8811
  var MODELS_3D_NAME = get3DModelsDirName();
8643
8812
  var LIBRARY_NAMESPACE2 = "jlc_mcp";
8644
8813
  var KICAD_VERSIONS3 = ["9.0", "8.0"];
8645
- var EASYEDA_LIBRARY_NAME = "EasyEDA";
8646
- var EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym";
8647
- var EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty";
8648
- var EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
8814
+ var EASYEDA_LIBRARY_NAME2 = "EasyEDA";
8815
+ var EASYEDA_SYMBOL_LIBRARY_NAME2 = "EasyEDA.kicad_sym";
8816
+ var EASYEDA_FOOTPRINT_LIBRARY_NAME2 = "EasyEDA.pretty";
8817
+ var EASYEDA_LIBRARY_DESCRIPTION2 = "EasyEDA Community Component Library";
8818
+ var EASYEDA_LOCAL_LIBRARY_NAME = "EasyEDA-local";
8819
+ var EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME = "EasyEDA-local.kicad_sym";
8820
+ var EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME = "EasyEDA-local.pretty";
8821
+ var EASYEDA_LOCAL_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library (Project-local)";
8649
8822
  function detectKicadVersion2() {
8650
8823
  const home = homedir2();
8651
8824
  const baseDir = join4(home, "Documents", "KiCad");
@@ -8786,10 +8959,7 @@ function createLibraryService() {
8786
8959
  return {
8787
8960
  async install(id, options = {}) {
8788
8961
  const isCommunityComponent = !isLcscId2(id);
8789
- if (isCommunityComponent && !options.projectPath) {
8790
- throw new Error("EasyEDA community components require projectPath for local storage");
8791
- }
8792
- const isGlobal = !isCommunityComponent && !options.projectPath;
8962
+ const isGlobal = !options.projectPath;
8793
8963
  const paths = isGlobal ? getGlobalLibraryPaths() : getProjectLibraryPaths(options.projectPath);
8794
8964
  let component = null;
8795
8965
  if (isLcscId2(id)) {
@@ -8832,13 +9002,27 @@ function createLibraryService() {
8832
9002
  let category;
8833
9003
  let modelPath;
8834
9004
  if (isCommunityComponent) {
8835
- const librariesDir = join4(options.projectPath, "libraries");
8836
- const symbolsDir = join4(librariesDir, "symbols");
8837
- footprintDir = join4(librariesDir, "footprints", EASYEDA_FOOTPRINT_LIBRARY_NAME);
8838
- models3dDir = join4(librariesDir, "3dmodels", "EasyEDA.3dshapes");
9005
+ const libName = isGlobal ? EASYEDA_LIBRARY_NAME2 : EASYEDA_LOCAL_LIBRARY_NAME;
9006
+ const symLibFile = isGlobal ? EASYEDA_SYMBOL_LIBRARY_NAME2 : EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME;
9007
+ const fpLibDir = isGlobal ? EASYEDA_FOOTPRINT_LIBRARY_NAME2 : EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME;
9008
+ const libDesc = isGlobal ? EASYEDA_LIBRARY_DESCRIPTION2 : EASYEDA_LOCAL_LIBRARY_DESCRIPTION;
9009
+ const models3dDirName = isGlobal ? "EasyEDA.3dshapes" : "EasyEDA-local.3dshapes";
9010
+ let symbolsDir;
9011
+ let easyedaModelsDir;
9012
+ if (isGlobal) {
9013
+ symbolsDir = paths.symbolsDir;
9014
+ footprintDir = join4(paths.footprintsDir, fpLibDir);
9015
+ easyedaModelsDir = join4(paths.models3dDir, models3dDirName);
9016
+ } else {
9017
+ const librariesDir = join4(options.projectPath, "libraries");
9018
+ symbolsDir = join4(librariesDir, "symbols");
9019
+ footprintDir = join4(librariesDir, "footprints", fpLibDir);
9020
+ easyedaModelsDir = join4(librariesDir, "3dmodels", models3dDirName);
9021
+ }
9022
+ models3dDir = easyedaModelsDir;
8839
9023
  await ensureDir(symbolsDir);
8840
9024
  await ensureDir(footprintDir);
8841
- symbolFile = join4(symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME);
9025
+ symbolFile = join4(symbolsDir, symLibFile);
8842
9026
  symbolName = component.info.name.replace(/[^a-zA-Z0-9_-]/g, "_");
8843
9027
  const include3d = options.include3d ?? true;
8844
9028
  let modelRelativePath;
@@ -8849,21 +9033,30 @@ function createLibraryService() {
8849
9033
  const modelFilename = `${symbolName}.step`;
8850
9034
  modelPath = join4(models3dDir, modelFilename);
8851
9035
  await writeBinary(modelPath, model);
8852
- modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/EasyEDA.3dshapes/${modelFilename}`;
9036
+ if (isGlobal) {
9037
+ modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${models3dDirName}/${modelFilename}`;
9038
+ } else {
9039
+ modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/${models3dDirName}/${modelFilename}`;
9040
+ }
8853
9041
  }
8854
9042
  }
8855
9043
  const footprint = footprintConverter.convert(component, {
8856
- libraryName: EASYEDA_LIBRARY_NAME,
9044
+ libraryName: libName,
8857
9045
  include3DModel: !!modelRelativePath,
8858
9046
  modelPath: modelRelativePath
8859
9047
  });
8860
9048
  footprintPath = join4(footprintDir, `${symbolName}.kicad_mod`);
8861
- footprintRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
9049
+ footprintRef = `${libName}:${symbolName}`;
8862
9050
  await writeText(footprintPath, footprint);
8863
9051
  component.info.package = footprintRef;
8864
- await ensureSymLibTable(options.projectPath, symbolFile, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
8865
- await ensureFpLibTable(options.projectPath, footprintDir, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
8866
- symbolRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
9052
+ if (isGlobal) {
9053
+ const { ensureGlobalEasyEDALibrary: ensureGlobalEasyEDALibrary2 } = await Promise.resolve().then(() => (init_global_lib_table(), exports_global_lib_table));
9054
+ await ensureGlobalEasyEDALibrary2();
9055
+ } else {
9056
+ await ensureSymLibTable(options.projectPath, symbolFile, libName, libDesc);
9057
+ await ensureFpLibTable(options.projectPath, footprintDir, libName, libDesc);
9058
+ }
9059
+ symbolRef = `${libName}:${symbolName}`;
8867
9060
  } else {
8868
9061
  category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
8869
9062
  const symbolLibraryFilename = getLibraryFilename(category);
@@ -8910,7 +9103,7 @@ function createLibraryService() {
8910
9103
  if (symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
8911
9104
  if (options.force) {
8912
9105
  symbolContent = symbolConverter.replaceInLibrary(existingContent, component, {
8913
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
9106
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
8914
9107
  symbolName: isCommunityComponent ? symbolName : undefined
8915
9108
  });
8916
9109
  symbolAction = "replaced";
@@ -8920,14 +9113,14 @@ function createLibraryService() {
8920
9113
  }
8921
9114
  } else {
8922
9115
  symbolContent = symbolConverter.appendToLibrary(existingContent, component, {
8923
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
9116
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
8924
9117
  symbolName: isCommunityComponent ? symbolName : undefined
8925
9118
  });
8926
9119
  symbolAction = "appended";
8927
9120
  }
8928
9121
  } else {
8929
9122
  symbolContent = symbolConverter.convert(component, {
8930
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
9123
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
8931
9124
  symbolName: isCommunityComponent ? symbolName : undefined
8932
9125
  });
8933
9126
  symbolAction = "created";
@@ -9052,6 +9245,21 @@ function createLibraryService() {
9052
9245
  fpLibTable: fpLibTablePath
9053
9246
  }
9054
9247
  };
9248
+ },
9249
+ async isEasyEDAInstalled(componentName) {
9250
+ const paths = getGlobalLibraryPaths();
9251
+ const easyedaLibPath = join4(paths.symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME2);
9252
+ if (!existsSync3(easyedaLibPath)) {
9253
+ return false;
9254
+ }
9255
+ try {
9256
+ const content = await readFile4(easyedaLibPath, "utf-8");
9257
+ const sanitizedName = componentName.replace(/[^a-zA-Z0-9_-]/g, "_");
9258
+ const pattern = new RegExp(`\\(symbol\\s+"${sanitizedName}"`, "m");
9259
+ return pattern.test(content);
9260
+ } catch {
9261
+ return false;
9262
+ }
9055
9263
  }
9056
9264
  };
9057
9265
  }
@@ -9067,6 +9275,231 @@ function createFixService() {
9067
9275
  init_lib_table();
9068
9276
  init_category_router();
9069
9277
  init_global_lib_table();
9278
+ // src/http/server.ts
9279
+ import { createServer } from "http";
9280
+
9281
+ // src/http/routes.ts
9282
+ import { readFileSync } from "fs";
9283
+ import { join as join5, dirname as dirname3 } from "path";
9284
+ import { fileURLToPath } from "url";
9285
+ var logger7 = createLogger("http-routes");
9286
+ var __filename2 = fileURLToPath(import.meta.url);
9287
+ var __dirname2 = dirname3(__filename2);
9288
+ var htmlCache = null;
9289
+ function getHtmlPage() {
9290
+ if (htmlCache)
9291
+ return htmlCache;
9292
+ try {
9293
+ const possiblePaths = [
9294
+ join5(__dirname2, "assets/search.html"),
9295
+ join5(__dirname2, "../dist/assets/search.html"),
9296
+ join5(__dirname2, "../assets/search.html"),
9297
+ join5(__dirname2, "../assets/search-built.html"),
9298
+ join5(process.cwd(), "dist/assets/search.html"),
9299
+ join5(process.cwd(), "packages/core/dist/assets/search.html"),
9300
+ join5(__dirname2, "../../dist/assets/search.html"),
9301
+ join5(__dirname2, "../../../core/dist/assets/search.html"),
9302
+ join5(__dirname2, "../../../../packages/core/dist/assets/search.html"),
9303
+ join5(__dirname2, "../../node_modules/@jlcpcb/core/dist/assets/search.html")
9304
+ ];
9305
+ for (const path of possiblePaths) {
9306
+ try {
9307
+ htmlCache = readFileSync(path, "utf-8");
9308
+ logger7.debug(`Loaded HTML from: ${path}`);
9309
+ return htmlCache;
9310
+ } catch {}
9311
+ }
9312
+ throw new Error("HTML file not found");
9313
+ } catch (error) {
9314
+ logger7.error("Failed to load HTML page:", error);
9315
+ return `<!DOCTYPE html>
9316
+ <html>
9317
+ <head><title>Error</title></head>
9318
+ <body>
9319
+ <h1>Error: Search page not found</h1>
9320
+ <p>The search page has not been built. Run: bun run build</p>
9321
+ </body>
9322
+ </html>`;
9323
+ }
9324
+ }
9325
+ async function handleRequest(req, res) {
9326
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
9327
+ const pathname = url.pathname;
9328
+ logger7.debug(`${req.method} ${pathname}`);
9329
+ if (pathname === "/" || pathname === "/index.html") {
9330
+ const query = url.searchParams.get("q") || undefined;
9331
+ serveHtml(res, query);
9332
+ } else if (pathname === "/api/search") {
9333
+ await handleSearch(url, res);
9334
+ } else if (pathname.startsWith("/api/component/")) {
9335
+ const uuid = pathname.replace("/api/component/", "");
9336
+ await handleComponent(uuid, res);
9337
+ } else if (pathname === "/health") {
9338
+ res.writeHead(200, { "Content-Type": "application/json" });
9339
+ res.end(JSON.stringify({ status: "ok" }));
9340
+ } else {
9341
+ res.writeHead(404, { "Content-Type": "application/json" });
9342
+ res.end(JSON.stringify({ error: "Not found" }));
9343
+ }
9344
+ }
9345
+ function serveHtml(res, initialQuery) {
9346
+ let html = getHtmlPage();
9347
+ if (initialQuery) {
9348
+ const script = `<script>window.__INITIAL_QUERY__ = ${JSON.stringify(initialQuery)};</script>`;
9349
+ html = html.replace("</head>", `${script}</head>`);
9350
+ }
9351
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
9352
+ res.end(html);
9353
+ }
9354
+ async function handleSearch(url, res) {
9355
+ const query = url.searchParams.get("q") || "";
9356
+ const source = url.searchParams.get("source") || "user";
9357
+ const page = parseInt(url.searchParams.get("page") || "1", 10);
9358
+ const limit = parseInt(url.searchParams.get("limit") || "20", 10);
9359
+ if (!query) {
9360
+ res.writeHead(400, { "Content-Type": "application/json" });
9361
+ res.end(JSON.stringify({ error: "Missing query parameter" }));
9362
+ return;
9363
+ }
9364
+ try {
9365
+ const allResults = await easyedaCommunityClient.search({
9366
+ query,
9367
+ source,
9368
+ limit: Math.min(limit * page + limit, 100)
9369
+ });
9370
+ const startIndex = (page - 1) * limit;
9371
+ const endIndex = startIndex + limit;
9372
+ const results = allResults.slice(startIndex, endIndex);
9373
+ const totalPages = Math.ceil(allResults.length / limit);
9374
+ res.writeHead(200, { "Content-Type": "application/json" });
9375
+ res.end(JSON.stringify({
9376
+ results,
9377
+ pagination: {
9378
+ page,
9379
+ limit,
9380
+ total: allResults.length,
9381
+ totalPages,
9382
+ hasNext: page < totalPages,
9383
+ hasPrev: page > 1
9384
+ }
9385
+ }));
9386
+ } catch (error) {
9387
+ logger7.error("Search error:", error);
9388
+ res.writeHead(500, { "Content-Type": "application/json" });
9389
+ res.end(JSON.stringify({ error: "Search failed" }));
9390
+ }
9391
+ }
9392
+ function communityToComponentData(community) {
9393
+ const cPara = community.symbol.head?.c_para ?? {};
9394
+ const fpCPara = community.footprint.head?.c_para ?? {};
9395
+ return {
9396
+ info: {
9397
+ name: community.title || cPara.name || "Unknown",
9398
+ prefix: cPara.pre || cPara.Prefix || "U",
9399
+ package: fpCPara.package || community.footprint.name,
9400
+ manufacturer: cPara.Manufacturer || cPara.BOM_Manufacturer,
9401
+ description: community.description || cPara.BOM_Manufacturer_Part,
9402
+ category: cPara.package
9403
+ },
9404
+ symbol: community.symbol,
9405
+ footprint: community.footprint,
9406
+ model3d: community.model3d,
9407
+ rawData: community.rawData
9408
+ };
9409
+ }
9410
+ async function handleComponent(uuid, res) {
9411
+ if (!uuid) {
9412
+ res.writeHead(400, { "Content-Type": "application/json" });
9413
+ res.end(JSON.stringify({ error: "Missing UUID" }));
9414
+ return;
9415
+ }
9416
+ try {
9417
+ const component2 = await easyedaCommunityClient.getComponent(uuid);
9418
+ if (!component2) {
9419
+ res.writeHead(404, { "Content-Type": "application/json" });
9420
+ res.end(JSON.stringify({ error: "Component not found" }));
9421
+ return;
9422
+ }
9423
+ const componentData = communityToComponentData(component2);
9424
+ let symbolSexpr = "";
9425
+ let footprintSexpr = "";
9426
+ try {
9427
+ symbolSexpr = symbolConverter.convertToSymbolEntry(componentData);
9428
+ } catch (e) {
9429
+ logger7.warn("Symbol conversion failed:", e);
9430
+ }
9431
+ try {
9432
+ footprintSexpr = footprintConverter.convert(componentData);
9433
+ } catch (e) {
9434
+ logger7.warn("Footprint conversion failed:", e);
9435
+ }
9436
+ res.writeHead(200, { "Content-Type": "application/json" });
9437
+ res.end(JSON.stringify({
9438
+ uuid: component2.uuid,
9439
+ title: component2.title,
9440
+ description: component2.description,
9441
+ owner: component2.owner,
9442
+ symbolSexpr,
9443
+ footprintSexpr,
9444
+ model3d: component2.model3d
9445
+ }));
9446
+ } catch (error) {
9447
+ logger7.error("Component fetch error:", error);
9448
+ res.writeHead(500, { "Content-Type": "application/json" });
9449
+ res.end(JSON.stringify({ error: "Failed to fetch component" }));
9450
+ }
9451
+ }
9452
+
9453
+ // src/http/server.ts
9454
+ var logger8 = createLogger("http-server");
9455
+ var DEFAULT_PORT = 3847;
9456
+ var serverInstance = null;
9457
+ function startHttpServer(options = {}) {
9458
+ if (serverInstance) {
9459
+ logger8.debug("HTTP server already running");
9460
+ const port2 = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
9461
+ options.onReady?.(`http://localhost:${port2}`);
9462
+ return port2;
9463
+ }
9464
+ const port = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
9465
+ serverInstance = createServer(async (req, res) => {
9466
+ res.setHeader("Access-Control-Allow-Origin", "*");
9467
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
9468
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
9469
+ if (req.method === "OPTIONS") {
9470
+ res.writeHead(204);
9471
+ res.end();
9472
+ return;
9473
+ }
9474
+ try {
9475
+ await handleRequest(req, res);
9476
+ } catch (error) {
9477
+ logger8.error("Request error:", error);
9478
+ res.writeHead(500, { "Content-Type": "application/json" });
9479
+ res.end(JSON.stringify({ error: "Internal server error" }));
9480
+ }
9481
+ });
9482
+ serverInstance.listen(port, () => {
9483
+ const url = `http://localhost:${port}`;
9484
+ logger8.info(`HTTP server listening on ${url}`);
9485
+ options.onReady?.(url);
9486
+ });
9487
+ serverInstance.on("error", (error) => {
9488
+ if (error.code === "EADDRINUSE") {
9489
+ logger8.warn(`Port ${port} already in use, HTTP server not started`);
9490
+ } else {
9491
+ logger8.error("HTTP server error:", error);
9492
+ }
9493
+ });
9494
+ return port;
9495
+ }
9496
+ function stopHttpServer() {
9497
+ if (serverInstance) {
9498
+ serverInstance.close();
9499
+ serverInstance = null;
9500
+ logger8.info("HTTP server stopped");
9501
+ }
9502
+ }
9070
9503
  export {
9071
9504
  writeText,
9072
9505
  writeJson,
@@ -9075,6 +9508,8 @@ export {
9075
9508
  validateDesignConstraints,
9076
9509
  validateComponent,
9077
9510
  symbolConverter,
9511
+ stopHttpServer,
9512
+ startHttpServer,
9078
9513
  safeParseInt,
9079
9514
  safeParseFloat,
9080
9515
  roundTo,
@@ -1 +1 @@
1
- {"version":3,"file":"easyeda-shapes.d.ts","sourceRoot":"","sources":["../../src/parsers/easyeda-shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAEhB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAQ7B;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA2CjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAiBtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAc1E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAe5E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAYpE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAY9E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAY5E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAYtE;AAMD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAwBxD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAc5D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAa1D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAe9D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAexD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAgB1D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAexD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAuB1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAexE;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAwEpE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAmG1E"}
1
+ {"version":3,"file":"easyeda-shapes.d.ts","sourceRoot":"","sources":["../../src/parsers/easyeda-shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAEhB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAQ7B;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA2CjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAiBtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAc1E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAe5E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAYpE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAY9E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAY5E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAYtE;AAMD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAwBxD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAc5D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAa1D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAe9D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAexD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAgB1D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAexD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAuB1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAexE;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAwEpE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAoG1E"}