@jlcpcb/cli 0.1.0 → 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.
package/dist/index.js CHANGED
@@ -2693,7 +2693,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
2693
2693
 
2694
2694
  // ../../node_modules/react/index.js
2695
2695
  var require_react = __commonJS((exports, module) => {
2696
- var react_development = __toESM(require_react_development(), 1);
2696
+ var react_development = __toESM(require_react_development());
2697
2697
  if (false) {} else {
2698
2698
  module.exports = react_development;
2699
2699
  }
@@ -3145,7 +3145,7 @@ var require_scheduler_development = __commonJS((exports) => {
3145
3145
 
3146
3146
  // ../../node_modules/scheduler/index.js
3147
3147
  var require_scheduler = __commonJS((exports, module) => {
3148
- var scheduler_development = __toESM(require_scheduler_development(), 1);
3148
+ var scheduler_development = __toESM(require_scheduler_development());
3149
3149
  if (false) {} else {
3150
3150
  module.exports = scheduler_development;
3151
3151
  }
@@ -3153,8 +3153,8 @@ var require_scheduler = __commonJS((exports, module) => {
3153
3153
 
3154
3154
  // ../../node_modules/react-reconciler/cjs/react-reconciler.development.js
3155
3155
  var require_react_reconciler_development = __commonJS((exports, module) => {
3156
- var React = __toESM(require_react(), 1);
3157
- var Scheduler = __toESM(require_scheduler(), 1);
3156
+ var React = __toESM(require_react());
3157
+ var Scheduler = __toESM(require_scheduler());
3158
3158
  module.exports = function($$$config) {
3159
3159
  function findHook(fiber, id) {
3160
3160
  for (fiber = fiber.memoizedState;fiber !== null && 0 < id; )
@@ -33706,7 +33706,7 @@ var require_stack_utils = __commonJS((exports, module) => {
33706
33706
 
33707
33707
  // ../../node_modules/react/cjs/react-jsx-dev-runtime.development.js
33708
33708
  var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
33709
- var React10 = __toESM(require_react(), 1);
33709
+ var React10 = __toESM(require_react());
33710
33710
  (function() {
33711
33711
  function getComponentNameFromType(type) {
33712
33712
  if (type == null)
@@ -33921,7 +33921,7 @@ React keys must be passed directly to JSX without using spread:
33921
33921
 
33922
33922
  // ../../node_modules/react/jsx-dev-runtime.js
33923
33923
  var require_jsx_dev_runtime = __commonJS((exports, module) => {
33924
- var react_jsx_dev_runtime_development = __toESM(require_react_jsx_dev_runtime_development(), 1);
33924
+ var react_jsx_dev_runtime_development = __toESM(require_react_jsx_dev_runtime_development());
33925
33925
  if (false) {} else {
33926
33926
  module.exports = react_jsx_dev_runtime_development;
33927
33927
  }
@@ -34086,6 +34086,10 @@ import { existsSync as existsSync3 } from "fs";
34086
34086
  import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
34087
34087
  import { homedir as homedir2, platform as platform2 } from "os";
34088
34088
  import { join as join4 } from "path";
34089
+ import { createServer } from "http";
34090
+ import { readFileSync } from "fs";
34091
+ import { join as join5, dirname as dirname3 } from "path";
34092
+ import { fileURLToPath } from "url";
34089
34093
  var __defProp2 = Object.defineProperty;
34090
34094
  var __export2 = (target, all) => {
34091
34095
  for (var name in all)
@@ -34693,7 +34697,8 @@ var DEFAULT_LIBRARY_DESCRIPTION = "JLC-MCP Component Library (LCSC/EasyEDA)";
34693
34697
  var init_lib_table = () => {};
34694
34698
  var exports_global_lib_table = {};
34695
34699
  __export2(exports_global_lib_table, {
34696
- ensureGlobalLibraryTables: () => ensureGlobalLibraryTables
34700
+ ensureGlobalLibraryTables: () => ensureGlobalLibraryTables,
34701
+ ensureGlobalEasyEDALibrary: () => ensureGlobalEasyEDALibrary
34697
34702
  });
34698
34703
  function detectKicadVersion() {
34699
34704
  const home = homedir();
@@ -34870,6 +34875,51 @@ async function ensureLibraryStubs(version) {
34870
34875
  }
34871
34876
  return { symbolsCreated, directoriesCreated };
34872
34877
  }
34878
+ function getEasyEDASymbolLibUri() {
34879
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${EASYEDA_SYMBOL_LIBRARY_NAME}`;
34880
+ }
34881
+ function getEasyEDAFootprintLibUri() {
34882
+ return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/footprints/${EASYEDA_FOOTPRINT_LIBRARY_NAME}`;
34883
+ }
34884
+ async function ensureGlobalEasyEDALibrary() {
34885
+ const version = detectKicadVersion();
34886
+ const configDir = getKicadConfigDir(version);
34887
+ await mkdir2(configDir, { recursive: true });
34888
+ const symTablePath = join3(configDir, "sym-lib-table");
34889
+ if (existsSync2(symTablePath)) {
34890
+ let content = await readFile3(symTablePath, "utf-8");
34891
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
34892
+ const uri = getEasyEDASymbolLibUri();
34893
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "sym", EASYEDA_LIBRARY_DESCRIPTION);
34894
+ await writeFile3(symTablePath, content, "utf-8");
34895
+ }
34896
+ } else {
34897
+ const uri = getEasyEDASymbolLibUri();
34898
+ const content = `(sym_lib_table
34899
+ (version 7)
34900
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
34901
+ )
34902
+ `;
34903
+ await writeFile3(symTablePath, content, "utf-8");
34904
+ }
34905
+ const fpTablePath = join3(configDir, "fp-lib-table");
34906
+ if (existsSync2(fpTablePath)) {
34907
+ let content = await readFile3(fpTablePath, "utf-8");
34908
+ if (!libraryExistsInTable(content, EASYEDA_LIBRARY_NAME)) {
34909
+ const uri = getEasyEDAFootprintLibUri();
34910
+ content = addLibraryToTable(content, EASYEDA_LIBRARY_NAME, uri, "fp", EASYEDA_LIBRARY_DESCRIPTION);
34911
+ await writeFile3(fpTablePath, content, "utf-8");
34912
+ }
34913
+ } else {
34914
+ const uri = getEasyEDAFootprintLibUri();
34915
+ const content = `(fp_lib_table
34916
+ (version 7)
34917
+ (lib (name "${EASYEDA_LIBRARY_NAME}")(type "KiCad")(uri "${uri}")(options "")(descr "${EASYEDA_LIBRARY_DESCRIPTION}"))
34918
+ )
34919
+ `;
34920
+ await writeFile3(fpTablePath, content, "utf-8");
34921
+ }
34922
+ }
34873
34923
  async function ensureGlobalLibraryTables() {
34874
34924
  const errors2 = [];
34875
34925
  const version = detectKicadVersion();
@@ -34922,6 +34972,10 @@ var LIBRARY_PREFIX2 = "JLC-MCP";
34922
34972
  var LIBRARY_NAMESPACE = "jlc_mcp";
34923
34973
  var KICAD_3RD_PARTY_VAR = "${KICAD9_3RD_PARTY}";
34924
34974
  var LIBRARY_DESCRIPTION = "Autogenerated by JLC-MCP";
34975
+ var EASYEDA_LIBRARY_NAME = "EasyEDA";
34976
+ var EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym";
34977
+ var EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty";
34978
+ var EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
34925
34979
  var init_global_lib_table = __esm2(() => {
34926
34980
  init_category_router();
34927
34981
  init_lib_table();
@@ -39658,7 +39712,7 @@ function parseFootprintShapes(shapes) {
39658
39712
  }
39659
39713
  }
39660
39714
  }
39661
- const type = pads.some((p) => p.holeRadius > 0) ? "tht" : "smd";
39715
+ const type = pads.some((p) => p.holeRadius > 0 || p.shape === "POLYGON" && p.isPlated) ? "tht" : "smd";
39662
39716
  return {
39663
39717
  name: "Unknown",
39664
39718
  type,
@@ -41644,7 +41698,6 @@ ${template.bodyGraphics}
41644
41698
  }
41645
41699
  }
41646
41700
  var symbolConverter = new SymbolConverter;
41647
- init_category_router();
41648
41701
  var SMD_SIZES = {
41649
41702
  "0201": { metric: "0603", imperialMm: "0.6x0.3" },
41650
41703
  "0402": { metric: "1005", imperialMm: "1.0x0.5" },
@@ -41661,57 +41714,7 @@ var PASSIVE_LIBRARIES = {
41661
41714
  C: "Capacitor_SMD",
41662
41715
  L: "Inductor_SMD"
41663
41716
  };
41664
- var LED_LIBRARY = "LED_SMD";
41665
- var DIODE_LIBRARY = "Diode_SMD";
41666
- var CATEGORY_TO_PREFIX2 = {
41667
- Resistors: "R",
41668
- Capacitors: "C",
41669
- Inductors: "L",
41670
- Diodes: "D"
41671
- };
41672
- var SOIC_MAPPINGS = {
41673
- "SOIC-8": "SOIC-8_3.9x4.9mm_P1.27mm",
41674
- "SOP-8": "SOIC-8_3.9x4.9mm_P1.27mm",
41675
- "SOIC-14": "SOIC-14_3.9x8.7mm_P1.27mm",
41676
- "SOIC-16": "SOIC-16_3.9x9.9mm_P1.27mm",
41677
- "SOIC-16W": "SOIC-16W_7.5x10.3mm_P1.27mm",
41678
- "SOIC-20": "SOIC-20W_7.5x12.8mm_P1.27mm",
41679
- "SOIC-24": "SOIC-24W_7.5x15.4mm_P1.27mm",
41680
- "SOIC-28": "SOIC-28W_7.5x17.9mm_P1.27mm"
41681
- };
41682
- var TSSOP_MAPPINGS = {
41683
- "TSSOP-8": "TSSOP-8_3x3mm_P0.65mm",
41684
- "TSSOP-14": "TSSOP-14_4.4x5mm_P0.65mm",
41685
- "TSSOP-16": "TSSOP-16_4.4x5mm_P0.65mm",
41686
- "TSSOP-20": "TSSOP-20_4.4x6.5mm_P0.65mm",
41687
- "TSSOP-24": "TSSOP-24_4.4x7.8mm_P0.65mm",
41688
- "TSSOP-28": "TSSOP-28_4.4x9.7mm_P0.65mm"
41689
- };
41690
- var SOT_MAPPINGS = {
41691
- "SOT-23": { library: "Package_TO_SOT_SMD", footprint: "SOT-23" },
41692
- "SOT-23-3": { library: "Package_TO_SOT_SMD", footprint: "SOT-23" },
41693
- "SOT-23-5": { library: "Package_TO_SOT_SMD", footprint: "SOT-23-5" },
41694
- "SOT-23-6": { library: "Package_TO_SOT_SMD", footprint: "SOT-23-6" },
41695
- "SOT-89": { library: "Package_TO_SOT_SMD", footprint: "SOT-89-3" },
41696
- "SOT-223": { library: "Package_TO_SOT_SMD", footprint: "SOT-223-3_TabPin2" }
41697
- };
41698
- var SOD_MAPPINGS = {
41699
- "SOD-123": { library: "Diode_SMD", footprint: "D_SOD-123" },
41700
- "SOD-123F": { library: "Diode_SMD", footprint: "D_SOD-123F" },
41701
- "SOD-323": { library: "Diode_SMD", footprint: "D_SOD-323" },
41702
- "SOD-523": { library: "Diode_SMD", footprint: "D_SOD-523" },
41703
- "SOD-923": { library: "Diode_SMD", footprint: "D_SOD-923" },
41704
- "SOD-128": { library: "Diode_SMD", footprint: "D_SOD-128" },
41705
- "SOD-80": { library: "Diode_SMD", footprint: "D_SOD-80" }
41706
- };
41707
- var DIODE_PACKAGE_MAPPINGS = {
41708
- SMA: { library: "Diode_SMD", footprint: "D_SMA" },
41709
- SMB: { library: "Diode_SMD", footprint: "D_SMB" },
41710
- SMC: { library: "Diode_SMD", footprint: "D_SMC" },
41711
- MELF: { library: "Diode_SMD", footprint: "D_MELF" },
41712
- MINIMELF: { library: "Diode_SMD", footprint: "D_MiniMELF" },
41713
- MICROMELF: { library: "Diode_SMD", footprint: "D_MicroMELF" }
41714
- };
41717
+ var PASSIVE_PAD_COUNT = 2;
41715
41718
  function normalizePackageName(name) {
41716
41719
  return name.toUpperCase().replace(/[_\s-]+/g, "-").replace(/\(.*?\)/g, "").trim();
41717
41720
  }
@@ -41726,58 +41729,23 @@ function extractSmdSize(packageName) {
41726
41729
  const match = normalized.match(sizePattern);
41727
41730
  return match ? match[1] : null;
41728
41731
  }
41729
- function mapToKicadFootprint(packageName, componentPrefix, category, description) {
41730
- const normalized = normalizePackageName(packageName);
41731
- let prefix = componentPrefix.toUpperCase();
41732
- const isKnownPrefix = PASSIVE_LIBRARIES[prefix] || prefix === "D" || prefix === "LED";
41733
- if (!isKnownPrefix && (category || description)) {
41734
- const detectedCategory = getLibraryCategory(prefix, category, description);
41735
- const categoryPrefix = CATEGORY_TO_PREFIX2[detectedCategory];
41736
- if (categoryPrefix) {
41737
- prefix = categoryPrefix;
41738
- }
41732
+ function mapToKicadFootprint(packageName, componentPrefix, _category, _description) {
41733
+ const prefix = componentPrefix.toUpperCase();
41734
+ if (!PASSIVE_LIBRARIES[prefix]) {
41735
+ return null;
41739
41736
  }
41740
41737
  const smdSize = extractSmdSize(packageName);
41741
- if (smdSize && PASSIVE_LIBRARIES[prefix]) {
41738
+ if (smdSize) {
41742
41739
  const sizeInfo = SMD_SIZES[smdSize];
41743
41740
  const library = PASSIVE_LIBRARIES[prefix];
41744
41741
  const footprint = `${prefix}_${smdSize}_${sizeInfo.metric}Metric`;
41745
41742
  return { library, footprint };
41746
41743
  }
41747
- if (smdSize && prefix === "LED") {
41748
- const sizeInfo = SMD_SIZES[smdSize];
41749
- const footprint = `LED_${smdSize}_${sizeInfo.metric}Metric`;
41750
- return { library: LED_LIBRARY, footprint };
41751
- }
41752
- if (smdSize && prefix === "D") {
41753
- const sizeInfo = SMD_SIZES[smdSize];
41754
- const footprint = `D_${smdSize}_${sizeInfo.metric}Metric`;
41755
- return { library: DIODE_LIBRARY, footprint };
41756
- }
41757
- for (const [pattern, mapping] of Object.entries(SOD_MAPPINGS)) {
41758
- if (normalized.includes(pattern) || normalized.startsWith(pattern.replace(/-/g, ""))) {
41759
- return mapping;
41760
- }
41761
- }
41762
- for (const [pattern, mapping] of Object.entries(DIODE_PACKAGE_MAPPINGS)) {
41763
- if (normalized === pattern || normalized.startsWith(pattern + "-") || normalized.startsWith(pattern + "_")) {
41764
- return mapping;
41765
- }
41766
- }
41767
- for (const [pattern, footprint] of Object.entries(SOIC_MAPPINGS)) {
41768
- if (normalized.includes(pattern) || normalized.startsWith(pattern.replace(/-/g, ""))) {
41769
- return { library: "Package_SO", footprint };
41770
- }
41771
- }
41772
- for (const [pattern, footprint] of Object.entries(TSSOP_MAPPINGS)) {
41773
- if (normalized.includes(pattern) || normalized.startsWith(pattern.replace(/-/g, ""))) {
41774
- return { library: "Package_SO", footprint };
41775
- }
41776
- }
41777
- for (const [pattern, mapping] of Object.entries(SOT_MAPPINGS)) {
41778
- if (normalized.includes(pattern) || normalized === pattern.replace(/-/g, "")) {
41779
- return mapping;
41780
- }
41744
+ return null;
41745
+ }
41746
+ function getExpectedPadCount(mapping) {
41747
+ if (mapping.library === "Resistor_SMD" || mapping.library === "Capacitor_SMD" || mapping.library === "Inductor_SMD") {
41748
+ return PASSIVE_PAD_COUNT;
41781
41749
  }
41782
41750
  return null;
41783
41751
  }
@@ -41808,8 +41776,8 @@ var KI_PAD_LAYER_SMD = {
41808
41776
  11: '"*.Cu" "*.Paste" "*.Mask"'
41809
41777
  };
41810
41778
  var KI_PAD_LAYER_THT = {
41811
- 1: '"F.Cu" "F.Mask"',
41812
- 2: '"B.Cu" "B.Mask"',
41779
+ 1: '"*.Cu" "*.Mask"',
41780
+ 2: '"*.Cu" "*.Mask"',
41813
41781
  11: '"*.Cu" "*.Mask"'
41814
41782
  };
41815
41783
  var KI_PAD_SHAPE = {
@@ -41955,13 +41923,19 @@ class FootprintConverter {
41955
41923
  const { info, footprint } = component;
41956
41924
  const packageName = footprint.name;
41957
41925
  const prefix = info.prefix;
41926
+ const actualPadCount = footprint.pads.length;
41958
41927
  const mapping = mapToKicadFootprint(packageName, prefix, info.category, info.description);
41959
41928
  if (mapping) {
41960
- return {
41961
- type: "reference",
41962
- reference: getKicadFootprintRef(mapping),
41963
- name: mapping.footprint
41964
- };
41929
+ const expectedPadCount = getExpectedPadCount(mapping);
41930
+ if (expectedPadCount !== null && expectedPadCount !== actualPadCount) {
41931
+ console.warn(`[footprint] Pad count mismatch for ${packageName}: expected ${expectedPadCount}, got ${actualPadCount}. Generating custom footprint.`);
41932
+ } else {
41933
+ return {
41934
+ type: "reference",
41935
+ reference: getKicadFootprintRef(mapping),
41936
+ name: mapping.footprint
41937
+ };
41938
+ }
41965
41939
  }
41966
41940
  const content = this.convert(component, options);
41967
41941
  const name = this.sanitizeName(footprint.name);
@@ -42013,15 +41987,20 @@ class FootprintConverter {
42013
41987
  `;
42014
41988
  return output;
42015
41989
  }
42016
- generatePolygonPad(pad, origin, layers) {
41990
+ generatePolygonPad(pad, origin, _layers) {
42017
41991
  const x = convertX(pad.centerX, origin.x);
42018
41992
  const y = convertY(pad.centerY, origin.y);
42019
41993
  const points = parsePoints(pad.points);
42020
41994
  if (points.length < 3) {
42021
41995
  return this.generatePad({ ...pad, shape: "RECT", points: "" }, origin);
42022
41996
  }
42023
- const isSmd = pad.holeRadius === 0;
41997
+ let holeRadius = pad.holeRadius;
41998
+ if (holeRadius === 0 && pad.isPlated && pad.shape === "POLYGON") {
41999
+ holeRadius = this.calculateDrillRadiusFromPolygon(points, pad.centerX, pad.centerY);
42000
+ }
42001
+ const isSmd = holeRadius === 0;
42024
42002
  const padType = isSmd ? "smd" : "thru_hole";
42003
+ const layers = getPadLayers(pad.layerId, isSmd);
42025
42004
  const polyPoints = points.map((p) => ({
42026
42005
  x: roundTo(toMM(p.x - pad.centerX), 2),
42027
42006
  y: roundTo(toMM(p.y - pad.centerY), 2)
@@ -42033,7 +42012,7 @@ class FootprintConverter {
42033
42012
  output += ` (size 0.01 0.01)
42034
42013
  `;
42035
42014
  if (!isSmd) {
42036
- const drillDiameter = roundTo(toMM(pad.holeRadius * 2), 4);
42015
+ const drillDiameter = roundTo(toMM(holeRadius * 2), 4);
42037
42016
  if (pad.holeLength && pad.holeLength > 0) {
42038
42017
  const holeH = roundTo(toMM(pad.holeLength), 4);
42039
42018
  output += ` (drill oval ${drillDiameter} ${holeH})
@@ -42067,6 +42046,25 @@ class FootprintConverter {
42067
42046
  `;
42068
42047
  return output;
42069
42048
  }
42049
+ calculateDrillRadiusFromPolygon(points, centerX, centerY) {
42050
+ if (points.length < 3)
42051
+ return 0;
42052
+ let minX = Infinity, maxX = -Infinity;
42053
+ let minY = Infinity, maxY = -Infinity;
42054
+ for (const pt of points) {
42055
+ const relX = pt.x - centerX;
42056
+ const relY = pt.y - centerY;
42057
+ minX = Math.min(minX, relX);
42058
+ maxX = Math.max(maxX, relX);
42059
+ minY = Math.min(minY, relY);
42060
+ maxY = Math.max(maxY, relY);
42061
+ }
42062
+ const width = maxX - minX;
42063
+ const height = maxY - minY;
42064
+ const minDim = Math.min(width, height);
42065
+ const drillDiameter = minDim * 0.6;
42066
+ return drillDiameter / 2;
42067
+ }
42070
42068
  generateHole(hole, origin) {
42071
42069
  const x = convertX(hole.centerX, origin.x);
42072
42070
  const y = convertY(hole.centerY, origin.y);
@@ -42193,9 +42191,9 @@ class FootprintConverter {
42193
42191
  const fontSize = roundTo(toMM(text.fontSize), 2);
42194
42192
  const rotation = text.rotation || 0;
42195
42193
  let justify = "";
42196
- if (x < -0.5) {
42194
+ if (text.type === "L") {
42197
42195
  justify = "left";
42198
- } else if (x > 0.5) {
42196
+ } else if (text.type === "R") {
42199
42197
  justify = "right";
42200
42198
  }
42201
42199
  return ` (fp_text user "${this.escapeString(text.text)}"
@@ -42212,6 +42210,9 @@ ${justify ? ` (justify ${justify})
42212
42210
  `;
42213
42211
  }
42214
42212
  generateSolidRegion(region, origin) {
42213
+ if (region.layerId === 99 || region.layerId === 100 || region.layerId === 101) {
42214
+ return "";
42215
+ }
42215
42216
  const layer = getLayer(region.layerId);
42216
42217
  const points = this.parseSvgPathToPoints(region.path, origin);
42217
42218
  if (points.length < 3)
@@ -42244,15 +42245,111 @@ ${justify ? ` (justify ${justify})
42244
42245
  }
42245
42246
  parseSvgPathToPoints(path, origin) {
42246
42247
  const points = [];
42247
- const commandRegex = /([ML])\s*([\d.-]+)[,\s]+([\d.-]+)/gi;
42248
+ let currentX = 0, currentY = 0;
42249
+ let startX = 0, startY = 0;
42250
+ const commandRegex = /([MLHVCSQTAZ])\s*([^MLHVCSQTAZ]*)/gi;
42248
42251
  let match;
42249
42252
  while ((match = commandRegex.exec(path)) !== null) {
42250
- const x = parseFloat(match[2]);
42251
- const y = parseFloat(match[3]);
42252
- points.push({
42253
- x: convertX(x, origin.x),
42254
- y: convertY(y, origin.y)
42255
- });
42253
+ const cmd = match[1].toUpperCase();
42254
+ const args = match[2].trim().split(/[\s,]+/).map(parseFloat).filter((n) => !isNaN(n));
42255
+ switch (cmd) {
42256
+ case "M":
42257
+ if (args.length >= 2) {
42258
+ currentX = args[0];
42259
+ currentY = args[1];
42260
+ if (points.length === 0) {
42261
+ startX = currentX;
42262
+ startY = currentY;
42263
+ }
42264
+ points.push({
42265
+ x: convertX(currentX, origin.x),
42266
+ y: convertY(currentY, origin.y)
42267
+ });
42268
+ }
42269
+ break;
42270
+ case "L":
42271
+ if (args.length >= 2) {
42272
+ currentX = args[0];
42273
+ currentY = args[1];
42274
+ points.push({
42275
+ x: convertX(currentX, origin.x),
42276
+ y: convertY(currentY, origin.y)
42277
+ });
42278
+ }
42279
+ break;
42280
+ case "H":
42281
+ if (args.length >= 1) {
42282
+ currentX = args[0];
42283
+ points.push({
42284
+ x: convertX(currentX, origin.x),
42285
+ y: convertY(currentY, origin.y)
42286
+ });
42287
+ }
42288
+ break;
42289
+ case "V":
42290
+ if (args.length >= 1) {
42291
+ currentY = args[0];
42292
+ points.push({
42293
+ x: convertX(currentX, origin.x),
42294
+ y: convertY(currentY, origin.y)
42295
+ });
42296
+ }
42297
+ break;
42298
+ case "C":
42299
+ if (args.length >= 6) {
42300
+ currentX = args[4];
42301
+ currentY = args[5];
42302
+ points.push({
42303
+ x: convertX(currentX, origin.x),
42304
+ y: convertY(currentY, origin.y)
42305
+ });
42306
+ }
42307
+ break;
42308
+ case "S":
42309
+ if (args.length >= 4) {
42310
+ currentX = args[2];
42311
+ currentY = args[3];
42312
+ points.push({
42313
+ x: convertX(currentX, origin.x),
42314
+ y: convertY(currentY, origin.y)
42315
+ });
42316
+ }
42317
+ break;
42318
+ case "Q":
42319
+ if (args.length >= 4) {
42320
+ currentX = args[2];
42321
+ currentY = args[3];
42322
+ points.push({
42323
+ x: convertX(currentX, origin.x),
42324
+ y: convertY(currentY, origin.y)
42325
+ });
42326
+ }
42327
+ break;
42328
+ case "T":
42329
+ if (args.length >= 2) {
42330
+ currentX = args[0];
42331
+ currentY = args[1];
42332
+ points.push({
42333
+ x: convertX(currentX, origin.x),
42334
+ y: convertY(currentY, origin.y)
42335
+ });
42336
+ }
42337
+ break;
42338
+ case "A":
42339
+ if (args.length >= 7) {
42340
+ currentX = args[5];
42341
+ currentY = args[6];
42342
+ points.push({
42343
+ x: convertX(currentX, origin.x),
42344
+ y: convertY(currentY, origin.y)
42345
+ });
42346
+ }
42347
+ break;
42348
+ case "Z":
42349
+ currentX = startX;
42350
+ currentY = startY;
42351
+ break;
42352
+ }
42256
42353
  }
42257
42354
  return points;
42258
42355
  }
@@ -42420,10 +42517,14 @@ var FOOTPRINT_LIBRARY_NAME = getFootprintDirName();
42420
42517
  var MODELS_3D_NAME = get3DModelsDirName();
42421
42518
  var LIBRARY_NAMESPACE2 = "jlc_mcp";
42422
42519
  var KICAD_VERSIONS3 = ["9.0", "8.0"];
42423
- var EASYEDA_LIBRARY_NAME = "EasyEDA";
42424
- var EASYEDA_SYMBOL_LIBRARY_NAME = "EasyEDA.kicad_sym";
42425
- var EASYEDA_FOOTPRINT_LIBRARY_NAME = "EasyEDA.pretty";
42426
- var EASYEDA_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library";
42520
+ var EASYEDA_LIBRARY_NAME2 = "EasyEDA";
42521
+ var EASYEDA_SYMBOL_LIBRARY_NAME2 = "EasyEDA.kicad_sym";
42522
+ var EASYEDA_FOOTPRINT_LIBRARY_NAME2 = "EasyEDA.pretty";
42523
+ var EASYEDA_LIBRARY_DESCRIPTION2 = "EasyEDA Community Component Library";
42524
+ var EASYEDA_LOCAL_LIBRARY_NAME = "EasyEDA-local";
42525
+ var EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME = "EasyEDA-local.kicad_sym";
42526
+ var EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME = "EasyEDA-local.pretty";
42527
+ var EASYEDA_LOCAL_LIBRARY_DESCRIPTION = "EasyEDA Community Component Library (Project-local)";
42427
42528
  function detectKicadVersion2() {
42428
42529
  const home = homedir2();
42429
42530
  const baseDir = join4(home, "Documents", "KiCad");
@@ -42564,10 +42665,7 @@ function createLibraryService() {
42564
42665
  return {
42565
42666
  async install(id, options = {}) {
42566
42667
  const isCommunityComponent = !isLcscId2(id);
42567
- if (isCommunityComponent && !options.projectPath) {
42568
- throw new Error("EasyEDA community components require projectPath for local storage");
42569
- }
42570
- const isGlobal = !isCommunityComponent && !options.projectPath;
42668
+ const isGlobal = !options.projectPath;
42571
42669
  const paths = isGlobal ? getGlobalLibraryPaths() : getProjectLibraryPaths(options.projectPath);
42572
42670
  let component = null;
42573
42671
  if (isLcscId2(id)) {
@@ -42610,13 +42708,27 @@ function createLibraryService() {
42610
42708
  let category;
42611
42709
  let modelPath;
42612
42710
  if (isCommunityComponent) {
42613
- const librariesDir = join4(options.projectPath, "libraries");
42614
- const symbolsDir = join4(librariesDir, "symbols");
42615
- footprintDir = join4(librariesDir, "footprints", EASYEDA_FOOTPRINT_LIBRARY_NAME);
42616
- models3dDir = join4(librariesDir, "3dmodels", "EasyEDA.3dshapes");
42711
+ const libName = isGlobal ? EASYEDA_LIBRARY_NAME2 : EASYEDA_LOCAL_LIBRARY_NAME;
42712
+ const symLibFile = isGlobal ? EASYEDA_SYMBOL_LIBRARY_NAME2 : EASYEDA_LOCAL_SYMBOL_LIBRARY_NAME;
42713
+ const fpLibDir = isGlobal ? EASYEDA_FOOTPRINT_LIBRARY_NAME2 : EASYEDA_LOCAL_FOOTPRINT_LIBRARY_NAME;
42714
+ const libDesc = isGlobal ? EASYEDA_LIBRARY_DESCRIPTION2 : EASYEDA_LOCAL_LIBRARY_DESCRIPTION;
42715
+ const models3dDirName = isGlobal ? "EasyEDA.3dshapes" : "EasyEDA-local.3dshapes";
42716
+ let symbolsDir;
42717
+ let easyedaModelsDir;
42718
+ if (isGlobal) {
42719
+ symbolsDir = paths.symbolsDir;
42720
+ footprintDir = join4(paths.footprintsDir, fpLibDir);
42721
+ easyedaModelsDir = join4(paths.models3dDir, models3dDirName);
42722
+ } else {
42723
+ const librariesDir = join4(options.projectPath, "libraries");
42724
+ symbolsDir = join4(librariesDir, "symbols");
42725
+ footprintDir = join4(librariesDir, "footprints", fpLibDir);
42726
+ easyedaModelsDir = join4(librariesDir, "3dmodels", models3dDirName);
42727
+ }
42728
+ models3dDir = easyedaModelsDir;
42617
42729
  await ensureDir(symbolsDir);
42618
42730
  await ensureDir(footprintDir);
42619
- symbolFile = join4(symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME);
42731
+ symbolFile = join4(symbolsDir, symLibFile);
42620
42732
  symbolName = component.info.name.replace(/[^a-zA-Z0-9_-]/g, "_");
42621
42733
  const include3d = options.include3d ?? true;
42622
42734
  let modelRelativePath;
@@ -42627,21 +42739,30 @@ function createLibraryService() {
42627
42739
  const modelFilename = `${symbolName}.step`;
42628
42740
  modelPath = join4(models3dDir, modelFilename);
42629
42741
  await writeBinary(modelPath, model);
42630
- modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/EasyEDA.3dshapes/${modelFilename}`;
42742
+ if (isGlobal) {
42743
+ modelRelativePath = `\${KICAD9_3RD_PARTY}/${LIBRARY_NAMESPACE2}/3dmodels/${models3dDirName}/${modelFilename}`;
42744
+ } else {
42745
+ modelRelativePath = `\${KIPRJMOD}/libraries/3dmodels/${models3dDirName}/${modelFilename}`;
42746
+ }
42631
42747
  }
42632
42748
  }
42633
42749
  const footprint = footprintConverter.convert(component, {
42634
- libraryName: EASYEDA_LIBRARY_NAME,
42750
+ libraryName: libName,
42635
42751
  include3DModel: !!modelRelativePath,
42636
42752
  modelPath: modelRelativePath
42637
42753
  });
42638
42754
  footprintPath = join4(footprintDir, `${symbolName}.kicad_mod`);
42639
- footprintRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
42755
+ footprintRef = `${libName}:${symbolName}`;
42640
42756
  await writeText(footprintPath, footprint);
42641
42757
  component.info.package = footprintRef;
42642
- await ensureSymLibTable(options.projectPath, symbolFile, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
42643
- await ensureFpLibTable(options.projectPath, footprintDir, EASYEDA_LIBRARY_NAME, EASYEDA_LIBRARY_DESCRIPTION);
42644
- symbolRef = `${EASYEDA_LIBRARY_NAME}:${symbolName}`;
42758
+ if (isGlobal) {
42759
+ const { ensureGlobalEasyEDALibrary: ensureGlobalEasyEDALibrary2 } = await Promise.resolve().then(() => (init_global_lib_table(), exports_global_lib_table));
42760
+ await ensureGlobalEasyEDALibrary2();
42761
+ } else {
42762
+ await ensureSymLibTable(options.projectPath, symbolFile, libName, libDesc);
42763
+ await ensureFpLibTable(options.projectPath, footprintDir, libName, libDesc);
42764
+ }
42765
+ symbolRef = `${libName}:${symbolName}`;
42645
42766
  } else {
42646
42767
  category = getLibraryCategory(component.info.prefix, component.info.category, component.info.description);
42647
42768
  const symbolLibraryFilename = getLibraryFilename(category);
@@ -42688,7 +42809,7 @@ function createLibraryService() {
42688
42809
  if (symbolConverter.symbolExistsInLibrary(existingContent, component.info.name)) {
42689
42810
  if (options.force) {
42690
42811
  symbolContent = symbolConverter.replaceInLibrary(existingContent, component, {
42691
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
42812
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
42692
42813
  symbolName: isCommunityComponent ? symbolName : undefined
42693
42814
  });
42694
42815
  symbolAction = "replaced";
@@ -42698,14 +42819,14 @@ function createLibraryService() {
42698
42819
  }
42699
42820
  } else {
42700
42821
  symbolContent = symbolConverter.appendToLibrary(existingContent, component, {
42701
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
42822
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
42702
42823
  symbolName: isCommunityComponent ? symbolName : undefined
42703
42824
  });
42704
42825
  symbolAction = "appended";
42705
42826
  }
42706
42827
  } else {
42707
42828
  symbolContent = symbolConverter.convert(component, {
42708
- libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME : undefined,
42829
+ libraryName: isCommunityComponent ? EASYEDA_LIBRARY_NAME2 : undefined,
42709
42830
  symbolName: isCommunityComponent ? symbolName : undefined
42710
42831
  });
42711
42832
  symbolAction = "created";
@@ -42830,12 +42951,243 @@ function createLibraryService() {
42830
42951
  fpLibTable: fpLibTablePath
42831
42952
  }
42832
42953
  };
42954
+ },
42955
+ async isEasyEDAInstalled(componentName) {
42956
+ const paths = getGlobalLibraryPaths();
42957
+ const easyedaLibPath = join4(paths.symbolsDir, EASYEDA_SYMBOL_LIBRARY_NAME2);
42958
+ if (!existsSync3(easyedaLibPath)) {
42959
+ return false;
42960
+ }
42961
+ try {
42962
+ const content = await readFile4(easyedaLibPath, "utf-8");
42963
+ const sanitizedName = componentName.replace(/[^a-zA-Z0-9_-]/g, "_");
42964
+ const pattern = new RegExp(`\\(symbol\\s+"${sanitizedName}"`, "m");
42965
+ return pattern.test(content);
42966
+ } catch {
42967
+ return false;
42968
+ }
42833
42969
  }
42834
42970
  };
42835
42971
  }
42836
42972
  init_lib_table();
42837
42973
  init_category_router();
42838
42974
  init_global_lib_table();
42975
+ var logger7 = createLogger("http-routes");
42976
+ var __filename2 = fileURLToPath(import.meta.url);
42977
+ var __dirname2 = dirname3(__filename2);
42978
+ var htmlCache = null;
42979
+ function getHtmlPage() {
42980
+ if (htmlCache)
42981
+ return htmlCache;
42982
+ try {
42983
+ const possiblePaths = [
42984
+ join5(__dirname2, "assets/search.html"),
42985
+ join5(__dirname2, "../dist/assets/search.html"),
42986
+ join5(__dirname2, "../assets/search.html"),
42987
+ join5(__dirname2, "../assets/search-built.html"),
42988
+ join5(process.cwd(), "dist/assets/search.html"),
42989
+ join5(process.cwd(), "packages/core/dist/assets/search.html"),
42990
+ join5(__dirname2, "../../dist/assets/search.html"),
42991
+ join5(__dirname2, "../../../core/dist/assets/search.html"),
42992
+ join5(__dirname2, "../../../../packages/core/dist/assets/search.html"),
42993
+ join5(__dirname2, "../../node_modules/@jlcpcb/core/dist/assets/search.html")
42994
+ ];
42995
+ for (const path of possiblePaths) {
42996
+ try {
42997
+ htmlCache = readFileSync(path, "utf-8");
42998
+ logger7.debug(`Loaded HTML from: ${path}`);
42999
+ return htmlCache;
43000
+ } catch {}
43001
+ }
43002
+ throw new Error("HTML file not found");
43003
+ } catch (error) {
43004
+ logger7.error("Failed to load HTML page:", error);
43005
+ return `<!DOCTYPE html>
43006
+ <html>
43007
+ <head><title>Error</title></head>
43008
+ <body>
43009
+ <h1>Error: Search page not found</h1>
43010
+ <p>The search page has not been built. Run: bun run build</p>
43011
+ </body>
43012
+ </html>`;
43013
+ }
43014
+ }
43015
+ async function handleRequest(req, res) {
43016
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
43017
+ const pathname = url.pathname;
43018
+ logger7.debug(`${req.method} ${pathname}`);
43019
+ if (pathname === "/" || pathname === "/index.html") {
43020
+ const query = url.searchParams.get("q") || undefined;
43021
+ serveHtml(res, query);
43022
+ } else if (pathname === "/api/search") {
43023
+ await handleSearch(url, res);
43024
+ } else if (pathname.startsWith("/api/component/")) {
43025
+ const uuid = pathname.replace("/api/component/", "");
43026
+ await handleComponent(uuid, res);
43027
+ } else if (pathname === "/health") {
43028
+ res.writeHead(200, { "Content-Type": "application/json" });
43029
+ res.end(JSON.stringify({ status: "ok" }));
43030
+ } else {
43031
+ res.writeHead(404, { "Content-Type": "application/json" });
43032
+ res.end(JSON.stringify({ error: "Not found" }));
43033
+ }
43034
+ }
43035
+ function serveHtml(res, initialQuery) {
43036
+ let html = getHtmlPage();
43037
+ if (initialQuery) {
43038
+ const script = `<script>window.__INITIAL_QUERY__ = ${JSON.stringify(initialQuery)};</script>`;
43039
+ html = html.replace("</head>", `${script}</head>`);
43040
+ }
43041
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
43042
+ res.end(html);
43043
+ }
43044
+ async function handleSearch(url, res) {
43045
+ const query = url.searchParams.get("q") || "";
43046
+ const source = url.searchParams.get("source") || "user";
43047
+ const page = parseInt(url.searchParams.get("page") || "1", 10);
43048
+ const limit = parseInt(url.searchParams.get("limit") || "20", 10);
43049
+ if (!query) {
43050
+ res.writeHead(400, { "Content-Type": "application/json" });
43051
+ res.end(JSON.stringify({ error: "Missing query parameter" }));
43052
+ return;
43053
+ }
43054
+ try {
43055
+ const allResults = await easyedaCommunityClient.search({
43056
+ query,
43057
+ source,
43058
+ limit: Math.min(limit * page + limit, 100)
43059
+ });
43060
+ const startIndex = (page - 1) * limit;
43061
+ const endIndex = startIndex + limit;
43062
+ const results = allResults.slice(startIndex, endIndex);
43063
+ const totalPages = Math.ceil(allResults.length / limit);
43064
+ res.writeHead(200, { "Content-Type": "application/json" });
43065
+ res.end(JSON.stringify({
43066
+ results,
43067
+ pagination: {
43068
+ page,
43069
+ limit,
43070
+ total: allResults.length,
43071
+ totalPages,
43072
+ hasNext: page < totalPages,
43073
+ hasPrev: page > 1
43074
+ }
43075
+ }));
43076
+ } catch (error) {
43077
+ logger7.error("Search error:", error);
43078
+ res.writeHead(500, { "Content-Type": "application/json" });
43079
+ res.end(JSON.stringify({ error: "Search failed" }));
43080
+ }
43081
+ }
43082
+ function communityToComponentData(community) {
43083
+ const cPara = community.symbol.head?.c_para ?? {};
43084
+ const fpCPara = community.footprint.head?.c_para ?? {};
43085
+ return {
43086
+ info: {
43087
+ name: community.title || cPara.name || "Unknown",
43088
+ prefix: cPara.pre || cPara.Prefix || "U",
43089
+ package: fpCPara.package || community.footprint.name,
43090
+ manufacturer: cPara.Manufacturer || cPara.BOM_Manufacturer,
43091
+ description: community.description || cPara.BOM_Manufacturer_Part,
43092
+ category: cPara.package
43093
+ },
43094
+ symbol: community.symbol,
43095
+ footprint: community.footprint,
43096
+ model3d: community.model3d,
43097
+ rawData: community.rawData
43098
+ };
43099
+ }
43100
+ async function handleComponent(uuid, res) {
43101
+ if (!uuid) {
43102
+ res.writeHead(400, { "Content-Type": "application/json" });
43103
+ res.end(JSON.stringify({ error: "Missing UUID" }));
43104
+ return;
43105
+ }
43106
+ try {
43107
+ const component2 = await easyedaCommunityClient.getComponent(uuid);
43108
+ if (!component2) {
43109
+ res.writeHead(404, { "Content-Type": "application/json" });
43110
+ res.end(JSON.stringify({ error: "Component not found" }));
43111
+ return;
43112
+ }
43113
+ const componentData = communityToComponentData(component2);
43114
+ let symbolSexpr = "";
43115
+ let footprintSexpr = "";
43116
+ try {
43117
+ symbolSexpr = symbolConverter.convertToSymbolEntry(componentData);
43118
+ } catch (e) {
43119
+ logger7.warn("Symbol conversion failed:", e);
43120
+ }
43121
+ try {
43122
+ footprintSexpr = footprintConverter.convert(componentData);
43123
+ } catch (e) {
43124
+ logger7.warn("Footprint conversion failed:", e);
43125
+ }
43126
+ res.writeHead(200, { "Content-Type": "application/json" });
43127
+ res.end(JSON.stringify({
43128
+ uuid: component2.uuid,
43129
+ title: component2.title,
43130
+ description: component2.description,
43131
+ owner: component2.owner,
43132
+ symbolSexpr,
43133
+ footprintSexpr,
43134
+ model3d: component2.model3d
43135
+ }));
43136
+ } catch (error) {
43137
+ logger7.error("Component fetch error:", error);
43138
+ res.writeHead(500, { "Content-Type": "application/json" });
43139
+ res.end(JSON.stringify({ error: "Failed to fetch component" }));
43140
+ }
43141
+ }
43142
+ var logger8 = createLogger("http-server");
43143
+ var DEFAULT_PORT = 3847;
43144
+ var serverInstance = null;
43145
+ function startHttpServer(options = {}) {
43146
+ if (serverInstance) {
43147
+ logger8.debug("HTTP server already running");
43148
+ const port2 = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
43149
+ options.onReady?.(`http://localhost:${port2}`);
43150
+ return port2;
43151
+ }
43152
+ const port = options.port ?? parseInt(process.env.JLC_MCP_HTTP_PORT || String(DEFAULT_PORT), 10);
43153
+ serverInstance = createServer(async (req, res) => {
43154
+ res.setHeader("Access-Control-Allow-Origin", "*");
43155
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
43156
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
43157
+ if (req.method === "OPTIONS") {
43158
+ res.writeHead(204);
43159
+ res.end();
43160
+ return;
43161
+ }
43162
+ try {
43163
+ await handleRequest(req, res);
43164
+ } catch (error) {
43165
+ logger8.error("Request error:", error);
43166
+ res.writeHead(500, { "Content-Type": "application/json" });
43167
+ res.end(JSON.stringify({ error: "Internal server error" }));
43168
+ }
43169
+ });
43170
+ serverInstance.listen(port, () => {
43171
+ const url = `http://localhost:${port}`;
43172
+ logger8.info(`HTTP server listening on ${url}`);
43173
+ options.onReady?.(url);
43174
+ });
43175
+ serverInstance.on("error", (error) => {
43176
+ if (error.code === "EADDRINUSE") {
43177
+ logger8.warn(`Port ${port} already in use, HTTP server not started`);
43178
+ } else {
43179
+ logger8.error("HTTP server error:", error);
43180
+ }
43181
+ });
43182
+ return port;
43183
+ }
43184
+ function stopHttpServer() {
43185
+ if (serverInstance) {
43186
+ serverInstance.close();
43187
+ serverInstance = null;
43188
+ logger8.info("HTTP server stopped");
43189
+ }
43190
+ }
42839
43191
 
42840
43192
  // ../../node_modules/ink/build/render.js
42841
43193
  import { Stream } from "node:stream";
@@ -49075,7 +49427,7 @@ var import_react30 = __toESM(require_react(), 1);
49075
49427
  // ../../node_modules/open/index.js
49076
49428
  import process19 from "node:process";
49077
49429
  import path from "node:path";
49078
- import { fileURLToPath } from "node:url";
49430
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
49079
49431
  import childProcess3 from "node:child_process";
49080
49432
  import fs6, { constants as fsConstants2 } from "node:fs/promises";
49081
49433
 
@@ -49386,8 +49738,8 @@ var is_in_ssh_default = isInSsh;
49386
49738
 
49387
49739
  // ../../node_modules/open/index.js
49388
49740
  var fallbackAttemptSymbol = Symbol("fallbackAttempt");
49389
- var __dirname2 = import.meta.url ? path.dirname(fileURLToPath(import.meta.url)) : "";
49390
- var localXdgOpenPath = path.join(__dirname2, "xdg-open");
49741
+ var __dirname3 = import.meta.url ? path.dirname(fileURLToPath2(import.meta.url)) : "";
49742
+ var localXdgOpenPath = path.join(__dirname3, "xdg-open");
49391
49743
  var { platform: platform5, arch } = process19;
49392
49744
  var tryEachApp = async (apps, opener) => {
49393
49745
  if (apps.length === 0) {
@@ -49531,7 +49883,7 @@ var baseOpen = async (options) => {
49531
49883
  if (app) {
49532
49884
  command = app;
49533
49885
  } else {
49534
- const isBundled = !__dirname2 || __dirname2 === "/";
49886
+ const isBundled = !__dirname3 || __dirname3 === "/";
49535
49887
  let exeLocalXdgOpen = false;
49536
49888
  try {
49537
49889
  await fs6.access(localXdgOpenPath, fsConstants2.X_OK);
@@ -50923,23 +51275,416 @@ function LibrarySetupScreen() {
50923
51275
  }, undefined, true, undefined, this);
50924
51276
  }
50925
51277
 
50926
- // src/app/App.tsx
51278
+ // src/app/screens/EasyEDAInfoScreen.tsx
51279
+ var import_react34 = __toESM(require_react(), 1);
51280
+
51281
+ // src/app/components/EasyEDADetailView.tsx
50927
51282
  var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
51283
+ function truncate4(str, len) {
51284
+ if (!str)
51285
+ return "";
51286
+ return str.length > len ? str.slice(0, len - 1) + "…" : str;
51287
+ }
51288
+ function formatDate(timestamp) {
51289
+ if (!timestamp)
51290
+ return "N/A";
51291
+ const date = new Date(timestamp * 1000);
51292
+ return date.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" });
51293
+ }
51294
+ function EasyEDADetailView({ component, terminalWidth, isInstalled, statusMessage }) {
51295
+ const isWide2 = terminalWidth >= 80;
51296
+ const labelWidth = 16;
51297
+ const colWidth = isWide2 ? Math.floor((terminalWidth - 4) / 2) : terminalWidth - 2;
51298
+ const valueWidth = colWidth - labelWidth - 1;
51299
+ const description = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51300
+ flexDirection: "column",
51301
+ marginBottom: 1,
51302
+ width: terminalWidth - 2,
51303
+ children: [
51304
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51305
+ dimColor: true,
51306
+ children: "Description"
51307
+ }, undefined, false, undefined, this),
51308
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51309
+ wrap: "wrap",
51310
+ children: component.description || "No description"
51311
+ }, undefined, false, undefined, this)
51312
+ ]
51313
+ }, undefined, true, undefined, this);
51314
+ const partInfo = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51315
+ flexDirection: "column",
51316
+ width: colWidth,
51317
+ children: [
51318
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51319
+ bold: true,
51320
+ underline: true,
51321
+ color: "cyan",
51322
+ children: "Component Info"
51323
+ }, undefined, false, undefined, this),
51324
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51325
+ marginTop: 1,
51326
+ flexDirection: "column",
51327
+ children: [
51328
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51329
+ children: [
51330
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51331
+ dimColor: true,
51332
+ children: "Title".padEnd(labelWidth)
51333
+ }, undefined, false, undefined, this),
51334
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51335
+ bold: true,
51336
+ children: truncate4(component.title || "N/A", valueWidth)
51337
+ }, undefined, false, undefined, this)
51338
+ ]
51339
+ }, undefined, true, undefined, this),
51340
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51341
+ children: [
51342
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51343
+ dimColor: true,
51344
+ children: "UUID".padEnd(labelWidth)
51345
+ }, undefined, false, undefined, this),
51346
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51347
+ color: "cyan",
51348
+ children: truncate4(component.uuid, valueWidth)
51349
+ }, undefined, false, undefined, this)
51350
+ ]
51351
+ }, undefined, true, undefined, this),
51352
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51353
+ children: [
51354
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51355
+ dimColor: true,
51356
+ children: "Package".padEnd(labelWidth)
51357
+ }, undefined, false, undefined, this),
51358
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51359
+ children: truncate4(component.footprint?.name || "N/A", valueWidth)
51360
+ }, undefined, false, undefined, this)
51361
+ ]
51362
+ }, undefined, true, undefined, this),
51363
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51364
+ children: [
51365
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51366
+ dimColor: true,
51367
+ children: "Pins".padEnd(labelWidth)
51368
+ }, undefined, false, undefined, this),
51369
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51370
+ children: component.symbol?.pins?.length || 0
51371
+ }, undefined, false, undefined, this)
51372
+ ]
51373
+ }, undefined, true, undefined, this),
51374
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51375
+ children: [
51376
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51377
+ dimColor: true,
51378
+ children: "Pads".padEnd(labelWidth)
51379
+ }, undefined, false, undefined, this),
51380
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51381
+ children: component.footprint?.pads?.length || 0
51382
+ }, undefined, false, undefined, this)
51383
+ ]
51384
+ }, undefined, true, undefined, this),
51385
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51386
+ children: [
51387
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51388
+ dimColor: true,
51389
+ children: "3D Model".padEnd(labelWidth)
51390
+ }, undefined, false, undefined, this),
51391
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51392
+ color: component.model3d ? "green" : "yellow",
51393
+ children: component.model3d ? "Yes" : "No"
51394
+ }, undefined, false, undefined, this)
51395
+ ]
51396
+ }, undefined, true, undefined, this),
51397
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51398
+ children: [
51399
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51400
+ dimColor: true,
51401
+ children: "Verified".padEnd(labelWidth)
51402
+ }, undefined, false, undefined, this),
51403
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51404
+ color: component.verify ? "green" : "yellow",
51405
+ children: component.verify ? "Yes" : "No"
51406
+ }, undefined, false, undefined, this)
51407
+ ]
51408
+ }, undefined, true, undefined, this)
51409
+ ]
51410
+ }, undefined, true, undefined, this)
51411
+ ]
51412
+ }, undefined, true, undefined, this);
51413
+ const communityInfo = /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51414
+ flexDirection: "column",
51415
+ marginLeft: isWide2 ? 2 : 0,
51416
+ marginTop: isWide2 ? 0 : 1,
51417
+ width: colWidth,
51418
+ children: [
51419
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51420
+ bold: true,
51421
+ underline: true,
51422
+ color: "cyan",
51423
+ children: "Community"
51424
+ }, undefined, false, undefined, this),
51425
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51426
+ marginTop: 1,
51427
+ flexDirection: "column",
51428
+ children: [
51429
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51430
+ children: [
51431
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51432
+ dimColor: true,
51433
+ children: "Owner".padEnd(labelWidth)
51434
+ }, undefined, false, undefined, this),
51435
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51436
+ children: truncate4(component.owner?.nickname || component.owner?.username || "N/A", valueWidth)
51437
+ }, undefined, false, undefined, this)
51438
+ ]
51439
+ }, undefined, true, undefined, this),
51440
+ component.creator && component.creator.uuid !== component.owner?.uuid && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51441
+ children: [
51442
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51443
+ dimColor: true,
51444
+ children: "Creator".padEnd(labelWidth)
51445
+ }, undefined, false, undefined, this),
51446
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51447
+ children: truncate4(component.creator.nickname || component.creator.username || "N/A", valueWidth)
51448
+ }, undefined, false, undefined, this)
51449
+ ]
51450
+ }, undefined, true, undefined, this),
51451
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51452
+ children: [
51453
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51454
+ dimColor: true,
51455
+ children: "Updated".padEnd(labelWidth)
51456
+ }, undefined, false, undefined, this),
51457
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51458
+ children: formatDate(component.updateTime)
51459
+ }, undefined, false, undefined, this)
51460
+ ]
51461
+ }, undefined, true, undefined, this),
51462
+ component.tags && component.tags.length > 0 && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51463
+ children: [
51464
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51465
+ dimColor: true,
51466
+ children: "Tags".padEnd(labelWidth)
51467
+ }, undefined, false, undefined, this),
51468
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51469
+ children: truncate4(component.tags.slice(0, 3).join(", "), valueWidth)
51470
+ }, undefined, false, undefined, this)
51471
+ ]
51472
+ }, undefined, true, undefined, this)
51473
+ ]
51474
+ }, undefined, true, undefined, this)
51475
+ ]
51476
+ }, undefined, true, undefined, this);
51477
+ const footerText = isInstalled ? "R Regenerate • Esc Back" : "Enter Install • Esc Back";
51478
+ return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51479
+ flexDirection: "column",
51480
+ width: "100%",
51481
+ children: [
51482
+ description,
51483
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51484
+ flexDirection: isWide2 ? "row" : "column",
51485
+ width: "100%",
51486
+ children: [
51487
+ partInfo,
51488
+ communityInfo
51489
+ ]
51490
+ }, undefined, true, undefined, this),
51491
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51492
+ marginTop: 1,
51493
+ flexDirection: "column",
51494
+ width: "100%",
51495
+ children: [
51496
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Divider, {
51497
+ width: terminalWidth
51498
+ }, undefined, false, undefined, this),
51499
+ statusMessage && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51500
+ paddingY: 0,
51501
+ children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51502
+ color: statusMessage.startsWith("✓") ? "green" : statusMessage.startsWith("✗") ? "red" : "yellow",
51503
+ children: statusMessage
51504
+ }, undefined, false, undefined, this)
51505
+ }, undefined, false, undefined, this),
51506
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51507
+ paddingY: 0,
51508
+ children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
51509
+ dimColor: true,
51510
+ children: footerText
51511
+ }, undefined, false, undefined, this)
51512
+ }, undefined, false, undefined, this),
51513
+ /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Divider, {
51514
+ width: terminalWidth
51515
+ }, undefined, false, undefined, this)
51516
+ ]
51517
+ }, undefined, true, undefined, this)
51518
+ ]
51519
+ }, undefined, true, undefined, this);
51520
+ }
51521
+
51522
+ // src/app/screens/EasyEDAInfoScreen.tsx
51523
+ var jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
51524
+ var componentService4 = createComponentService();
51525
+ var libraryService5 = createLibraryService();
51526
+ function EasyEDAInfoScreen() {
51527
+ const { replace } = useNavigation();
51528
+ const { params } = useCurrentScreen();
51529
+ const { columns: terminalWidth } = useTerminalSize();
51530
+ const [component, setComponent] = import_react34.useState(null);
51531
+ const [isLoading, setIsLoading] = import_react34.useState(true);
51532
+ const [error, setError] = import_react34.useState(null);
51533
+ const [isInstalling, setIsInstalling] = import_react34.useState(false);
51534
+ const [statusMessage, setStatusMessage] = import_react34.useState(null);
51535
+ const [isInstalled, setIsInstalled] = import_react34.useState(false);
51536
+ const installingRef = import_react34.useRef(false);
51537
+ import_react34.useEffect(() => {
51538
+ const init = async () => {
51539
+ if (!params.uuid) {
51540
+ setError("No UUID provided");
51541
+ setIsLoading(false);
51542
+ return;
51543
+ }
51544
+ try {
51545
+ await libraryService5.ensureGlobalTables();
51546
+ const fetched = await componentService4.fetchCommunity(params.uuid);
51547
+ if (fetched) {
51548
+ setComponent(fetched);
51549
+ const installed = await libraryService5.isEasyEDAInstalled(fetched.title);
51550
+ setIsInstalled(installed);
51551
+ } else {
51552
+ setError("Component not found");
51553
+ }
51554
+ } catch (err) {
51555
+ setError(err instanceof Error ? err.message : "Failed to fetch component");
51556
+ } finally {
51557
+ setIsLoading(false);
51558
+ }
51559
+ };
51560
+ init();
51561
+ }, [params.uuid]);
51562
+ use_input_default((input, key) => {
51563
+ if (isLoading || !component || isInstalling)
51564
+ return;
51565
+ const lowerInput = input.toLowerCase();
51566
+ if (lowerInput === "r") {
51567
+ if (installingRef.current)
51568
+ return;
51569
+ installingRef.current = true;
51570
+ setIsInstalling(true);
51571
+ setStatusMessage("Regenerating symbol and footprint...");
51572
+ libraryService5.install(params.uuid, { force: true }).then((result) => {
51573
+ setStatusMessage(`✓ Reinstalled: ${result.symbolRef}`);
51574
+ setIsInstalled(true);
51575
+ setTimeout(() => setStatusMessage(null), 3000);
51576
+ }).catch((err) => {
51577
+ setStatusMessage(`✗ Failed: ${err instanceof Error ? err.message : "Unknown error"}`);
51578
+ setTimeout(() => setStatusMessage(null), 3000);
51579
+ }).finally(() => {
51580
+ setIsInstalling(false);
51581
+ installingRef.current = false;
51582
+ });
51583
+ return;
51584
+ }
51585
+ if (key.return) {
51586
+ if (installingRef.current)
51587
+ return;
51588
+ installingRef.current = true;
51589
+ setIsInstalling(true);
51590
+ setStatusMessage("Installing component...");
51591
+ libraryService5.ensureGlobalTables().then(() => libraryService5.install(params.uuid, {})).then((result) => {
51592
+ if (result.symbolAction === "exists") {
51593
+ setStatusMessage(`⚡ Already installed (use R to reinstall)`);
51594
+ } else {
51595
+ setStatusMessage(`✓ Installed: ${result.symbolRef}`);
51596
+ setIsInstalled(true);
51597
+ }
51598
+ setTimeout(() => setStatusMessage(null), 3000);
51599
+ }).catch((err) => {
51600
+ setStatusMessage(`✗ Failed: ${err instanceof Error ? err.message : "Unknown error"}`);
51601
+ setTimeout(() => setStatusMessage(null), 3000);
51602
+ }).finally(() => {
51603
+ setIsInstalling(false);
51604
+ installingRef.current = false;
51605
+ });
51606
+ }
51607
+ });
51608
+ if (isLoading) {
51609
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
51610
+ flexDirection: "column",
51611
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
51612
+ color: "yellow",
51613
+ children: [
51614
+ "⏳ Loading component ",
51615
+ params.uuid,
51616
+ "..."
51617
+ ]
51618
+ }, undefined, true, undefined, this)
51619
+ }, undefined, false, undefined, this);
51620
+ }
51621
+ if (error || !component) {
51622
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
51623
+ flexDirection: "column",
51624
+ children: [
51625
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
51626
+ color: "red",
51627
+ children: [
51628
+ "✗ ",
51629
+ error || "Component not found"
51630
+ ]
51631
+ }, undefined, true, undefined, this),
51632
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
51633
+ dimColor: true,
51634
+ children: "Press Esc to go back"
51635
+ }, undefined, false, undefined, this)
51636
+ ]
51637
+ }, undefined, true, undefined, this);
51638
+ }
51639
+ return /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
51640
+ flexDirection: "column",
51641
+ children: [
51642
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Box_default, {
51643
+ marginBottom: 1,
51644
+ children: /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
51645
+ bold: true,
51646
+ children: [
51647
+ "Component: ",
51648
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
51649
+ color: "cyan",
51650
+ children: component.title
51651
+ }, undefined, false, undefined, this),
51652
+ isInstalled && /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(Text, {
51653
+ color: "green",
51654
+ children: " ✓ Installed"
51655
+ }, undefined, false, undefined, this)
51656
+ ]
51657
+ }, undefined, true, undefined, this)
51658
+ }, undefined, false, undefined, this),
51659
+ /* @__PURE__ */ jsx_dev_runtime14.jsxDEV(EasyEDADetailView, {
51660
+ component,
51661
+ terminalWidth,
51662
+ isInstalled,
51663
+ statusMessage
51664
+ }, undefined, false, undefined, this)
51665
+ ]
51666
+ }, undefined, true, undefined, this);
51667
+ }
51668
+
51669
+ // src/app/App.tsx
51670
+ var jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
50928
51671
  function ScreenRouter() {
50929
51672
  const { screen } = useCurrentScreen();
50930
51673
  switch (screen) {
50931
51674
  case "search":
50932
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(SearchScreen, {}, undefined, false, undefined, this);
51675
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(SearchScreen, {}, undefined, false, undefined, this);
50933
51676
  case "info":
50934
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(InfoScreen, {}, undefined, false, undefined, this);
51677
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(InfoScreen, {}, undefined, false, undefined, this);
50935
51678
  case "install":
50936
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(InstallScreen, {}, undefined, false, undefined, this);
51679
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(InstallScreen, {}, undefined, false, undefined, this);
50937
51680
  case "installed":
50938
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(InstalledScreen, {}, undefined, false, undefined, this);
51681
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(InstalledScreen, {}, undefined, false, undefined, this);
50939
51682
  case "library":
50940
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(LibraryScreen, {}, undefined, false, undefined, this);
51683
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(LibraryScreen, {}, undefined, false, undefined, this);
50941
51684
  case "library-setup":
50942
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(LibrarySetupScreen, {}, undefined, false, undefined, this);
51685
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(LibrarySetupScreen, {}, undefined, false, undefined, this);
51686
+ case "easyeda-info":
51687
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(EasyEDAInfoScreen, {}, undefined, false, undefined, this);
50943
51688
  default:
50944
51689
  return null;
50945
51690
  }
@@ -50961,34 +51706,34 @@ function AppContent() {
50961
51706
  exit();
50962
51707
  }
50963
51708
  });
