@jlcpcb/mcp 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @jlcpcb/mcp
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d5a74c2: Fix EasyEDA 3D model transform import so KiCad footprints preserve model offsets and rotations.
8
+
9
+ Improve CLI table formatting so search and library rows keep aligned columns, including with wide characters.
10
+
11
+ Add workspace lint scripts and ESLint configuration so `bun run lint` works across packages.
12
+
13
+ - Updated dependencies [d5a74c2]
14
+ - @jlcpcb/core@0.3.1
15
+
3
16
  ## 0.3.0
4
17
 
5
18
  ### Minor Changes
package/dist/index.js CHANGED
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
19
37
  var __export = (target, all) => {
20
38
  for (var name in all)
21
39
  __defProp(target, name, {
22
40
  get: all[name],
23
41
  enumerable: true,
24
42
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
26
44
  });
27
45
  };
28
46
 
@@ -6276,7 +6294,7 @@ var require_formats = __commonJS((exports) => {
6276
6294
  }
6277
6295
  var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
6278
6296
  function getTime(strictTimeZone) {
6279
- return function time(str) {
6297
+ return function time3(str) {
6280
6298
  const matches = TIME.exec(str);
6281
6299
  if (!matches)
6282
6300
  return false;
@@ -21912,13 +21930,17 @@ import { readFileSync } from "fs";
21912
21930
  import { join as join6, dirname as dirname3 } from "path";
21913
21931
  import { fileURLToPath } from "url";
21914
21932
  var __defProp2 = Object.defineProperty;
21933
+ var __returnValue2 = (v) => v;
21934
+ function __exportSetter2(name, newValue) {
21935
+ this[name] = __returnValue2.bind(null, newValue);
21936
+ }
21915
21937
  var __export2 = (target, all) => {
21916
21938
  for (var name in all)
21917
21939
  __defProp2(target, name, {
21918
21940
  get: all[name],
21919
21941
  enumerable: true,
21920
21942
  configurable: true,
21921
- set: (newValue) => all[name] = () => newValue
21943
+ set: __exportSetter2.bind(all, name)
21922
21944
  });
21923
21945
  };
21924
21946
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -27150,6 +27172,34 @@ function safeParseInt(value, defaultValue = 0) {
27150
27172
  const parsed = parseInt(value, 10);
27151
27173
  return isNaN(parsed) ? defaultValue : parsed;
27152
27174
  }
27175
+ function parse3DModelNode(data) {
27176
+ try {
27177
+ const jsonStr = data.slice("SVGNODE~".length);
27178
+ const svgData = JSON.parse(jsonStr);
27179
+ const attrs = svgData?.attrs;
27180
+ if (!attrs?.uuid) {
27181
+ return;
27182
+ }
27183
+ const [translationX, translationY] = String(attrs.c_origin ?? "0,0").split(",").map((value) => safeParseFloat(value));
27184
+ const [rotationX, rotationY, rotationZ] = String(attrs.c_rotation ?? "0,0,0").split(",").map((value) => safeParseFloat(value));
27185
+ return {
27186
+ name: attrs.title || "3D Model",
27187
+ uuid: attrs.uuid,
27188
+ translation: {
27189
+ x: translationX,
27190
+ y: translationY,
27191
+ z: safeParseFloat(String(attrs.z ?? "0"))
27192
+ },
27193
+ rotation: {
27194
+ x: rotationX,
27195
+ y: rotationY,
27196
+ z: rotationZ
27197
+ }
27198
+ };
27199
+ } catch {
27200
+ return;
27201
+ }
27202
+ }
27153
27203
  function parseSymbolPin(pinData) {
27154
27204
  try {
27155
27205
  const segments = pinData.split("^^");
@@ -27629,16 +27679,10 @@ function parseFootprintShapes(shapes) {
27629
27679
  break;
27630
27680
  }
27631
27681
  case "SVGNODE": {
27632
- try {
27633
- const jsonStr = line.split("~")[1];
27634
- const svgData = JSON.parse(jsonStr);
27635
- if (svgData?.attrs?.uuid) {
27636
- model3d = {
27637
- name: svgData.attrs.title || "3D Model",
27638
- uuid: svgData.attrs.uuid
27639
- };
27640
- }
27641
- } catch {}
27682
+ const parsedModel3d = parse3DModelNode(line);
27683
+ if (parsedModel3d) {
27684
+ model3d = parsedModel3d;
27685
+ }
27642
27686
  break;
27643
27687
  }
27644
27688
  case "SOLIDREGION": {
@@ -29746,6 +29790,9 @@ var KI_PAD_SHAPE = {
29746
29790
  OVAL: "oval",
29747
29791
  POLYGON: "custom"
29748
29792
  };
29793
+ function normalize3DModelRotation(rotation) {
29794
+ return ((360 - rotation) % 360 + 360) % 360;
29795
+ }
29749
29796
  function calculatePadCenter(pads) {
29750
29797
  if (pads.length === 0) {
29751
29798
  return { x: 0, y: 0 };
@@ -29874,7 +29921,7 @@ class FootprintConverter {
29874
29921
  output += ` (embedded_fonts no)
29875
29922
  `;
29876
29923
  if (include3DModel && model3d && options.modelPath) {
29877
- output += this.generate3DModel(options.modelPath);
29924
+ output += this.generate3DModel(options.modelPath, model3d, origin);
29878
29925
  }
29879
29926
  output += `)`;
29880
29927
  return output;
@@ -30463,16 +30510,22 @@ ${justify ? ` (justify ${justify})
30463
30510
  }
30464
30511
  return output;
30465
30512
  }
30466
- generate3DModel(modelPath) {
30513
+ generate3DModel(modelPath, model3d, origin) {
30514
+ const offsetX = roundTo(toMM(model3d.translation.x - origin.x), 3);
30515
+ const offsetY = roundTo(-toMM(model3d.translation.y - origin.y), 3);
30516
+ const offsetZ = roundTo(-toMM(model3d.translation.z), 3);
30517
+ const rotateX = normalize3DModelRotation(model3d.rotation.x);
30518
+ const rotateY = normalize3DModelRotation(model3d.rotation.y);
30519
+ const rotateZ = normalize3DModelRotation(model3d.rotation.z);
30467
30520
  return ` (model "${modelPath}"
30468
30521
  (offset
30469
- (xyz 0 0 0)
30522
+ (xyz ${offsetX} ${offsetY} ${offsetZ})
30470
30523
  )
30471
30524
  (scale
30472
30525
  (xyz 1 1 1)
30473
30526
  )
30474
30527
  (rotate
30475
- (xyz 0 0 0)
30528
+ (xyz ${rotateX} ${rotateY} ${rotateZ})
30476
30529
  )
30477
30530
  )
30478
30531
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jlcpcb/mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "MCP server for JLC/EasyEDA component sourcing, library fetching, and conversion to KiCad format",
@@ -21,6 +21,7 @@
21
21
  "build": "bun build ./src/index.ts --outdir ./dist --target node",
22
22
  "start": "bun run ./src/index.ts",
23
23
  "dev": "bun --watch ./src/index.ts",
24
+ "lint": "eslint src --ext .ts",
24
25
  "typecheck": "tsc --noEmit",
25
26
  "test": "bun test",
26
27
  "clean": "rm -rf dist"
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+
3
+ import { toolHandlers, tools } from './index.js';
4
+
5
+ describe('tools', () => {
6
+ it('exposes a handler for every registered tool', () => {
7
+ expect(tools.length).toBeGreaterThan(0);
8
+
9
+ for (const tool of tools) {
10
+ expect(toolHandlers[tool.name]).toBeDefined();
11
+ }
12
+ });
13
+ });