fritzkit 0.0.0-stage → 0.1.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.
Files changed (319) hide show
  1. package/.claude-plugin/marketplace.json +12 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/CHANGELOG.md +8 -0
  4. package/LICENSE +21 -0
  5. package/README.md +429 -2
  6. package/dist/cli/commands/doctor.d.ts +4 -0
  7. package/dist/cli/commands/doctor.js +7 -0
  8. package/dist/cli/commands/doctor.js.map +1 -0
  9. package/dist/cli/commands/export.d.ts +17 -0
  10. package/dist/cli/commands/export.js +81 -0
  11. package/dist/cli/commands/export.js.map +1 -0
  12. package/dist/cli/commands/kicad.d.ts +10 -0
  13. package/dist/cli/commands/kicad.js +22 -0
  14. package/dist/cli/commands/kicad.js.map +1 -0
  15. package/dist/cli/commands/parts.d.ts +22 -0
  16. package/dist/cli/commands/parts.js +46 -0
  17. package/dist/cli/commands/parts.js.map +1 -0
  18. package/dist/cli/commands/prune.d.ts +14 -0
  19. package/dist/cli/commands/prune.js +67 -0
  20. package/dist/cli/commands/prune.js.map +1 -0
  21. package/dist/cli/commands/render.d.ts +24 -0
  22. package/dist/cli/commands/render.js +62 -0
  23. package/dist/cli/commands/render.js.map +1 -0
  24. package/dist/cli/commands/schema.d.ts +10 -0
  25. package/dist/cli/commands/schema.js +35 -0
  26. package/dist/cli/commands/schema.js.map +1 -0
  27. package/dist/cli/commands/ui.d.ts +14 -0
  28. package/dist/cli/commands/ui.js +67 -0
  29. package/dist/cli/commands/ui.js.map +1 -0
  30. package/dist/cli/commands/validate.d.ts +24 -0
  31. package/dist/cli/commands/validate.js +91 -0
  32. package/dist/cli/commands/validate.js.map +1 -0
  33. package/dist/cli/context.d.ts +81 -0
  34. package/dist/cli/context.js +139 -0
  35. package/dist/cli/context.js.map +1 -0
  36. package/dist/cli/main.d.ts +2 -0
  37. package/dist/cli/main.js +8 -0
  38. package/dist/cli/main.js.map +1 -0
  39. package/dist/cli/program.d.ts +21 -0
  40. package/dist/cli/program.js +158 -0
  41. package/dist/cli/program.js.map +1 -0
  42. package/dist/cli/raster.d.ts +4 -0
  43. package/dist/cli/raster.js +9 -0
  44. package/dist/cli/raster.js.map +1 -0
  45. package/dist/core/compile/breadboard-util.d.ts +12 -0
  46. package/dist/core/compile/breadboard-util.js +107 -0
  47. package/dist/core/compile/breadboard-util.js.map +1 -0
  48. package/dist/core/compile/breadboard.d.ts +60 -0
  49. package/dist/core/compile/breadboard.js +513 -0
  50. package/dist/core/compile/breadboard.js.map +1 -0
  51. package/dist/core/compile/fzp.d.ts +12 -0
  52. package/dist/core/compile/fzp.js +183 -0
  53. package/dist/core/compile/fzp.js.map +1 -0
  54. package/dist/core/compile/icon.d.ts +17 -0
  55. package/dist/core/compile/icon.js +27 -0
  56. package/dist/core/compile/icon.js.map +1 -0
  57. package/dist/core/compile/index.d.ts +4 -0
  58. package/dist/core/compile/index.js +62 -0
  59. package/dist/core/compile/index.js.map +1 -0
  60. package/dist/core/compile/pcb.d.ts +39 -0
  61. package/dist/core/compile/pcb.js +164 -0
  62. package/dist/core/compile/pcb.js.map +1 -0
  63. package/dist/core/compile/schematic.d.ts +69 -0
  64. package/dist/core/compile/schematic.js +240 -0
  65. package/dist/core/compile/schematic.js.map +1 -0
  66. package/dist/core/compile/style.d.ts +52 -0
  67. package/dist/core/compile/style.js +72 -0
  68. package/dist/core/compile/style.js.map +1 -0
  69. package/dist/core/compile/types.d.ts +50 -0
  70. package/dist/core/compile/types.js +15 -0
  71. package/dist/core/compile/types.js.map +1 -0
  72. package/dist/core/export/bin.d.ts +61 -0
  73. package/dist/core/export/bin.js +221 -0
  74. package/dist/core/export/bin.js.map +1 -0
  75. package/dist/core/export/fzpz.d.ts +37 -0
  76. package/dist/core/export/fzpz.js +121 -0
  77. package/dist/core/export/fzpz.js.map +1 -0
  78. package/dist/core/export/index.d.ts +5 -0
  79. package/dist/core/export/index.js +6 -0
  80. package/dist/core/export/index.js.map +1 -0
  81. package/dist/core/export/install.d.ts +77 -0
  82. package/dist/core/export/install.js +183 -0
  83. package/dist/core/export/install.js.map +1 -0
  84. package/dist/core/export/prune.d.ts +72 -0
  85. package/dist/core/export/prune.js +165 -0
  86. package/dist/core/export/prune.js.map +1 -0
  87. package/dist/core/fritzing.d.ts +22 -0
  88. package/dist/core/fritzing.js +77 -0
  89. package/dist/core/fritzing.js.map +1 -0
  90. package/dist/core/identity.d.ts +29 -0
  91. package/dist/core/identity.js +70 -0
  92. package/dist/core/identity.js.map +1 -0
  93. package/dist/core/image/bbox.d.ts +27 -0
  94. package/dist/core/image/bbox.js +24 -0
  95. package/dist/core/image/bbox.js.map +1 -0
  96. package/dist/core/image/index.d.ts +8 -0
  97. package/dist/core/image/index.js +9 -0
  98. package/dist/core/image/index.js.map +1 -0
  99. package/dist/core/image/key.d.ts +72 -0
  100. package/dist/core/image/key.js +317 -0
  101. package/dist/core/image/key.js.map +1 -0
  102. package/dist/core/image/prepare.d.ts +31 -0
  103. package/dist/core/image/prepare.js +98 -0
  104. package/dist/core/image/prepare.js.map +1 -0
  105. package/dist/core/image/remover.d.ts +17 -0
  106. package/dist/core/image/remover.js +51 -0
  107. package/dist/core/image/remover.js.map +1 -0
  108. package/dist/core/index.d.ts +16 -0
  109. package/dist/core/index.js +17 -0
  110. package/dist/core/index.js.map +1 -0
  111. package/dist/core/kicad/footprint.d.ts +104 -0
  112. package/dist/core/kicad/footprint.js +317 -0
  113. package/dist/core/kicad/footprint.js.map +1 -0
  114. package/dist/core/kicad/index.d.ts +25 -0
  115. package/dist/core/kicad/index.js +197 -0
  116. package/dist/core/kicad/index.js.map +1 -0
  117. package/dist/core/kicad/sexpr.d.ts +35 -0
  118. package/dist/core/kicad/sexpr.js +163 -0
  119. package/dist/core/kicad/sexpr.js.map +1 -0
  120. package/dist/core/resolve.d.ts +113 -0
  121. package/dist/core/resolve.js +301 -0
  122. package/dist/core/resolve.js.map +1 -0
  123. package/dist/core/schema/part.d.ts +995 -0
  124. package/dist/core/schema/part.js +278 -0
  125. package/dist/core/schema/part.js.map +1 -0
  126. package/dist/core/schema/part.schema.json +1122 -0
  127. package/dist/core/svg.d.ts +20 -0
  128. package/dist/core/svg.js +56 -0
  129. package/dist/core/svg.js.map +1 -0
  130. package/dist/core/units.d.ts +28 -0
  131. package/dist/core/units.js +42 -0
  132. package/dist/core/units.js.map +1 -0
  133. package/dist/core/validate/checker.d.ts +46 -0
  134. package/dist/core/validate/checker.js +142 -0
  135. package/dist/core/validate/checker.js.map +1 -0
  136. package/dist/core/validate/doctor.d.ts +25 -0
  137. package/dist/core/validate/doctor.js +156 -0
  138. package/dist/core/validate/doctor.js.map +1 -0
  139. package/dist/core/validate/explain.d.ts +10 -0
  140. package/dist/core/validate/explain.js +135 -0
  141. package/dist/core/validate/explain.js.map +1 -0
  142. package/dist/core/validate/index.d.ts +21 -0
  143. package/dist/core/validate/index.js +96 -0
  144. package/dist/core/validate/index.js.map +1 -0
  145. package/dist/core/validate/structural.d.ts +74 -0
  146. package/dist/core/validate/structural.js +664 -0
  147. package/dist/core/validate/structural.js.map +1 -0
  148. package/dist/core/workspace.d.ts +69 -0
  149. package/dist/core/workspace.js +213 -0
  150. package/dist/core/workspace.js.map +1 -0
  151. package/dist/mcp/jsonpatch.d.ts +30 -0
  152. package/dist/mcp/jsonpatch.js +145 -0
  153. package/dist/mcp/jsonpatch.js.map +1 -0
  154. package/dist/mcp/main.d.ts +2 -0
  155. package/dist/mcp/main.js +5 -0
  156. package/dist/mcp/main.js.map +1 -0
  157. package/dist/mcp/server.d.ts +28 -0
  158. package/dist/mcp/server.js +310 -0
  159. package/dist/mcp/server.js.map +1 -0
  160. package/dist/server/api.d.ts +72 -0
  161. package/dist/server/api.js +507 -0
  162. package/dist/server/api.js.map +1 -0
  163. package/dist/server/events.d.ts +28 -0
  164. package/dist/server/events.js +134 -0
  165. package/dist/server/events.js.map +1 -0
  166. package/dist/server/http.d.ts +36 -0
  167. package/dist/server/http.js +127 -0
  168. package/dist/server/http.js.map +1 -0
  169. package/dist/server/index.d.ts +65 -0
  170. package/dist/server/index.js +193 -0
  171. package/dist/server/index.js.map +1 -0
  172. package/dist/ui/assets/index-DLU8opyI.js +63 -0
  173. package/dist/ui/assets/index-GhUA-T3b.css +1 -0
  174. package/dist/ui/index.html +14 -0
  175. package/package.json +89 -4
  176. package/skills/fritzkit/SKILL.md +690 -0
  177. package/skills/fritzkit/reference/examples.md +275 -0
  178. package/skills/fritzkit/reference/part-json.md +333 -0
  179. package/skills/fritzkit/scripts/measure.py +197 -0
  180. package/tools/fzp-checker/.python-version +1 -0
  181. package/tools/fzp-checker/README.md +45 -0
  182. package/tools/fzp-checker/check_fzp.py +48 -0
  183. package/tools/fzp-checker/pyproject.toml +26 -0
  184. package/tools/fzp-checker/uv.lock +266 -0
  185. package/tools/fzp-checker/vendor/LICENSE.txt +8 -0
  186. package/tools/fzp-checker/vendor/UPSTREAM.md +25 -0
  187. package/tools/fzp-checker/vendor/fzp_checker.py +14 -0
  188. package/tools/fzp-checker/vendor/scripts/checks/__init__.py +26 -0
  189. package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
  190. package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
  191. package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
  192. package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
  193. package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
  194. package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
  195. package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
  196. package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
  197. package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
  198. package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
  199. package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
  200. package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
  201. package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
  202. package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
  203. package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
  204. package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
  205. package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
  206. package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
  207. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
  208. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
  209. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
  210. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
  211. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
  212. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
  213. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
  214. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
  215. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
  216. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
  217. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
  218. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
  219. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
  220. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
  221. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
  222. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
  223. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
  224. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
  225. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
  226. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
  227. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
  228. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
  229. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
  230. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
  231. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
  232. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
  233. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
  234. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
  235. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
  236. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
  237. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
  238. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
  239. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
  240. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
  241. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
  242. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
  243. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
  244. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
  245. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
  246. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
  247. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
  248. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
  249. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
  250. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
  251. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
  252. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
  253. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
  254. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
  255. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
  256. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
  257. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
  258. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
  259. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
  260. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
  261. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
  262. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
  263. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
  264. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
  265. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
  266. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
  267. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
  268. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
  269. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
  270. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
  271. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
  272. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
  273. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
  274. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
  275. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
  276. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
  277. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
  278. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
  279. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
  280. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
  281. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
  282. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
  283. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
  284. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
  285. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
  286. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
  287. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
  288. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
  289. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
  290. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
  291. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
  292. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
  293. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
  294. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
  295. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
  296. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
  297. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
  298. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
  299. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
  300. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
  301. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
  302. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
  303. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
  304. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
  305. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
  306. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
  307. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
  308. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
  309. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
  310. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
  311. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
  312. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
  313. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
  314. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
  315. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
  316. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
  317. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
  318. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
  319. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_mismatch_schematic.svg +6 -0