50964
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
51709
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
50965
51710
  flexDirection: "column",
50966
51711
  padding: 1,
50967
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(ScreenRouter, {}, undefined, false, undefined, this)
51712
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(ScreenRouter, {}, undefined, false, undefined, this)
50968
51713
  }, undefined, false, undefined, this);
50969
51714
  }
50970
51715
  function App2({ initialScreen, initialParams }) {
50971
- return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(NavigationProvider, {
51716
+ return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(NavigationProvider, {
50972
51717
  initialScreen,
50973
51718
  initialParams,
50974
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(AppStateProvider, {
50975
- children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(AppContent, {}, undefined, false, undefined, this)
51719
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(AppStateProvider, {
51720
+ children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(AppContent, {}, undefined, false, undefined, this)
50976
51721
  }, undefined, false, undefined, this)
50977
51722
  }, undefined, false, undefined, this);
50978
51723
  }
50979
51724
  function renderApp(initialScreen, initialParams) {
50980
- render_default(/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(App2, {
51725
+ render_default(/* @__PURE__ */ jsx_dev_runtime15.jsxDEV(App2, {
50981
51726
  initialScreen,
50982
51727
  initialParams
50983
51728
  }, undefined, false, undefined, this));
50984
51729
  }
50985
51730
 
50986
51731
  // src/commands/search.ts
50987
- var componentService4 = createComponentService();
51732
+ var componentService5 = createComponentService();
50988
51733
  async function searchCommand(query, options) {
50989
51734
  console.log(`Searching for "${query}"...`);
50990
51735
  try {
50991
- let results = await componentService4.search(query, options);
51736
+ let results = await componentService5.search(query, options);
50992
51737
  if (!options.basicOnly) {
50993
51738
  results = results.sort((a, b) => {
50994
51739
  if (a.libraryType === "basic" && b.libraryType !== "basic")
@@ -51099,7 +51844,7 @@ var r = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23
51099
51844
  Object.keys(r.modifier);
51100
51845
  var tD = Object.keys(r.color);
51101
51846
  var eD = Object.keys(r.bgColor);
51102
- [...tD];
51847
+ [...tD, ...eD];
51103
51848
  function sD() {
51104
51849
  const t = new Map;
51105
51850
  for (const [u, F] of Object.entries(r)) {
@@ -51519,13 +52264,13 @@ var L2 = () => {
51519
52264
  };
51520
52265
 
51521
52266
  // src/commands/info.ts
51522
- var componentService5 = createComponentService();
52267
+ var componentService6 = createComponentService();
51523
52268
  async function infoCommand(id, options) {
51524
52269
  if (options.json) {
51525
52270
  const spinner = L2();
51526
52271
  spinner.start(`Fetching component ${id}...`);
51527
52272
  try {
51528
- const details = await componentService5.getDetails(id);
52273
+ const details = await componentService6.getDetails(id);
51529
52274
  spinner.stop("Component found");
51530
52275
  console.log(JSON.stringify(details, null, 2));
51531
52276
  } catch (error) {
@@ -51539,15 +52284,23 @@ async function infoCommand(id, options) {
51539
52284
  }
51540
52285
 
51541
52286
  // src/commands/install.ts
51542
- var componentService6 = createComponentService();
51543
- var libraryService5 = createLibraryService();
52287
+ var componentService7 = createComponentService();
52288
+ var libraryService6 = createLibraryService();
52289
+ function isLcscId3(id) {
52290
+ return /^C\d+$/i.test(id);
52291
+ }
51544
52292
  async function installCommand(id, options) {
52293
+ if (id && !isLcscId3(id)) {
52294
+ v2.error(`"${id}" is not an LCSC part number (e.g., C2040).`);
52295
+ v2.info(`For EasyEDA community components, use: ${source_default.cyan(`jlc easyeda install ${id}`)}`);
52296
+ process.exit(1);
52297
+ }
51545
52298
  if (id && options.force) {
51546
52299
  const spinner2 = L2();
51547
52300
  spinner2.start(`Installing component ${id}...`);
51548
52301
  try {
51549
- await libraryService5.ensureGlobalTables();
51550
- const result = await libraryService5.install(id, {
52302
+ await libraryService6.ensureGlobalTables();
52303
+ const result = await libraryService6.install(id, {
51551
52304
  projectPath: options.projectPath,
51552
52305
  include3d: options.include3d,
51553
52306
  force: true
@@ -51573,7 +52326,7 @@ async function installCommand(id, options) {
51573
52326
  const spinner2 = L2();
51574
52327
  spinner2.start(`Fetching component ${id}...`);
51575
52328
  try {
51576
- const details = await componentService6.getDetails(id);
52329
+ const details = await componentService7.getDetails(id);
51577
52330
  spinner2.stop("Component found");
51578
52331
  renderApp("info", { componentId: id, component: details });
51579
52332
  } catch (error) {
@@ -51599,7 +52352,7 @@ async function installCommand(id, options) {
51599
52352
  const spinner = L2();
51600
52353
  spinner.start(`Searching for "${query}"...`);
51601
52354
  const searchOptions = { limit: 20 };
51602
- let results = await componentService6.search(query, searchOptions);
52355
+ let results = await componentService7.search(query, searchOptions);
51603
52356
  results = results.sort((a2, b2) => {
51604
52357
  if (a2.libraryType === "basic" && b2.libraryType !== "basic")
51605
52358
  return -1;
@@ -51616,15 +52369,15 @@ async function installCommand(id, options) {
51616
52369
  }
51617
52370
 
51618
52371
  // src/commands/library.ts
51619
- var libraryService6 = createLibraryService();
52372
+ var libraryService7 = createLibraryService();
51620
52373
  async function libraryCommand(options) {
51621
52374
  if (options.json) {
51622
52375
  const spinner = L2();
51623
52376
  spinner.start("Loading library status...");
51624
52377
  try {
51625
52378
  const [status, components] = await Promise.all([
51626
- libraryService6.getStatus(),
51627
- libraryService6.listInstalled({})
52379
+ libraryService7.getStatus(),
52380
+ libraryService7.listInstalled({})
51628
52381
  ]);
51629
52382
  spinner.stop(`Found ${components.length} installed components`);
51630
52383
  console.log(JSON.stringify({
@@ -51647,6 +52400,91 @@ async function libraryCommand(options) {
51647
52400
  renderApp("library", {});
51648
52401
  }
51649
52402
 
52403
+ // src/commands/easyeda.ts
52404
+ var componentService8 = createComponentService();
52405
+ var libraryService8 = createLibraryService();
52406
+ async function easyedaSearchCommand(query, options) {
52407
+ const port = options.port ?? 3847;
52408
+ console.log("Starting component browser...");
52409
+ startHttpServer({
52410
+ port,
52411
+ onReady: async (url) => {
52412
+ const searchUrl = query ? `${url}?q=${encodeURIComponent(query)}` : url;
52413
+ console.log(`Browser opened at ${searchUrl}`);
52414
+ await open_default(searchUrl);
52415
+ console.log("Press Ctrl+C to stop the server and exit");
52416
+ }
52417
+ });
52418
+ await new Promise((resolve) => {
52419
+ process.on("SIGINT", () => {
52420
+ console.log(`
52421
+ Shutting down server...`);
52422
+ stopHttpServer();
52423
+ resolve();
52424
+ process.exit(0);
52425
+ });
52426
+ });
52427
+ }
52428
+ async function easyedaInstallCommand(uuid, options) {
52429
+ if (uuid && options.force) {
52430
+ const spinner2 = L2();
52431
+ spinner2.start(`Installing EasyEDA component ${uuid}...`);
52432
+ try {
52433
+ await libraryService8.ensureGlobalTables();
52434
+ const result = await libraryService8.install(uuid, {
52435
+ projectPath: options.projectPath,
52436
+ include3d: options.include3d,
52437
+ force: true
52438
+ });
52439
+ spinner2.stop(source_default.green("✓ Component installed"));
52440
+ console.log();
52441
+ console.log(source_default.cyan("Symbol: "), result.symbolRef);
52442
+ console.log(source_default.cyan("Footprint: "), result.footprintRef);
52443
+ console.log(source_default.cyan("Action: "), result.symbolAction);
52444
+ if (result.files.model3d) {
52445
+ console.log(source_default.cyan("3D Model: "), result.files.model3d);
52446
+ }
52447
+ console.log();
52448
+ console.log(source_default.dim(`Library: ${result.files.symbolLibrary}`));
52449
+ } catch (error) {
52450
+ spinner2.stop(source_default.red("✗ Installation failed"));
52451
+ v2.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
52452
+ process.exit(1);
52453
+ }
52454
+ return;
52455
+ }
52456
+ if (uuid) {
52457
+ renderApp("easyeda-info", { uuid });
52458
+ return;
52459
+ }
52460
+ const query = await ue({
52461
+ message: "Search EasyEDA community library:",
52462
+ placeholder: "e.g., STM32F103, ESP32, Arduino Nano",
52463
+ validate: (value) => {
52464
+ if (!value)
52465
+ return "Please enter a search term";
52466
+ return;
52467
+ }
52468
+ });
52469
+ if (BD(query)) {
52470
+ ve("Installation cancelled");
52471
+ process.exit(0);
52472
+ }
52473
+ const spinner = L2();
52474
+ spinner.start(`Searching EasyEDA community for "${query}"...`);
52475
+ const searchOptions = {
52476
+ limit: 20,
52477
+ source: "easyeda-community"
52478
+ };
52479
+ const results = await componentService8.search(query, searchOptions);
52480
+ spinner.stop(`Found ${results.length} results`);
52481
+ if (results.length === 0) {
52482
+ v2.warn("No components found. Try a different search term.");
52483
+ return;
52484
+ }
52485
+ renderApp("search", { query, results });
52486
+ }
52487
+
51650
52488
  // src/index.ts
51651
52489
  var program2 = new Command;
51652
52490
  program2.name("jlc").description("JLC/EasyEDA component sourcing and KiCad library management").version("0.1.0");
@@ -51674,4 +52512,18 @@ program2.command("library").description("View JLC-MCP library status and install
51674
52512
  json: options.json
51675
52513
  });
51676
52514
  });
52515
+ var easyeda = program2.command("easyeda").description("EasyEDA community component browser");
52516
+ easyeda.command("search <query...>").description("Open browser-based component search").option("-p, --port <number>", "HTTP server port", "3847").action(async (queryParts, options) => {
52517
+ const query = queryParts.join(" ");
52518
+ await easyedaSearchCommand(query, {
52519
+ port: options.port ? parseInt(options.port, 10) : undefined
52520
+ });
52521
+ });
52522
+ easyeda.command("install [uuid]").description("Install EasyEDA community component to KiCad libraries").option("-p, --project <path>", "Install to project-local library").option("--with-3d", "Include 3D model").option("-f, --force", "Force reinstall (regenerate symbol and footprint)").action(async (uuid, options) => {
52523
+ await easyedaInstallCommand(uuid, {
52524
+ projectPath: options.project,
52525
+ include3d: options.with3d,
52526
+ force: options.force
52527
+ });
52528
+ });
51677
52529
  program2.parse();