@tobisk/pcbs 1.0.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 (168) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +186 -0
  3. package/dist/src/cli/cli.d.ts +6 -0
  4. package/dist/src/cli/cli.js +87 -0
  5. package/dist/src/cli/cli.js.map +1 -0
  6. package/dist/src/cli/codegen.d.ts +25 -0
  7. package/dist/src/cli/codegen.js +170 -0
  8. package/dist/src/cli/codegen.js.map +1 -0
  9. package/dist/src/cli/commands/export.d.ts +13 -0
  10. package/dist/src/cli/commands/export.js +301 -0
  11. package/dist/src/cli/commands/export.js.map +1 -0
  12. package/dist/src/cli/commands/lib.d.ts +10 -0
  13. package/dist/src/cli/commands/lib.js +158 -0
  14. package/dist/src/cli/commands/lib.js.map +1 -0
  15. package/dist/src/cli/commands/parts.d.ts +4 -0
  16. package/dist/src/cli/commands/parts.js +104 -0
  17. package/dist/src/cli/commands/parts.js.map +1 -0
  18. package/dist/src/cli/commands/setup.d.ts +4 -0
  19. package/dist/src/cli/commands/setup.js +86 -0
  20. package/dist/src/cli/commands/setup.js.map +1 -0
  21. package/dist/src/cli/commands/synth.d.ts +4 -0
  22. package/dist/src/cli/commands/synth.js +75 -0
  23. package/dist/src/cli/commands/synth.js.map +1 -0
  24. package/dist/src/cli/commands/types.d.ts +1 -0
  25. package/dist/src/cli/commands/types.js +140 -0
  26. package/dist/src/cli/commands/types.js.map +1 -0
  27. package/dist/src/cli/commands/validate.d.ts +5 -0
  28. package/dist/src/cli/commands/validate.js +105 -0
  29. package/dist/src/cli/commands/validate.js.map +1 -0
  30. package/dist/src/cli/config.d.ts +9 -0
  31. package/dist/src/cli/config.js +80 -0
  32. package/dist/src/cli/config.js.map +1 -0
  33. package/dist/src/cli/env.d.ts +4 -0
  34. package/dist/src/cli/env.js +166 -0
  35. package/dist/src/cli/env.js.map +1 -0
  36. package/dist/src/cli/search-jlc.d.ts +0 -0
  37. package/dist/src/cli/search-jlc.js +23 -0
  38. package/dist/src/cli/search-jlc.js.map +1 -0
  39. package/dist/src/cli/synthesis.d.ts +11 -0
  40. package/dist/src/cli/synthesis.js +120 -0
  41. package/dist/src/cli/synthesis.js.map +1 -0
  42. package/dist/src/cli/utils/bom.d.ts +19 -0
  43. package/dist/src/cli/utils/bom.js +130 -0
  44. package/dist/src/cli/utils/bom.js.map +1 -0
  45. package/dist/src/cli/utils/cpl.d.ts +18 -0
  46. package/dist/src/cli/utils/cpl.js +101 -0
  47. package/dist/src/cli/utils/cpl.js.map +1 -0
  48. package/dist/src/cli/utils.d.ts +11 -0
  49. package/dist/src/cli/utils.js +136 -0
  50. package/dist/src/cli/utils.js.map +1 -0
  51. package/dist/src/generate-kicad-types.d.ts +1 -0
  52. package/dist/src/generate-kicad-types.js +137 -0
  53. package/dist/src/generate-kicad-types.js.map +1 -0
  54. package/dist/src/synth/3d/Kicad3DModel.d.ts +81 -0
  55. package/dist/src/synth/3d/Kicad3DModel.js +250 -0
  56. package/dist/src/synth/3d/Kicad3DModel.js.map +1 -0
  57. package/dist/src/synth/3d/booleans.d.ts +10 -0
  58. package/dist/src/synth/3d/booleans.js +30 -0
  59. package/dist/src/synth/3d/booleans.js.map +1 -0
  60. package/dist/src/synth/3d/fillet.d.ts +14 -0
  61. package/dist/src/synth/3d/fillet.js +47 -0
  62. package/dist/src/synth/3d/fillet.js.map +1 -0
  63. package/dist/src/synth/3d/index.d.ts +7 -0
  64. package/dist/src/synth/3d/index.js +14 -0
  65. package/dist/src/synth/3d/index.js.map +1 -0
  66. package/dist/src/synth/3d/occ-loader.d.ts +5 -0
  67. package/dist/src/synth/3d/occ-loader.js +77 -0
  68. package/dist/src/synth/3d/occ-loader.js.map +1 -0
  69. package/dist/src/synth/3d/occ.d.ts +3 -0
  70. package/dist/src/synth/3d/occ.js +31 -0
  71. package/dist/src/synth/3d/occ.js.map +1 -0
  72. package/dist/src/synth/3d/primitives.d.ts +38 -0
  73. package/dist/src/synth/3d/primitives.js +58 -0
  74. package/dist/src/synth/3d/primitives.js.map +1 -0
  75. package/dist/src/synth/3d/stepWriter.d.ts +7 -0
  76. package/dist/src/synth/3d/stepWriter.js +72 -0
  77. package/dist/src/synth/3d/stepWriter.js.map +1 -0
  78. package/dist/src/synth/3d/transforms.d.ts +18 -0
  79. package/dist/src/synth/3d/transforms.js +74 -0
  80. package/dist/src/synth/3d/transforms.js.map +1 -0
  81. package/dist/src/synth/3d/types.d.ts +54 -0
  82. package/dist/src/synth/3d/types.js +21 -0
  83. package/dist/src/synth/3d/types.js.map +1 -0
  84. package/dist/src/synth/3d/vrmlWriter.d.ts +5 -0
  85. package/dist/src/synth/3d/vrmlWriter.js +171 -0
  86. package/dist/src/synth/3d/vrmlWriter.js.map +1 -0
  87. package/dist/src/synth/Component.d.ts +67 -0
  88. package/dist/src/synth/Component.js +185 -0
  89. package/dist/src/synth/Component.js.map +1 -0
  90. package/dist/src/synth/Composable.d.ts +73 -0
  91. package/dist/src/synth/Composable.js +128 -0
  92. package/dist/src/synth/Composable.js.map +1 -0
  93. package/dist/src/synth/KicadFootprint.d.ts +97 -0
  94. package/dist/src/synth/KicadFootprint.js +312 -0
  95. package/dist/src/synth/KicadFootprint.js.map +1 -0
  96. package/dist/src/synth/KicadLibrary.d.ts +66 -0
  97. package/dist/src/synth/KicadLibrary.js +162 -0
  98. package/dist/src/synth/KicadLibrary.js.map +1 -0
  99. package/dist/src/synth/KicadSymbol.d.ts +71 -0
  100. package/dist/src/synth/KicadSymbol.js +190 -0
  101. package/dist/src/synth/KicadSymbol.js.map +1 -0
  102. package/dist/src/synth/Layout.d.ts +37 -0
  103. package/dist/src/synth/Layout.js +58 -0
  104. package/dist/src/synth/Layout.js.map +1 -0
  105. package/dist/src/synth/Markers.d.ts +35 -0
  106. package/dist/src/synth/Markers.js +48 -0
  107. package/dist/src/synth/Markers.js.map +1 -0
  108. package/dist/src/synth/Module.d.ts +56 -0
  109. package/dist/src/synth/Module.js +65 -0
  110. package/dist/src/synth/Module.js.map +1 -0
  111. package/dist/src/synth/Net.d.ts +23 -0
  112. package/dist/src/synth/Net.js +95 -0
  113. package/dist/src/synth/Net.js.map +1 -0
  114. package/dist/src/synth/Registry.d.ts +36 -0
  115. package/dist/src/synth/Registry.js +75 -0
  116. package/dist/src/synth/Registry.js.map +1 -0
  117. package/dist/src/synth/Schematic.d.ts +36 -0
  118. package/dist/src/synth/Schematic.js +51 -0
  119. package/dist/src/synth/Schematic.js.map +1 -0
  120. package/dist/src/synth/index.d.ts +21 -0
  121. package/dist/src/synth/index.js +46 -0
  122. package/dist/src/synth/index.js.map +1 -0
  123. package/dist/src/synth/kicad-types-placeholder.d.ts +6 -0
  124. package/dist/src/synth/kicad-types-placeholder.js +7 -0
  125. package/dist/src/synth/kicad-types-placeholder.js.map +1 -0
  126. package/dist/src/synth/types.d.ts +111 -0
  127. package/dist/src/synth/types.js +23 -0
  128. package/dist/src/synth/types.js.map +1 -0
  129. package/dist/src/tests/3d-model.test.d.ts +1 -0
  130. package/dist/src/tests/3d-model.test.js +282 -0
  131. package/dist/src/tests/3d-model.test.js.map +1 -0
  132. package/dist/src/tests/codegen.test.d.ts +1 -0
  133. package/dist/src/tests/codegen.test.js +80 -0
  134. package/dist/src/tests/codegen.test.js.map +1 -0
  135. package/dist/src/tests/dmx_node.test.d.ts +1 -0
  136. package/dist/src/tests/dmx_node.test.js +42 -0
  137. package/dist/src/tests/dmx_node.test.js.map +1 -0
  138. package/dist/src/tests/dnc.test.d.ts +1 -0
  139. package/dist/src/tests/dnc.test.js +100 -0
  140. package/dist/src/tests/dnc.test.js.map +1 -0
  141. package/dist/src/tests/kicad-lib.test.d.ts +1 -0
  142. package/dist/src/tests/kicad-lib.test.js +465 -0
  143. package/dist/src/tests/kicad-lib.test.js.map +1 -0
  144. package/dist/src/tests/layout.test.d.ts +1 -0
  145. package/dist/src/tests/layout.test.js +92 -0
  146. package/dist/src/tests/layout.test.js.map +1 -0
  147. package/dist/src/tests/migration.test.d.ts +1 -0
  148. package/dist/src/tests/migration.test.js +58 -0
  149. package/dist/src/tests/migration.test.js.map +1 -0
  150. package/dist/src/tests/net_merging.test.d.ts +1 -0
  151. package/dist/src/tests/net_merging.test.js +97 -0
  152. package/dist/src/tests/net_merging.test.js.map +1 -0
  153. package/dist/src/tests/occ-minimal.test.d.ts +1 -0
  154. package/dist/src/tests/occ-minimal.test.js +14 -0
  155. package/dist/src/tests/occ-minimal.test.js.map +1 -0
  156. package/dist/src/tests/placement.test.d.ts +1 -0
  157. package/dist/src/tests/placement.test.js +130 -0
  158. package/dist/src/tests/placement.test.js.map +1 -0
  159. package/dist/src/tests/synthesis.test.d.ts +1 -0
  160. package/dist/src/tests/synthesis.test.js +88 -0
  161. package/dist/src/tests/synthesis.test.js.map +1 -0
  162. package/dist/src/types/kicad-library.d.ts +6 -0
  163. package/dist/src/types/kicad-library.js +7 -0
  164. package/dist/src/types/kicad-library.js.map +1 -0
  165. package/dist/src/types/kicad-library.ts +61770 -0
  166. package/package.json +57 -0
  167. package/scripts/patch-opencascade.ts +18 -0
  168. package/scripts/wasm-stub.js +1 -0
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ensurePythonEnv = ensurePythonEnv;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const child_process_1 = require("child_process");
40
+ const config_1 = require("./config");
41
+ const utils_1 = require("./utils");
42
+ /**
43
+ * Ensures the Python virtual environment exists and required packages are installed.
44
+ */
45
+ function ensurePythonEnv() {
46
+ const { projectRoot, pythonPath } = (0, config_1.getConfig)();
47
+ const venvDir = path.join(projectRoot, ".venv");
48
+ // 1. Check if .venv exists
49
+ if (!fs.existsSync(venvDir)) {
50
+ console.log("🐍 Python virtual environment not found. Creating one...");
51
+ try {
52
+ (0, child_process_1.execSync)("python3 -m venv .venv", { cwd: projectRoot, stdio: "inherit" });
53
+ console.log(" ✅ Virtual environment created.");
54
+ }
55
+ catch (err) {
56
+ (0, utils_1.die)(`Failed to create virtual environment: ${err}`);
57
+ }
58
+ }
59
+ // 2. Check for required packages
60
+ const packages = ["circuit-synth", "reportlab"];
61
+ const pip = path.join(projectRoot, ".venv", "bin", "pip");
62
+ console.log("🔍 Checking Python dependencies...");
63
+ const missing = [];
64
+ for (const pkg of packages) {
65
+ try {
66
+ // Use pip show for reliability.
67
+ (0, child_process_1.execSync)(`${pip} show ${pkg}`, { stdio: "ignore" });
68
+ }
69
+ catch {
70
+ missing.push(pkg);
71
+ }
72
+ }
73
+ if (missing.length > 0) {
74
+ console.log(`📦 Installing missing Python packages: ${missing.join(", ")}...`);
75
+ try {
76
+ (0, child_process_1.execSync)(`${pip} install ${missing.join(" ")}`, {
77
+ cwd: projectRoot,
78
+ stdio: "inherit",
79
+ });
80
+ console.log(" ✅ Packages installed successfully.");
81
+ // Patch circuit-synth if it was just installed
82
+ patchCircuitSynth();
83
+ }
84
+ catch (err) {
85
+ (0, utils_1.die)(`Failed to install Python packages: ${err}`);
86
+ }
87
+ }
88
+ else {
89
+ console.log(" ✅ All Python dependencies are up to date.");
90
+ }
91
+ }
92
+ /**
93
+ * Patches circuit-synth's main_generator.py to ensure stable root UUIDs.
94
+ * This prevents KiCad from resetting footprint positions between synthesis runs.
95
+ */
96
+ function patchCircuitSynth() {
97
+ const { pythonPath: python } = (0, config_1.getConfig)();
98
+ let generatorPath;
99
+ try {
100
+ generatorPath = (0, child_process_1.execSync)(`${python} -c "import circuit_synth, os; print(os.path.join(os.path.dirname(circuit_synth.__file__), 'kicad', 'sch_gen', 'main_generator.py'))"`, { encoding: "utf-8" }).trim().split('\n').pop() || "";
101
+ }
102
+ catch (err) {
103
+ console.log("⚠️ Could not locate circuit-synth for patching.");
104
+ return;
105
+ }
106
+ if (!generatorPath || !fs.existsSync(generatorPath)) {
107
+ return;
108
+ }
109
+ const content = fs.readFileSync(generatorPath, "utf-8");
110
+ // Check if patch is already applied
111
+ if (content.includes("BUGFIX: Reuse existing root UUID")) {
112
+ console.log(" ✅ circuit-synth is already patched.");
113
+ return;
114
+ }
115
+ console.log("🩹 Patching circuit-synth for UUID stability...");
116
+ const targetCode = ` # 5) NATURAL HIERARCHY: Top circuit goes on root schematic, subcircuits become child sheets
117
+ logger.info(
118
+ "🔧 NATURAL HIERARCHY: Top circuit on root, subcircuits as child sheets"
119
+ )
120
+ root_uuid = str(
121
+ uuid.uuid4()
122
+ ) # UUID for root schematic (project_name.kicad_sch)
123
+ hierarchical_path = [root_uuid] # Top circuit gets just root level path
124
+
125
+ logger.info(f"Root schematic UUID: {root_uuid}")`;
126
+ const replacementCode = ` # 5) NATURAL HIERARCHY: Top circuit goes on root schematic, subcircuits become child sheets
127
+ logger.info(
128
+ "🔧 NATURAL HIERARCHY: Top circuit on root, subcircuits as child sheets"
129
+ )
130
+
131
+ # BUGFIX: Reuse existing root UUID from the schematic file if it exists.
132
+ # This prevents hierarchy_path and root_uuid from changing between synthesis
133
+ # runs, which would cause KiCad to treat all footprints as new components
134
+ # and reset their positions in the PCB view.
135
+ existing_sch = self.project_dir / f"{self.project_name}.kicad_sch"
136
+ root_uuid = None
137
+ if existing_sch.exists():
138
+ import re as _re
139
+ try:
140
+ with open(existing_sch, "r", encoding="utf-8") as _f:
141
+ # Read only the first 1KB — the UUID is near the top of the file
142
+ header = _f.read(1024)
143
+ match = _re.search(r'\\(uuid\\s+"([0-9a-f-]+)"\\)', header)
144
+ if match:
145
+ root_uuid = match.group(1)
146
+ logger.info(f"Reusing existing root UUID from schematic: {root_uuid}")
147
+ except Exception as e:
148
+ logger.warning(f"Failed to read existing schematic UUID: {e}")
149
+
150
+ if not root_uuid:
151
+ root_uuid = str(uuid.uuid4())
152
+ logger.info(f"Generated new root UUID: {root_uuid}")
153
+
154
+ hierarchical_path = [root_uuid] # Top circuit gets just root level path
155
+
156
+ logger.info(f"Root schematic UUID: {root_uuid}")`;
157
+ if (content.includes(targetCode)) {
158
+ const newContent = content.replace(targetCode, replacementCode);
159
+ fs.writeFileSync(generatorPath, newContent, "utf-8");
160
+ console.log(" ✅ circuit-synth patched successfully.");
161
+ }
162
+ else {
163
+ console.log("⚠️ Could not find target code in main_generator.py to patch. Maybe the version changed?");
164
+ }
165
+ }
166
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/cli/env.ts"],"names":[],"mappings":"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"}
File without changes
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ fetch("https://jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList/v2", {
3
+ "headers": {
4
+ "accept": "application/json, text/plain, */*",
5
+ "accept-language": "de-DE,de;q=0.9,en-GB;q=0.8,en;q=0.7,en-US;q=0.6,zh-TW;q=0.5,zh;q=0.4",
6
+ "cache-control": "no-cache",
7
+ "content-type": "application/json",
8
+ "pragma": "no-cache",
9
+ "priority": "u=1, i",
10
+ "sec-ch-ua": "\"Not(A:Brand\";v=\"8\", \"Chromium\";v=\"144\", \"Google Chrome\";v=\"144\"",
11
+ "sec-ch-ua-mobile": "?0",
12
+ "sec-ch-ua-platform": "\"macOS\"",
13
+ "sec-fetch-dest": "empty",
14
+ "sec-fetch-mode": "cors",
15
+ "sec-fetch-site": "same-origin",
16
+ "secretkey": "64656661756c744b65794964",
17
+ "x-xsrf-token": "edef5572-087c-4ab6-a072-c5deef784c57",
18
+ "cookie": "_fwb=117Kq87IgTtDj37hKQb20Gr.1771215174963; _tt_enable_cookie=1; _ttp=01KHJADGZ8JXKS1WXAZR1WJQ28_.tt.1; _ga=GA1.1.1007220391.1771215177; _gcl_au=1.1.1194838226.1771215184; traceUrl=%7B%22advertisingUrl%22%3A%22https%3A%2F%2Fjlcpcb.com%2F%22%2C%22websiteUrl%22%3A%22https%3A%2F%2Fcart.jlcpcb.com%2Fquote%3FstencilLayer%3D2%26stencilWidth%3D104.5%26stencilLength%3D99.5%26stencilCounts%3D10%26plateType%3D1%26spm%3DJlcpcb.Homepage.1010%22%7D; jlc_s=A; acw_tc=0a00bef117712909067731382e609cd4013419758a9f087f20ba4823880bf5; wcs_bt=1961948:1771290907; XSRF-TOKEN=edef5572-087c-4ab6-a072-c5deef784c57; JLCPCB_SESSION_ID=7a1104a8-dbaa-4859-b51c-02fe5bc559e3; _clck=xmbwtp%5E2%5Eg3n%5E1%5E2238; ttcsid_CRN218RC77U9Q4TC4VAG=1771290908312::CUEo79QKe2IOc43-EOpP.2.1771290995830.1; ttcsid=1771290908312::rTcAhMTWRJSeMdcdwcOJ.2.1771290995830.0; _clsk=1af0q3q%5E1771290996183%5E4%5E1%5Eb.clarity.ms%2Fcollect; _ga_XDWD4D52RC=GS2.1.s1771290908$o2$g1$t1771290996$j60$l0$h0; _ga_0GJG2DD1ZP=GS2.1.s1771290908$o2$g1$t1771290996$j60$l0$h0; _uetsid=3079d5200aee11f1a93473fb6a997923; _uetvid=3079d5400aee11f1948e85661da10573"
19
+ },
20
+ "body": "{\"currentPage\":1,\"pageSize\":25,\"presaleType\":\"stock\",\"searchType\":2,\"keyword\":\"10k resistor\",\"componentLibraryType\":null,\"stockFlag\":null,\"stockSort\":null,\"firstSortName\":null,\"secondSortName\":null}",
21
+ "method": "POST"
22
+ });
23
+ //# sourceMappingURL=search-jlc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-jlc.js","sourceRoot":"","sources":["../../../src/cli/search-jlc.ts"],"names":[],"mappings":";AAAA,KAAK,CAAC,6FAA6F,EAAE;IACnG,SAAS,EAAE;QACT,QAAQ,EAAE,mCAAmC;QAC7C,iBAAiB,EAAE,sEAAsE;QACzF,eAAe,EAAE,UAAU;QAC3B,cAAc,EAAE,kBAAkB;QAClC,QAAQ,EAAE,UAAU;QACpB,UAAU,EAAE,QAAQ;QACpB,WAAW,EAAE,8EAA8E;QAC3F,kBAAkB,EAAE,IAAI;QACxB,oBAAoB,EAAE,WAAW;QACjC,gBAAgB,EAAE,OAAO;QACzB,gBAAgB,EAAE,MAAM;QACxB,gBAAgB,EAAE,aAAa;QAC/B,WAAW,EAAE,0BAA0B;QACvC,cAAc,EAAE,sCAAsC;QACtD,QAAQ,EAAE,ilCAAilC;KAC5lC;IACD,MAAM,EAAE,gOAAgO;IACxO,QAAQ,EAAE,MAAM;CACjB,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { CircuitSnapshot } from "./codegen";
2
+ /**
3
+ * Execute the generated Python code via circuit-synth.
4
+ *
5
+ * Appends the execution boilerplate to the code and runs it through
6
+ * the project's Python virtual environment.
7
+ */
8
+ export declare function runSynthesis(snapshot: CircuitSnapshot, outputDir: string): {
9
+ success: boolean;
10
+ output: string;
11
+ };
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runSynthesis = runSynthesis;
37
+ const child_process_1 = require("child_process");
38
+ const path = __importStar(require("path"));
39
+ const codegen_1 = require("./codegen");
40
+ const KicadLibrary_1 = require("../synth/KicadLibrary");
41
+ const fs = __importStar(require("fs"));
42
+ const config_1 = require("./config");
43
+ /** Escape a Python string value (duplicated from codegen for independence) */
44
+ function pyStr(s) {
45
+ return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
46
+ }
47
+ /**
48
+ * Execute the generated Python code via circuit-synth.
49
+ *
50
+ * Appends the execution boilerplate to the code and runs it through
51
+ * the project's Python virtual environment.
52
+ */
53
+ function runSynthesis(snapshot, outputDir) {
54
+ const code = (0, codegen_1.generatePython)(snapshot);
55
+ const funcName = snapshot.name.toLowerCase().replace(/[^a-z0-9_]/g, "_");
56
+ const { projectRoot, pythonPath } = (0, config_1.getConfig)();
57
+ // Execution boilerplate
58
+ const runScript = `
59
+ if __name__ == "__main__":
60
+ import os
61
+ import sys
62
+
63
+ # Run the circuit function
64
+ circuit_obj = ${funcName}()
65
+
66
+ # Generate the KiCad project
67
+ # circuit-synth will create the folder if it doesn't exist
68
+ result = circuit_obj.generate_kicad_project(
69
+ project_name=${pyStr(outputDir)},
70
+ placement_algorithm=${pyStr(snapshot.placementAlgorithm || "hierarchical")},
71
+ generate_pcb=False,
72
+ force_regenerate=True,
73
+ )
74
+ import json
75
+ # Convert Paths to strings for JSON serialization
76
+ ser_result = {k: str(v) if hasattr(v, "__fspath__") or hasattr(v, "parts") else v for k, v in result.items()}
77
+ print("RES:" + json.dumps(ser_result))
78
+ `;
79
+ const fullCode = code + runScript;
80
+ // Setup environment for circuit-synth
81
+ const env = { ...process.env };
82
+ const localLib = path.join(projectRoot, "lib");
83
+ const kicadLib = path.join(projectRoot, ".kicad");
84
+ const systemLib = "/Applications/KiCad/KiCad.app/Contents/SharedSupport/symbols";
85
+ // Set KICAD_SYMBOL_DIR and PYTHONPATH
86
+ const testSyms = path.join(projectRoot, "src", "tests", "assets", "symbols");
87
+ env.KICAD_SYMBOL_DIR = `${localLib}:${kicadLib}:${testSyms}:${systemLib}`;
88
+ env.PYTHONPATH = `${projectRoot}:${process.env.PYTHONPATH || ""}`;
89
+ const res = (0, child_process_1.spawnSync)(pythonPath, ["-c", fullCode], {
90
+ cwd: projectRoot,
91
+ env,
92
+ encoding: "utf-8",
93
+ });
94
+ if (res.error) {
95
+ return { success: false, output: res.error.message };
96
+ }
97
+ const output = res.stdout + (res.stderr || "");
98
+ const jsonMatch = output.match(/RES:({.*})/);
99
+ const synthResult = jsonMatch ? JSON.parse(jsonMatch[1]) : null;
100
+ const success = res.status === 0 && (synthResult ? synthResult.success : true);
101
+ if (success) {
102
+ // Generate library tables in the project directory
103
+ try {
104
+ const relPath = path.relative(outputDir, kicadLib);
105
+ const fpTable = KicadLibrary_1.KicadLibrary.generateFpLibTable(relPath);
106
+ const symTable = KicadLibrary_1.KicadLibrary.generateSymLibTable(relPath);
107
+ fs.writeFileSync(path.join(outputDir, "fp-lib-table"), fpTable);
108
+ fs.writeFileSync(path.join(outputDir, "sym-lib-table"), symTable);
109
+ console.log(` ✅ Generated KiCad library tables in project directory.`);
110
+ }
111
+ catch (err) {
112
+ console.warn(` ⚠️ Failed to generate library tables: ${err.message}`);
113
+ }
114
+ }
115
+ return {
116
+ success,
117
+ output: output,
118
+ };
119
+ }
120
+ //# sourceMappingURL=synthesis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synthesis.js","sourceRoot":"","sources":["../../../src/cli/synthesis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { Component } from "../../synth/Component";
2
+ /**
3
+ * Generate a JLCPCB-compatible BOM CSV from the component registry.
4
+ *
5
+ * Format:
6
+ * Comment, Designator, Footprint, LCSC Part #
7
+ *
8
+ * Grouping:
9
+ * - All parts with the same footprint, same value, and same LCSC Part #
10
+ * are merged into one line.
11
+ * - Comment = value (e.g. "39pF")
12
+ * - Designator = comma-separated refs (sorted numerically)
13
+ * - Footprint = footprint name (library:name → just the name part)
14
+ * - LCSC Part # = from component's partNo
15
+ *
16
+ * Components without a value default to their symbol name.
17
+ * DNC and TestPoint components are excluded.
18
+ */
19
+ export declare function generateBom(projectName: string, outputDir: string, components: Component<any>[]): string | null;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateBom = generateBom;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Generate a JLCPCB-compatible BOM CSV from the component registry.
41
+ *
42
+ * Format:
43
+ * Comment, Designator, Footprint, LCSC Part #
44
+ *
45
+ * Grouping:
46
+ * - All parts with the same footprint, same value, and same LCSC Part #
47
+ * are merged into one line.
48
+ * - Comment = value (e.g. "39pF")
49
+ * - Designator = comma-separated refs (sorted numerically)
50
+ * - Footprint = footprint name (library:name → just the name part)
51
+ * - LCSC Part # = from component's partNo
52
+ *
53
+ * Components without a value default to their symbol name.
54
+ * DNC and TestPoint components are excluded.
55
+ */
56
+ function generateBom(projectName, outputDir, components) {
57
+ console.log(` -> Generating BOM from circuit registry...`);
58
+ // Build BOM entries, filtering out marker components
59
+ const entries = [];
60
+ for (const comp of components) {
61
+ // Skip DNC markers
62
+ if (comp.symbol === "Device:DNC")
63
+ continue;
64
+ // Determine value
65
+ let value = comp.value || "";
66
+ if (!value) {
67
+ // Fallback: use symbol name (part after colon)
68
+ const parts = comp.symbol.split(":");
69
+ value = parts.length > 1 ? parts[parts.length - 1] : comp.symbol;
70
+ }
71
+ // Extract footprint name (strip library prefix)
72
+ let footprint = comp.footprint;
73
+ const fpParts = footprint.split(":");
74
+ if (fpParts.length > 1) {
75
+ footprint = fpParts[fpParts.length - 1];
76
+ }
77
+ const lcsc = comp.partNo || "";
78
+ entries.push({
79
+ ref: comp.ref,
80
+ value,
81
+ footprint,
82
+ lcsc,
83
+ });
84
+ }
85
+ if (entries.length === 0) {
86
+ console.warn("⚠️ No components found for BOM.");
87
+ return null;
88
+ }
89
+ // Group by (value, footprint, lcsc) - Muenchian grouping equivalent
90
+ const groups = new Map();
91
+ for (const entry of entries) {
92
+ const key = `${entry.value}\x00${entry.footprint}\x00${entry.lcsc}`;
93
+ if (!groups.has(key)) {
94
+ groups.set(key, []);
95
+ }
96
+ groups.get(key).push(entry);
97
+ }
98
+ // Sort refs within each group numerically
99
+ const sortRef = (a, b) => {
100
+ // Extract numeric suffix for natural sorting
101
+ const numA = parseInt(a.replace(/[^0-9]/g, ""), 10) || 0;
102
+ const numB = parseInt(b.replace(/[^0-9]/g, ""), 10) || 0;
103
+ if (numA !== numB)
104
+ return numA - numB;
105
+ return a.localeCompare(b);
106
+ };
107
+ // Build CSV
108
+ const csvLines = ["Comment,Designator,Footprint,LCSC Part #"];
109
+ // Sort groups by first ref in each group
110
+ const sortedGroups = [...groups.entries()].sort((a, b) => {
111
+ const refsA = a[1].map((e) => e.ref).sort(sortRef);
112
+ const refsB = b[1].map((e) => e.ref).sort(sortRef);
113
+ return sortRef(refsA[0], refsB[0]);
114
+ });
115
+ for (const [, group] of sortedGroups) {
116
+ const { value, footprint, lcsc } = group[0];
117
+ const refs = group
118
+ .map((e) => e.ref)
119
+ .sort(sortRef)
120
+ .join(",");
121
+ // CSV escape: wrap in quotes if value contains commas or quotes
122
+ const esc = (s) => s.includes(",") || s.includes('"') ? `"${s.replace(/"/g, '""')}"` : s;
123
+ csvLines.push(`${esc(value)},${esc(refs)},${esc(footprint)},${esc(lcsc)}`);
124
+ }
125
+ const bomFile = path.join(outputDir, `BOM-${projectName}.csv`);
126
+ fs.writeFileSync(bomFile, csvLines.join("\n"), "utf-8");
127
+ console.log(` -> BOM written: ${path.basename(bomFile)} (${sortedGroups.length} unique parts)`);
128
+ return bomFile;
129
+ }
130
+ //# sourceMappingURL=bom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bom.js","sourceRoot":"","sources":["../../../../src/cli/utils/bom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Convert KiCad's Pick & Place (pos) ASCII output to JLCPCB CPL format.
3
+ *
4
+ * KiCad pos (ASCII, mm, both sides) output has the following columns:
5
+ * Ref Val Package PosX PosY Rot Side
6
+ *
7
+ * JLCPCB CPL expects:
8
+ * Designator Val Package Mid X Mid Y Rotation Layer
9
+ *
10
+ * Based on the XSL conversion logic from:
11
+ * https://gist.github.com/arturo182/a8c4a4b96907cfccf616a1edb59d0389
12
+ *
13
+ * Differences:
14
+ * - Column headers are renamed to match JLCPCB expectations
15
+ * - "Side" values "top"/"bottom" are mapped to "Top"/"Bottom"
16
+ * - Position values are expressed in mm with "mm" suffix
17
+ */
18
+ export declare function convertPosToCpl(posFilePath: string, cplOutputPath: string): string;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.convertPosToCpl = convertPosToCpl;
37
+ const fs = __importStar(require("fs"));
38
+ /**
39
+ * Convert KiCad's Pick & Place (pos) ASCII output to JLCPCB CPL format.
40
+ *
41
+ * KiCad pos (ASCII, mm, both sides) output has the following columns:
42
+ * Ref Val Package PosX PosY Rot Side
43
+ *
44
+ * JLCPCB CPL expects:
45
+ * Designator Val Package Mid X Mid Y Rotation Layer
46
+ *
47
+ * Based on the XSL conversion logic from:
48
+ * https://gist.github.com/arturo182/a8c4a4b96907cfccf616a1edb59d0389
49
+ *
50
+ * Differences:
51
+ * - Column headers are renamed to match JLCPCB expectations
52
+ * - "Side" values "top"/"bottom" are mapped to "Top"/"Bottom"
53
+ * - Position values are expressed in mm with "mm" suffix
54
+ */
55
+ function convertPosToCpl(posFilePath, cplOutputPath) {
56
+ const content = fs.readFileSync(posFilePath, "utf-8");
57
+ const lines = content.split("\n");
58
+ const csvLines = [];
59
+ // JLCPCB CPL header
60
+ csvLines.push("Designator,Val,Package,Mid X,Mid Y,Rotation,Layer");
61
+ for (const line of lines) {
62
+ const trimmed = line.trim();
63
+ // Skip empty lines, comment lines (starting with #), header lines
64
+ if (!trimmed || trimmed.startsWith("#"))
65
+ continue;
66
+ // KiCad ASCII pos format is whitespace-separated:
67
+ // Ref Val Package PosX PosY Rot Side
68
+ // But Val and Package may contain spaces... KiCad uses fixed-width columns.
69
+ // Actually, looking at KiCad output, it uses a specific format.
70
+ // Let's parse it properly.
71
+ // KiCad ASCII pos output looks like:
72
+ // ### Module positions - created on ...
73
+ // ### Printed by KiCad version ...
74
+ // ## Unit = mm, Angle = deg.
75
+ // ## Side : All
76
+ // # Ref Val Package PosX PosY Rot Side
77
+ // C1 100nF C_0603_1608Metric 152.4000 -98.0000 0.0000 top
78
+ //
79
+ // Parse with regex for fixed-width-ish format
80
+ // Fields are separated by whitespace, but the last field is "top" or "bottom"
81
+ const match = trimmed.match(/^(\S+)\s+(\S+)\s+(\S+)\s+([-\d.]+)\s+([-\d.]+)\s+([-\d.]+)\s+(\S+)$/);
82
+ if (!match)
83
+ continue;
84
+ const [, ref, val, pkg, posX, posY, rot, side] = match;
85
+ // Map side: KiCad uses "top"/"bottom", JLCPCB uses "Top"/"Bottom"
86
+ const layer = side.toLowerCase() === "top"
87
+ ? "Top"
88
+ : side.toLowerCase() === "bottom"
89
+ ? "Bottom"
90
+ : side;
91
+ // Format position with mm suffix (JLCPCB expects this)
92
+ const midX = `${posX}mm`;
93
+ const midY = `${posY}mm`;
94
+ // CSV escape helper
95
+ const esc = (s) => s.includes(",") || s.includes('"') ? `"${s.replace(/"/g, '""')}"` : s;
96
+ csvLines.push(`${esc(ref)},${esc(val)},${esc(pkg)},${midX},${midY},${rot},${layer}`);
97
+ }
98
+ fs.writeFileSync(cplOutputPath, csvLines.join("\n"), "utf-8");
99
+ return cplOutputPath;
100
+ }
101
+ //# sourceMappingURL=cpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpl.js","sourceRoot":"","sources":["../../../../src/cli/utils/cpl.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,0CAiEC;AArFD,uCAAyB;AAGzB;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,eAAe,CAC3B,WAAmB,EACnB,aAAqB;IAErB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,oBAAoB;IACpB,QAAQ,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAEnE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAE5B,kEAAkE;QAClE,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAElD,kDAAkD;QAClD,2CAA2C;QAC3C,4EAA4E;QAC5E,gEAAgE;QAChE,2BAA2B;QAE3B,qCAAqC;QACrC,wCAAwC;QACxC,mCAAmC;QACnC,6BAA6B;QAC7B,gBAAgB;QAChB,8EAA8E;QAC9E,gFAAgF;QAChF,EAAE;QAEF,8CAA8C;QAC9C,8EAA8E;QAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CACvB,qEAAqE,CACxE,CAAC;QAEF,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QAEvD,kEAAkE;QAClE,MAAM,KAAK,GACP,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK;YACxB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,QAAQ;gBAC7B,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,IAAI,CAAC;QAEnB,uDAAuD;QACvD,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC;QAEzB,oBAAoB;QACpB,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CACtB,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1E,QAAQ,CAAC,IAAI,CACT,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,KAAK,EAAE,CACxE,CAAC;IACN,CAAC;IAED,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAC9D,OAAO,aAAa,CAAC;AACzB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare function die(msg: string): never;
2
+ export declare function prompt(question: string): Promise<string>;
3
+ /**
4
+ * List available schematics from src/schematics/.
5
+ * Each schematic is a sub-folder containing an index.ts with a default export.
6
+ */
7
+ export declare function listSchematics(): string[];
8
+ /**
9
+ * Resolve a schematic entry: either a direct path or interactive selection.
10
+ */
11
+ export declare function resolveSchematic(entry?: string): Promise<string>;