@@ -0,0 +1,690 @@
1
+ ---
2
+ name: fritzkit
3
+ description: Use when the user wants a Fritzing part (.fzpz) for a real board, module or component - from an Amazon or AliExpress listing, a datasheet PDF, a Digikey part number, product photos, a manufacturer wiki page or a KiCad footprint (.kicad_mod) - or wants to edit, fix, re-export, swap versions of, install or prune an existing fritzkit part. fritzkit compiles a part.json spec (outline in mm, every pad/pin with its name, role and position, buses for shared nets) into a checker-clean Fritzing part with breadboard, schematic, PCB and icon views, and installs it into Fritzing's My Parts. You do the research and vision work with your own tools; fritzkit (CLI `npx fritzkit` or the `fritzkit` MCP server) is offline and deterministic.
4
+ ---
5
+
6
+ # fritzkit: build a Fritzing part from a product link
7
+
8
+ You turn sources (listing, photos, datasheet) into a `part.json`; fritzkit turns `part.json`
9
+ into a `.fzpz` that Fritzing imports. The quality of the part is decided by YOUR spec: the
10
+ outline size, every pad's name, role, net and position. fritzkit handles all the Fritzing
11
+ format rules (layer ids, units, fonts, the 0.1 in grid, connector ids).
12
+
13
+ Two equivalent interfaces; use whichever is available:
14
+
15
+ | step | CLI (`npx fritzkit ...` or `fritzkit ...`) | MCP tool (server `fritzkit`) |
16
+ | ------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------- |
17
+ | create workspace | `fritzkit init` | `fritzkit_init_workspace` |
18
+ | new part | `fritzkit new <slug> --title "<title>"` | `fritzkit_new_part` |
19
+ | read / write spec | edit `<workspace>/<slug>/part.json` | `fritzkit_get_part`, `fritzkit_set_part`, `fritzkit_patch_part` (JSON patch), `fritzkit_add_pins` |
20
+ | check | `fritzkit validate <slug> [--checker]` | `fritzkit_validate` |
21
+ | look at it | `fritzkit render <slug> --png [--view <v>]` | `fritzkit_render` (returns SVG text or a PNG image) |
22
+ | package | `fritzkit export <slug>` | `fritzkit_export` |
23
+ | install in Fritzing | `fritzkit install <slug>` | `fritzkit_install` |
24
+ | remove old versions | `fritzkit prune <family> --yes` | `fritzkit_prune` |
25
+ | KiCad footprint | `fritzkit import-kicad <file> --slug <slug>` | `fritzkit_import_kicad` |
26
+ | schema / health | `fritzkit schema`, `fritzkit doctor` | `fritzkit_schema`, `fritzkit_doctor` |
27
+
28
+ Check which interface you have: if tools named `fritzkit_*` are in your tool list, the MCP
29
+ server is available; if not, use the CLI. The two are equivalent.
30
+
31
+ Global CLI flags: `--workspace <dir>` (default `~/fritzkit/parts`, or env
32
+ `FRITZKIT_WORKSPACE`) and `--json`. `<part>` is a slug in the workspace or a path to a part
33
+ folder. `--workspace` may be relative to the cwd. Each part lives in `<workspace>/<slug>/`
34
+ with `part.json` and `assets/`. `fritzkit new` writes a TEMPLATE (a placeholder one-pin
35
+ breakout): replace the whole part.json with your own spec, do not edit the template in place.
36
+ EVERY placeholder field must go. The template values to grep for before export: description `Generic sensor breakout. Replace this with what the part is.`, tags
37
+ `sensor` and `breakout`, outline 20 x 15 mm, and a single pin with key and name `VCC`
38
+ (description `Supply voltage`, role `power`, female, x 3.81, y 7.62). If your part has no
39
+ `VCC` pin, no `VCC` may survive; run `grep -nE 'VCC|Generic sensor|breakout' part.json`.
40
+
41
+ Field reference (every field, type, default, unit): [reference/part-json.md](reference/part-json.md).
42
+ More worked examples (HW-391 pads-only board, wire-lead ESC, relay module, KiCad import): [reference/examples.md](reference/examples.md).
43
+
44
+ ## The coordinate frame (read this first)
45
+
46
+ All lengths are millimetres. Origin = TOP-LEFT corner of the board outline, x to the right
47
+ along `widthMm`, y DOWN along `heightMm` - exactly like a top-down photo on screen. Every
48
+ position is a CENTRE (pad centre, first header pin centre). Pad `wMm` is along x, `hMm`
49
+ along y. Things may stick out past the outline (leads, USB sockets).
50
+
51
+ ## Workflow
52
+
53
+ ### 1. Gather sources with your own tools (fritzkit never goes online)
54
+
55
+ Make the part folder first (`fritzkit new <slug> --title "..."`; run `fritzkit init` once
56
+ if the workspace does not exist), then collect into `<workspace>/<slug>/assets/`:
57
+
58
+ - **Amazon listing** (in this order):
59
+ 1. Fetch the MOBILE form with a plain GET: `curl -sSL https://www.amazon.com/gp/aw/d/<ASIN> -o
60
+ assets/listing.html` (the 10-character ASIN from the `/dp/<ASIN>` link). No header spoofing,
61
+ no cookies, no scripting around anything. This returns the full page including the
62
+ gallery. The `/dp/` desktop form tends to return a captcha. NEVER solve or script around
63
+ a captcha: if any fetch returns a captcha or "robot check" page, stop, use the browser
64
+ tool instead, or ask the user to save the product images into `assets/`.
65
+ 2. Extract the full-resolution gallery URLs from the HTML:
66
+ `grep -o '"hiRes":"[^"]*"' assets/listing.html | sort -u` (fall back to `"large":"..."`).
67
+ 3. Download them from `m.media-amazon.com` with a plain GET (`curl -sSL -o assets/img1.jpg
68
+ <url>`); if a URL has a size token such as `._AC_SX679_`, drop it for the full file.
69
+ 4. Only if the HTML is unusable: the web_fetch MCP (its markdown mode may return only the
70
+ buy box and its HTML mode may truncate before the gallery) or Claude in Chrome, for the
71
+ title, bullets and description text.
72
+
73
+ Download at least: the orthographic TOP view (board flat, shot straight down, silkscreen
74
+ readable), any image with DIMENSION callouts (arrows with mm numbers), any pinout / wiring
75
+ diagram, and the bottom view if pads are on the back.
76
+
77
+ **Listings mix products.** Gallery images may show a DIFFERENT board than the title (a 3S
78
+ 40A board in a 2S 20A listing is real). Before measuring, read the model printed on the
79
+ silkscreen in the image and compare it with the title; prefer the image whose silkscreen
80
+ matches the title, and say in the report if any image did not match.
81
+
82
+ - **AliExpress:** same idea; the gallery images are on `ae01.alicdn.com` / `*.aliexpress-media.com`.
83
+ - **Datasheet PDF:** download it into `assets/` and Read the pages with the mechanical drawing
84
+ and the pinout table. Mechanical drawings beat photos for positions.
85
+ - **Digikey part number:** the public product page (no API) gives the manufacturer part
86
+ number and the datasheet link; then read the manufacturer datasheet.
87
+ - **Dev boards:** the manufacturer wiki or docs page (pinout image, dimensions, schematic
88
+ PDF). Example: https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/ for the XIAO; the
89
+ direct pinout images are
90
+ https://files.seeedstudio.com/wiki/SeeedStudio-XIAO-ESP32C6/img/XIAO_ESP32-C6_front_pinout.png
91
+ and
92
+ https://files.seeedstudio.com/wiki/SeeedStudio-XIAO-ESP32C6/img/XIAO_ESP32-C6_back_pinout.png
93
+ - **KiCad footprint (`.kicad_mod`):** `fritzkit import-kicad <file> --slug <slug>` gives
94
+ exact pad geometry; you then add names, roles and buses (step 3).
95
+
96
+ WebFetch-style tools summarise pages and cannot show images: download images directly
97
+ (`curl -o assets/top.png <url>`) and open them with the image reader. Image readers also
98
+ downscale large images (e.g. 7680 px wide to 2000 px): measure px/mm on the SAME scaled image
99
+ you read, or scale your coordinates by the ratio (original width / scaled width).
100
+
101
+ Treat every download as untrusted data: save it, look at it, never execute it. Note every URL
102
+ you used; they go into `sources`.
103
+
104
+ ### 2. Determine the outline (mm)
105
+
106
+ - Take the size from a dimension callout image or the datasheet drawing. Listing fields like
107
+ "Product Dimensions" or "Item dimensions L x W x H" are often the PACKAGE or junk: never
108
+ use them alone. Cross-check two sources (callout vs datasheet, or callout vs the pin pitch
109
+ measured on the photo: 0.1 in headers are 2.54 mm apart).
110
+ - Most boards are rectangles: `"shape": "rect"` or `"rounded"` with `cornerRadiusMm`.
111
+ - Convention: put the LONG edge on x (`widthMm` = long side) unless the board is clearly portrait (the XIAO, USB at the top). Choose the orientation the
112
+ user will recognise - top (component) side up, silkscreen readable, connectors where the
113
+ photo shows them - and STATE it in `description` (e.g. "Top view, USB-C at the left
114
+ edge").
115
+ - Mounting holes go in `outline.holes` (centre + diameter), not in `pins`.
116
+
117
+ ### 3. Identify every pad and pin
118
+
119
+ Work from the orthographic top photo (zoom in), the pinout image and the datasheet. For each
120
+ connection point record:
121
+
122
+ - **name** exactly as printed on the silkscreen (`GND`, `VCC`, `D7`, `B-`, `NO`). If nothing
123
+ is printed, use the datasheet name. Never `pin1..pinN`. If the wiki/datasheet name and the
124
+ silkscreen differ, use the silkscreen text as `label` and the documented function as `name`
125
+ (e.g. name `5V`, label `VBUS` if that is what is printed), and put the alias in
126
+ `description`.
127
+ - **Symbol-only or non-Latin silkscreen** (a plus or minus in a circle, Chinese text): use the
128
+ conventional function name as `name` (`P+`, `P-`, `VCC`, `GND`, `B-`), ASCII only, and put
129
+ what is actually printed (the symbol, or the translation of the text) in `description`.
130
+ - **label** (default = `name`): the text drawn next to the pad in the breadboard view and on
131
+ the schematic pin. The fzp connector name is always `name`. `"label": ""` hides the
132
+ breadboard label (see "Drawing order and labels").
133
+ - **role**: `power` (any supply), `gnd`, `io`, `analog`, `pwm`, `i2c`, `spi`, `uart`, `usb`,
134
+ `clock`, `reset`, `enable`, `nc`, `other` (relay contacts, motor phases).
135
+ - **description**: function and alternate functions (`D4 / SDA`, `charge input 5V`).
136
+ - **net sharing**: which pads are the same copper. Every set of shared pads becomes one
137
+ `buses` entry (e.g. all `GND` pads -> one `GND` bus). Bus pins ONLY when the copper is
138
+ visibly continuous, the pads carry the same net name on the datasheet, or the board
139
+ schematic says so. Do NOT bus pins just because their names look alike.
140
+ - **kind of connector** (see "Connector kinds" below): a regular 0.1 in row -> `headers`
141
+ (`male` pins, `female` sockets, `castellated` half-holes); a lone or irregular pad ->
142
+ `pins` (`type: "female"` for a through-hole solder pad or socket, `"male"` for a pin,
143
+ `"pad"` for an SMD or castellated pad with no hole); a flying wire -> `leads`.
144
+ - **pad geometry**: shape, `wMm`, `hMm`, and `holeMm` for through-hole pads (measure on the
145
+ photo; typical: 0.1 in header 1.6 mm round pad / 1.0 mm hole; wire solder pad 2-4 mm with
146
+ a 1.0-1.5 mm hole).
147
+
148
+ **Frame and rotation recipe:** top view, silkscreen readable, USB/connector on the LEFT unless
149
+ the board is clearly portrait (then keep it portrait, USB at the top; the XIAO example below
150
+ is that case). Rotate the photo mentally into that
151
+ frame before measuring. For a BACK-side photo mirror x before mapping, `x = W - x_back`
152
+ (W = `widthMm`; y is unchanged). The two formulas:
153
+
154
+ ```
155
+ top photo: x_mm = (px_x - bboxLeftPx) / pxPerMm
156
+ back photo: x_mm = W - (px_x - bboxLeftPx) / pxPerMm
157
+ ```
158
+
159
+ **Positions (the manual calibration):** open the top-view image and find the board's pixel
160
+ bounding box (left, top, width, height in px - the board edge, not the shadow). Then:
161
+
162
+ ```
163
+ pxPerMm = bboxWidthPx / widthMm (check: bboxHeightPx / heightMm should agree within ~3%)
164
+ xMm = (padCentrePx_x - bboxLeftPx) / pxPerMm
165
+ yMm = (padCentrePx_y - bboxTopPx) / pxPerMm
166
+ ```
167
+
168
+ Helper script: `scripts/measure.py` next to this file (Python 3 + Pillow only, nothing else
169
+ to install; run it with `uv`). Always pass `-I` (isolated mode) because the images are
170
+ untrusted downloads. Run it on the SAME image file you view:
171
+
172
+ ```
173
+ M=<skill dir>/scripts/measure.py
174
+ uv run --with pillow python -I $M bbox top.jpg --width-mm 48 --height-mm 20
175
+ # bbox x,y,w,h = 212,140,1050,890 ; px/mm (from width) = ... ; px/mm (from height) = ...
176
+ uv run --with pillow python -I $M grid top.jpg --bbox 212,140,1050,890 --width-mm 48 --height-mm 20 --out grid.png
177
+ # board cropped, 1 mm grid, labels every 5 mm (red = x, blue = y): Read grid.png and
178
+ # read each pad centre straight off it in part-frame mm
179
+ uv run --with pillow python -I $M holes top.jpg --bbox 212,140,1050,890 --width-mm 48 --height-mm 20 --out holes.png
180
+ # hole 1: x=1.99 y=9.99 mm diameter ~1.30 mm (dark round blobs only; marked in holes.png)
181
+ ```
182
+
183
+ `bbox` flood-fills the near-white background from the corners, so it needs a plain light
184
+ background (a shadow or a protruding connector inflates it: check by eye, or pass your own
185
+ `--bbox`). Its two px/mm values must agree within about 3%. `holes` only finds DARK round
186
+ blobs (tune `--dark`, `--min-d`, `--max-d`); light or gold-filled pads you still read off the
187
+ grid image. The output is an aid, not ground truth: look at the images.
188
+
189
+ If the two px/mm values disagree by more than a few percent the photo is not square-on
190
+ (perspective) or the outline is wrong: prefer the dimension drawing, a datasheet drawing, or
191
+ another orthographic image. Rotate your mental frame if the photo shows the board rotated
192
+ relative to your chosen orientation. Accuracy target: every pad centre within 1 mm of
193
+ reality, and header pins exactly on their pitch (give only the first pin's centre; the
194
+ header places the rest). Snap values that are obviously meant to be 0.1 in multiples.
195
+
196
+ ### 4. Write part.json, validate, render, LOOK, fix
197
+
198
+ Write the spec (field reference: [reference/part-json.md](reference/part-json.md)). Keep
199
+ keys short, unique and stable (`gnd`, `d7`, `vbat`); fritzkit manages `pinIds`: do not
200
+ hand-edit them for a part that has ever been exported or installed (connector ids must stay
201
+ stable). For a brand-new part you may delete the `pinIds` object before the first export to
202
+ close gaps. The gap warning (`connector-numbering`) is only a warning.
203
+
204
+ Full worked example, the Seeed Studio XIAO ESP32-C6 (17.8 x 21 mm, two columns of 7
205
+ castellated 0.1 in pads along the long edges, a 1 x 2 BAT+/BAT- castellated header on the
206
+ back, a bus for the GND pads that share a net). This is the PORTRAIT case of the frame recipe:
207
+ top view, USB-C at the TOP short edge, so `widthMm` is the short side. It is a copy of the
208
+ checked, measured golden part in `examples/xiao-esp32-c6/` (part.json plus a README with the
209
+ source of every number), minus `pinIds` and two of the sources:
210
+
211
+ ```json
212
+ {
213
+ "$schema": "https://raw.githubusercontent.com/pmaxhogan/fritzkit/main/src/core/schema/part.schema.json",
214
+ "slug": "xiao-esp32-c6",
215
+ "title": "Seeed Studio XIAO ESP32-C6",
216
+ "version": 1,
217
+ "label": "U",
218
+ "tags": ["xiao", "esp32-c6", "seeed", "castellated", "wifi 6", "zigbee", "thread"],
219
+ "description": "Seeed Studio XIAO ESP32-C6, 21 x 17.8 mm. Top (component) view, USB-C at the TOP short edge, the two columns of 7 castellated 0.1 in pads along the long edges: D0..D6 down the left edge, 5V/GND/3V3/D10/D9/D8/D7 down the right edge. BAT+ and BAT- are pads on the BACK, drawn where they sit seen through the board. BAT- and the side GND pad are the same net (Seeed's KiCad symbol names that pad GND), hence the GND bus.",
220
+ "sources": [
221
+ "https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/",
222
+ "https://files.seeedstudio.com/wiki/XIAO-KiCad-Library/New_XIAO_Series_Footprints.zip"
223
+ ],
224
+ "properties": {
225
+ "mpn": "XIAO ESP32C6",
226
+ "type": "module",
227
+ "chip": "ESP32-C6"
228
+ },
229
+ "outline": {
230
+ "widthMm": 17.8,
231
+ "heightMm": 21,
232
+ "shape": "rounded",
233
+ "cornerRadiusMm": 1.5
234
+ },
235
+ "artwork": {
236
+ "style": "vector",
237
+ "boardColor": "#1A1A1A",
238
+ "components": [
239
+ {
240
+ "kind": "rect",
241
+ "xMm": 4.4,
242
+ "yMm": -1.5,
243
+ "wMm": 9,
244
+ "hMm": 7.4,
245
+ "color": "#B8B8B8",
246
+ "rxMm": 0.8,
247
+ "note": "USB-C receptacle, overhangs the top edge by 1.5 mm (KiCad footprint silk)"
248
+ },
249
+ {
250
+ "kind": "rect",
251
+ "xMm": 3,
252
+ "yMm": 6.8,
253
+ "wMm": 11.8,
254
+ "hMm": 10.4,
255
+ "color": "#6E6E6E",
256
+ "rxMm": 0.4,
257
+ "note": "ESP32-C6 RF shield can"
258
+ },
259
+ {
260
+ "kind": "text",
261
+ "text": "XIAO",
262
+ "xMm": 8.9,
263
+ "yMm": 9.8,
264
+ "sizeMm": 1.8,
265
+ "color": "#E8E8E8"
266
+ },
267
+ {
268
+ "kind": "text",
269
+ "text": "ESP32-C6",
270
+ "xMm": 8.9,
271
+ "yMm": 12,
272
+ "sizeMm": 1.4,
273
+ "color": "#E8E8E8"
274
+ },
275
+ {
276
+ "kind": "rect",
277
+ "xMm": 3.2,
278
+ "yMm": 18,
279
+ "wMm": 2.4,
280
+ "hMm": 2.4,
281
+ "color": "#C8A040",
282
+ "rxMm": 0.3,
283
+ "note": "U.FL external antenna connector"
284
+ },
285
+ {
286
+ "kind": "rect",
287
+ "xMm": 8.7,
288
+ "yMm": 18.5,
289
+ "wMm": 6.4,
290
+ "hMm": 1.9,
291
+ "color": "#B0B0B0",
292
+ "rxMm": 0.2,
293
+ "note": "ceramic chip antenna"
294
+ }
295
+ ]
296
+ },
297
+ "headers": [
298
+ {
299
+ "key": "left",
300
+ "kind": "castellated",
301
+ "cols": 7,
302
+ "orientation": "vertical",
303
+ "xMm": 1.28,
304
+ "yMm": 2.88,
305
+ "pins": [
306
+ {
307
+ "key": "d0",
308
+ "name": "D0",
309
+ "role": "analog",
310
+ "description": "D0 / A0: GPIO0 (LP_GPIO0), ADC"
311
+ },
312
+ {
313
+ "key": "d1",
314
+ "name": "D1",
315
+ "role": "analog",
316
+ "description": "D1 / A1: GPIO1 (LP_GPIO1), ADC"
317
+ },
318
+ {
319
+ "key": "d2",
320
+ "name": "D2",
321
+ "role": "analog",
322
+ "description": "D2 / A2: GPIO2 (LP_GPIO2), ADC"
323
+ },
324
+ {
325
+ "key": "d3",
326
+ "name": "D3",
327
+ "role": "io",
328
+ "description": "D3: GPIO21 (SDIO_DATA1)"
329
+ },
330
+ {
331
+ "key": "d4",
332
+ "name": "D4",
333
+ "role": "i2c",
334
+ "description": "D4 / SDA: GPIO22, I2C data (SDIO_DATA2)"
335
+ },
336
+ {
337
+ "key": "d5",
338
+ "name": "D5",
339
+ "role": "i2c",
340
+ "description": "D5 / SCL: GPIO23, I2C clock (SDIO_DATA3)"
341
+ },
342
+ {
343
+ "key": "d6",
344
+ "name": "D6",
345
+ "role": "uart",
346
+ "description": "D6 / TX: GPIO16, UART transmit"
347
+ }
348
+ ]
349
+ },
350
+ {
351
+ "key": "right",
352
+ "kind": "castellated",
353
+ "cols": 7,
354
+ "orientation": "vertical",
355
+ "xMm": 16.52,
356
+ "yMm": 2.88,
357
+ "pins": [
358
+ {
359
+ "key": "5v",
360
+ "name": "5V",
361
+ "role": "power",
362
+ "description": "5V (VBUS): USB-C 5 V in/out. No voltage here when running from the battery"
363
+ },
364
+ {
365
+ "key": "gnd",
366
+ "name": "GND",
367
+ "role": "gnd",
368
+ "description": "Ground"
369
+ },
370
+ {
371
+ "key": "3v3",
372
+ "name": "3V3",
373
+ "role": "power",
374
+ "description": "3V3 (3V3_OUT): 3.3 V regulator output"
375
+ },
376
+ {
377
+ "key": "d10",
378
+ "name": "D10",
379
+ "role": "spi",
380
+ "description": "D10 / MOSI: GPIO18, SPI data out"
381
+ },
382
+ {
383
+ "key": "d9",
384
+ "name": "D9",
385
+ "role": "spi",
386
+ "description": "D9 / MISO: GPIO20, SPI data in"
387
+ },
388
+ {
389
+ "key": "d8",
390
+ "name": "D8",
391
+ "role": "spi",
392
+ "description": "D8 / SCK: GPIO19, SPI clock"
393
+ },
394
+ {
395
+ "key": "d7",
396
+ "name": "D7",
397
+ "role": "uart",
398
+ "description": "D7 / RX: GPIO17, UART receive"
399
+ }
400
+ ]
401
+ },
402
+ {
403
+ "key": "bat",
404
+ "kind": "castellated",
405
+ "cols": 2,
406
+ "orientation": "horizontal",
407
+ "xMm": 7.11,
408
+ "yMm": 16.03,
409
+ "pad": {
410
+ "shape": "rect",
411
+ "wMm": 1.1,
412
+ "hMm": 2.5,
413
+ "smd": true
414
+ },
415
+ "pins": [
416
+ {
417
+ "key": "bat+",
418
+ "name": "BAT+",
419
+ "label": "B+",
420
+ "role": "power",
421
+ "description": "Li-ion battery plus (VBAT), pad on the BACK; charged by the on-board SGM40567 charger"
422
+ },
423
+ {
424
+ "key": "bat-",
425
+ "name": "BAT-",
426
+ "label": "B-",
427
+ "role": "gnd",
428
+ "description": "Battery minus, pad on the BACK. Same net as GND"
429
+ }
430
+ ]
431
+ }
432
+ ],
433
+ "buses": [
434
+ {
435
+ "id": "GND",
436
+ "pins": ["gnd", "bat-"]
437
+ }
438
+ ]
439
+ }
440
+ ```
441
+
442
+ Derivation: 7 pins at 2.54 mm span 15.24 mm; the columns are 15.24 mm apart, centred on the
443
+ 17.8 mm width: x = (17.8 - 15.24) / 2 = 1.28 and 16.52. The first pad is at y = 2.88 (from
444
+ Seeed's KiCad footprint), pitch 2.54. BAT+ (7.11, 16.03) and BAT- (9.65, 16.03) are measured
445
+ from the same footprint, not estimated. Read the wiki yourself for the real pin functions and
446
+ silkscreen (apply the name/label rule).
447
+
448
+ What this example deliberately leaves out or hedges:
449
+
450
+ - The real board also has back-side test pads (MTDO, MTDI, MTCK, MTMS, EN, BOOT, a second GND
451
+ and 3V3). They are omitted; if you add them, give them `"label": ""` to avoid clutter.
452
+ - BAT- is bused with the side GND pad because Seeed's KiCad symbol names that pad GND; the
453
+ wiki text itself does not say it. Without such evidence, leave a pin unbused until a
454
+ continuity check.
455
+ - The battery pads are on the BACK but drawn where they sit seen through the board, as a
456
+ castellated `bat` header with a custom `pad`.
457
+
458
+ Then loop until it matches the photo:
459
+
460
+ 1. `fritzkit validate <slug>` (MCP `fritzkit_validate`). Fix every ERROR. Schema errors name
461
+ the JSON path (`headers.0.pins: header.pins must have exactly rows*cols entries`). The
462
+ first run also writes `pinIds` into part.json - that is expected.
463
+ 2. `fritzkit render <slug> --png` (MCP `fritzkit_render`, view `breadboard`, PNG). The CLI
464
+ writes into `<workspace>/<slug>/build/` and prints the paths; Read the PNG.
465
+ 3. LOOK at the breadboard PNG next to the top-view photo, pad by pad: same count, same
466
+ order, same names, same side, same spacing, outline proportions right, nothing labelled
467
+ on the wrong pad, USB/connector on the right edge. Also check `--view schematic` (power
468
+ top, GND bottom, no duplicated names that should be one bus) and `--view pcb` (pad sizes
469
+ and holes plausible).
470
+ 4. Fix part.json and repeat. Typical fixes: x and y swapped, a row mirrored (pin order
471
+ reversed), first-pin centre off by half a pitch, orientation `horizontal` vs `vertical`.
472
+
473
+ Optional: `fritzkit validate <slug> --checker` also runs Fritzing's own part checker (needs
474
+ `uv`; see Troubleshooting). In the current build the plain text output may show only
475
+ `No issues found.` without a checker line; use `--json`, whose `checker` object (`ran`,
476
+ `summary` such as `fzp checker: ran (0 errors, 0 warnings)`, `errors`, `warnings`) is the
477
+ reliable way to confirm the checker actually ran.
478
+
479
+ `validate` and `render` also accept a path to a `.fzpz`. `validate <file.fzpz>` checks the
480
+ package itself (zip entries, fzp XML, layer ids, connector refs, svg roots, module id) but
481
+ not your spec (no schema or bus checks); `render <file.fzpz>` writes that package's SVGs.
482
+
483
+ `render` writes `<view>.svg` for each view (breadboard, schematic, pcb, icon) into
484
+ `<workspace>/<slug>/build/` (or `--out <dir>`); `--png` ALSO writes `<view>.png` next to each
485
+ SVG. Options: `--view <v>`, `--out <dir>`, `--png`. It prints the output paths.
486
+ `--view <breadboard|schematic|pcb|icon>` restricts the run to that ONE view (default `all`), so
487
+ you only get that view's files: use it for fast re-checks while iterating. If it ever prints
488
+ paths for views you did not ask for, ignore the extras and report it.
489
+
490
+ ### 5. Export and install
491
+
492
+ - `fritzkit export <slug>` writes `<workspace>/<slug>/<moduleId>.fzpz` and prints its path.
493
+ It refuses while the validator reports errors; fix them. `--force` overrides that gate -
494
+ do not use it to ship a broken part.
495
+ - `fritzkit install <slug>` (or `export <slug> --install`) puts it into Fritzing's user parts
496
+ folder so it shows up under **My Parts**. **Fritzing must be closed**: Fritzing reads its
497
+ parts folder only at startup, so fritzkit refuses (exit code 3) while Fritzing.exe runs and
498
+ says so (the `.fzpz` is still written and its path printed). Ask the user to quit
499
+ Fritzing, then retry.
500
+ - Alternative: hand the `.fzpz` path to the user - in Fritzing, File > Open on the `.fzpz`
501
+ imports it into My Parts.
502
+ - Every export of changed content gets a NEW moduleId (Fritzing refuses duplicate ids), so
503
+ repeated installs pile up versions. `fritzkit prune <slug>` lists the older installed
504
+ versions of the part's family (a dry run); `fritzkit prune <slug> --yes` deletes them,
505
+ keeping the newest (`--keep <moduleId>` keeps a specific one instead). Run it with
506
+ Fritzing closed too.
507
+
508
+ ### 6. Report to the user
509
+
510
+ Say plainly:
511
+
512
+ - which sources you used (URLs, which image gave the dimensions, which gave the pinout),
513
+ - what was MEASURED (dimension callout, datasheet drawing) vs ESTIMATED (pad positions read
514
+ off a photo, back-side pads, pad sizes),
515
+ - which nets you could not confirm and the user should check with a multimeter (continuity
516
+ between pads you bused or deliberately did not bus), and which dimensions to check with
517
+ calipers before fabricating a PCB from the footprint,
518
+ - the `.fzpz` path, whether it was installed, and the orientation of the drawing.
519
+
520
+ ## Drawing order and labels
521
+
522
+ - Breadboard layer order: silkscreen and `components` are drawn UNDER header bodies, pads and
523
+ pin labels. Put your own text and shapes where pads and labels are not.
524
+ - Pin labels use `label` (default `name`). `"label": ""` skips the breadboard label for that
525
+ pin (pads and headers). In the schematic an empty label is NOT replaced by `name`: the pin
526
+ is drawn with an empty label (the pin still exists; the fzp connector name is still `name`).
527
+ - Schematic `layout` `by-role` (the only one): ONE box; pins are grouped by role onto sides
528
+ (power/usb/enable/reset left, gnd bottom, io-like right), in spec order within a role; every
529
+ bused GND pin still gets its own pin. `schematic.sides` overrides the side per role
530
+ (`{"left": ["power", "gnd"], ...}`). There is no explicit per-pin ordering hint yet; reorder
531
+ the pins in the spec to change order within a role.
532
+
533
+ ## Reading the PCB preview
534
+
535
+ Through-hole pads are drawn as a copper RING (an SVG `<circle>` with `fill="none"`, the ring
536
+ being its stroke), so the drill is the empty centre. Oblong or rectangular pads also get a
537
+ solid copper rect UNDER the ring, which makes the PNG look like a solid block with no visible
538
+ hole. To read the drill from `pcb.svg`: units are mils (1/1000 in; 1 mm = 39.37 mil); the
539
+ circle has `r` (the stroke centre line) and `stroke-width` (the ring width), so
540
+ `drill diameter = 2 * (r - stroke-width / 2)` and `copper diameter = 2 * (r + stroke-width / 2)`.
541
+ A pad's copper is clamped to at least the drill plus the minimum ring, and the drill to a
542
+ minimum size, so very small `holeMm` values are enlarged. In a rectangular pad the ring spans
543
+ the SHORT side; the rest is the rect.
544
+
545
+ ## Pin labels: placement and suppression
546
+
547
+ There is no field to move a label: breadboard labels sit at a fixed offset next to their pad.
548
+ If a label collides with artwork or another label, set `"label": ""` on that pin (breadboard
549
+ only; the schematic pin is then drawn unlabelled and the connector name is still `name`) or
550
+ move your own silkscreen/components, which are drawn UNDER pads and labels (see "Drawing order
551
+ and labels"). Layer order, bottom to top: board, `silkscreen` and `components`, header bodies,
552
+ pads, pin labels.
553
+
554
+ ## Environment notes
555
+
556
+ - Old `.fzpz` exports stay in the part folder (each material change is a new moduleId):
557
+ delete stale ones or use the path `export` printed.
558
+
559
+ ## Common mistakes
560
+
561
+ Surveyed community Fritzing parts mostly fail on the same few things. fritzkit makes the
562
+ format ones impossible (layer ids that do not match the fzp, a schematic off the 0.1 in grid,
563
+ unitless SVG sizes read at the wrong dpi, Arial or other unsupported fonts, `<tspan>` and CSS
564
+ styles, multi-MB embedded photos, a PCB view missing `copper1`). What it cannot fix is the
565
+ content, which is your job:
566
+
567
+ - **No buses for shared nets.** The most common defect. Several GND pads must be one bus or
568
+ Fritzing treats them as unconnected. Equally wrong: busing pins that only share a name
569
+ pattern.
570
+ - **Generic names** (`pin1`, `P3`) instead of the silkscreen text. Users wire by name.
571
+ - **Wrong roles**: a supply pin left as `io` lands on the wrong side of the schematic.
572
+ - **Mirrored or rotated pin order**: the render shows names in a different order than the
573
+ photo. Compare pad by pad.
574
+ - **Outline from the listing's "Product Dimensions"** (often the box) instead of a callout.
575
+ - **Header modelled as loose pins** with drifting positions: use `headers` so the pitch is
576
+ exact and the part seats in a breadboard.
577
+ - **Editing `pinIds`** of an exported part, or reusing a key for a different pin: breaks wire survival on swap.
578
+
579
+ ## Artwork styles
580
+
581
+ - `vector` (default): Fritzing house style - board colour, component shapes, silkscreen
582
+ text, real-looking headers and pads. Crisp at every zoom, small, editable in Fritzing's
583
+ Parts Editor, accepted everywhere. Use it unless the user asks for a photo.
584
+ - `photo` (opt-in): the listing photo embedded in the breadboard view with vector pads on
585
+ top. Needs `artwork.photo.src` pointing into `assets/`, usually with
586
+ `removeBackground: true` (keys out a white listing background and trims to the board),
587
+ plus `crop` / `rotate` (clockwise, after the crop) to match the spec orientation. The
588
+ photo must be orthographic and the trimmed board must fill the outline exactly. Caveats:
589
+ Fritzing's Parts Editor drops embedded rasters if the user edits the part, it pixelates
590
+ when zoomed, and Gerber export strips images (PCB view is always vector anyway). Use it
591
+ when recognisability matters more than editability (a busy board that vector shapes
592
+ cannot convey).
593
+
594
+ ## Connector kinds
595
+
596
+ Through-hole solder pads at arbitrary positions (`pins`, `type: "female"`):
597
+
598
+ ```json
599
+ {
600
+ "key": "vin",
601
+ "name": "VIN",
602
+ "role": "power",
603
+ "xMm": 3.2,
604
+ "yMm": 4.5,
605
+ "pad": { "shape": "rect", "wMm": 2.5, "hMm": 2.5, "holeMm": 1.2 }
606
+ }
607
+ ```
608
+
609
+ 0.1 in header (`headers`, `kind` `male` or `female`; position = FIRST pin):
610
+
611
+ ```json
612
+ {
613
+ "key": "j1",
614
+ "kind": "male",
615
+ "cols": 4,
616
+ "xMm": 2.54,
617
+ "yMm": 1.27,
618
+ "pins": [
619
+ { "key": "vcc", "name": "VCC", "role": "power" },
620
+ { "key": "gnd", "name": "GND", "role": "gnd" },
621
+ { "key": "scl", "name": "SCL", "role": "i2c" },
622
+ { "key": "sda", "name": "SDA", "role": "i2c" }
623
+ ]
624
+ }
625
+ ```
626
+
627
+ Flying wire leads (`leads`; the connector is the free end):
628
+
629
+ ```json
630
+ {
631
+ "key": "vbat",
632
+ "name": "VBAT",
633
+ "role": "power",
634
+ "color": "#D02020",
635
+ "gaugeMm": 2.2,
636
+ "fromXMm": 0,
637
+ "fromYMm": 6,
638
+ "direction": "left",
639
+ "lengthMm": 40
640
+ }
641
+ ```
642
+
643
+ Castellated / SMD edge pads (`headers` with `kind: "castellated"`, or a `pins` entry with
644
+ `type: "pad"` and no hole). For a vertical column on a side edge, rotate the pad yourself:
645
+
646
+ ```json
647
+ {
648
+ "key": "left",
649
+ "kind": "castellated",
650
+ "cols": 7,
651
+ "orientation": "vertical",
652
+ "xMm": 1.0,
653
+ "yMm": 2.88,
654
+ "pad": { "shape": "rect", "wMm": 2.4, "hMm": 1.6, "smd": true },
655
+ "pins": ["...7 header pins..."]
656
+ }
657
+ ```
658
+
659
+ ## Swapping versions in Fritzing
660
+
661
+ All exports of one slug share a `family` (the slug) and differ in `variant`
662
+ (`v<version> <hash8>`). In Fritzing, click a placed copy, then pick the newer variant in the
663
+ Inspector's variant drop-down: Fritzing swaps the part in place. Connector ids come from the
664
+ persistent `pinIds` map, so every pin that still exists keeps its id and its wires stay
665
+ attached. Bump `version` when the change is material so the user can tell versions apart.
666
+
667
+ ## Troubleshooting
668
+
669
+ Validator issue codes (`fritzkit validate` prints `ERROR [code] <where> message (fix: ...)`):
670
+
671
+ | code | what to change |
672
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
673
+ | `spec-schema` | part.json does not match the schema: fix the field at the printed path (`fritzkit schema` shows the format) |
674
+ | `buses` | a bus names an unknown key, a pin is in two buses (merge them), or a bus has < 2 members (remove it) |
675
+ | `connector-numbering` | a WARNING about gaps after removing pins is expected and harmless; never renumber `pinIds` to "fix" it |
676
+ | `layer-ids`, `connector-refs`, `svg-root`, `svg-style`, `pcb-layers`, `copper-only`, `zip-entries`, `module-id`, `required-tags`, `fzp-xml`, `svg-xml` | generated-output problems: these should not happen from a valid spec. Re-run with `--checker`, and report the spec and the message to the user as a fritzkit bug |
677
+
678
+ Other problems:
679
+
680
+ - Duplicate pin key, or a bus key that does not exist: the message names it; keys are case
681
+ sensitive and unique across pins, headers and leads.
682
+ - `Fritzing is running` / exit code 3 on install or prune: ask the user to quit Fritzing
683
+ (File > Quit), then retry.
684
+ - `--checker` says uv is missing: install uv (https://docs.astral.sh/uv/) or skip the
685
+ checker; `fritzkit doctor` lists what is missing (Node version, uv, Fritzing install, the
686
+ user parts folder).
687
+ - The part imports but has no artwork, or the part does not appear under My Parts: run
688
+ `fritzkit doctor`, re-export, check Fritzing was restarted after install.
689
+ - Render looks right but pads are slightly off: re-measure px/mm on a straighter photo or
690
+ use the datasheet drawing; header pins only need the first pin right.