fritzkit 0.0.0-stage → 0.1.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 (328) 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/__pycache__/__init__.cpython-313.pyc +0 -0
  190. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/check_missing_leg_ids.cpython-313.pyc +0 -0
  191. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/connector_numbering.cpython-313.pyc +0 -0
  192. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checker_runner.cpython-313.pyc +0 -0
  193. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checkers.cpython-313.pyc +0 -0
  194. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_svg_checkers.cpython-313.pyc +0 -0
  195. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_utils.cpython-313.pyc +0 -0
  196. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_checkers.cpython-313.pyc +0 -0
  197. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_utils.cpython-313.pyc +0 -0
  198. package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
  199. package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
  200. package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
  201. package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
  202. package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
  203. package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
  204. package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
  205. package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
  206. package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
  207. package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
  208. package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
  209. package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
  210. package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
  211. package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
  212. package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
  213. package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
  214. package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
  215. package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
  216. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
  217. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
  218. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
  219. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
  220. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
  221. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
  222. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
  223. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
  224. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
  225. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
  226. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
  227. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
  228. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
  229. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
  230. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
  231. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
  232. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
  233. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
  234. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
  235. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
  236. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
  237. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
  238. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
  239. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
  240. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
  241. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
  242. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
  243. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
  244. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
  245. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
  246. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
  247. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
  248. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
  249. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
  250. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
  251. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
  252. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
  253. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
  254. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
  255. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
  256. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
  257. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
  258. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
  259. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
  260. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
  261. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
  262. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
  263. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
  264. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
  265. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
  266. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
  267. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
  268. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
  269. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
  270. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
  271. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
  272. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
  273. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
  274. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
  275. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
  276. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
  277. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
  278. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
  279. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
  280. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
  281. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
  282. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
  283. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
  284. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
  285. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
  286. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
  287. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
  288. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
  289. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
  290. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
  291. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
  292. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
  293. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
  294. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
  295. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
  296. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
  297. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
  298. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
  299. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
  300. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
  301. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
  302. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
  303. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
  304. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
  305. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
  306. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
  307. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
  308. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
  309. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
  310. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
  311. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
  312. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
  313. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
  314. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
  315. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
  316. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
  317. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
  318. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
  319. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
  320. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
  321. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
  322. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
  323. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
  324. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
  325. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
  326. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
  327. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
  328. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_mismatch_schematic.svg +6 -0
@@ -0,0 +1,275 @@
1
+ # fritzkit worked examples
2
+
3
+ Each example is a complete, valid `part.json` that compiles to a checker-clean `.fzpz`.
4
+ Field reference: [part-json.md](part-json.md). The XIAO ESP32-C6 example (headers, castellated
5
+ pads, a bus; a portrait board, USB at the top) is in [../SKILL.md](../SKILL.md); the second worked example, a pads-only board
6
+ measured from a listing photo, is "Pads-only board measured from a photo" below.
7
+
8
+ ## Brushless ESC with wire leads
9
+
10
+ A board with no pins at all, only flying wires: battery leads on one side, three motor
11
+ phases on the other, a thin servo cable underneath. Each wire is a `leads` entry; the
12
+ connector is the free end, which plugs into a breadboard. The two ground wires (thick
13
+ battery minus and the servo cable's ground) are the same net, so they are one bus. The
14
+ motor phases are NOT bused: they are three separate outputs.
15
+
16
+ ```json
17
+ {
18
+ "slug": "esc-30a-example",
19
+ "title": "30A brushless ESC (wire leads)",
20
+ "description": "Top view. Battery leads leave the left edge, motor phases the right edge, servo cable the bottom edge.",
21
+ "label": "A",
22
+ "outline": { "widthMm": 45, "heightMm": 24, "shape": "rounded", "cornerRadiusMm": 2 },
23
+ "artwork": {
24
+ "boardColor": "#1F4FA0",
25
+ "silkscreen": [{ "kind": "text", "text": "30A ESC", "xMm": 22.5, "yMm": 12, "sizeMm": 2.5 }]
26
+ },
27
+ "leads": [
28
+ {
29
+ "key": "vbat",
30
+ "name": "VBAT",
31
+ "role": "power",
32
+ "color": "#D02020",
33
+ "gaugeMm": 2.2,
34
+ "fromXMm": 0,
35
+ "fromYMm": 6,
36
+ "direction": "left",
37
+ "lengthMm": 40
38
+ },
39
+ {
40
+ "key": "gnd",
41
+ "name": "GND",
42
+ "role": "gnd",
43
+ "color": "#000000",
44
+ "gaugeMm": 2.2,
45
+ "fromXMm": 0,
46
+ "fromYMm": 18,
47
+ "direction": "left",
48
+ "lengthMm": 40
49
+ },
50
+ {
51
+ "key": "ma",
52
+ "name": "A",
53
+ "role": "other",
54
+ "color": "#202020",
55
+ "fromXMm": 45,
56
+ "fromYMm": 5,
57
+ "direction": "right",
58
+ "lengthMm": 30,
59
+ "description": "motor phase A"
60
+ },
61
+ {
62
+ "key": "mb",
63
+ "name": "B",
64
+ "role": "other",
65
+ "color": "#202020",
66
+ "fromXMm": 45,
67
+ "fromYMm": 12,
68
+ "direction": "right",
69
+ "lengthMm": 30,
70
+ "description": "motor phase B"
71
+ },
72
+ {
73
+ "key": "mc",
74
+ "name": "C",
75
+ "role": "other",
76
+ "color": "#202020",
77
+ "fromXMm": 45,
78
+ "fromYMm": 19,
79
+ "direction": "right",
80
+ "lengthMm": 30,
81
+ "description": "motor phase C"
82
+ },
83
+ {
84
+ "key": "sig",
85
+ "name": "SIG",
86
+ "role": "pwm",
87
+ "color": "#F0F0F0",
88
+ "gaugeMm": 0.8,
89
+ "fromXMm": 10,
90
+ "fromYMm": 24,
91
+ "direction": "down",
92
+ "lengthMm": 30,
93
+ "description": "servo PWM input"
94
+ },
95
+ {
96
+ "key": "sgnd",
97
+ "name": "SIG GND",
98
+ "label": "-",
99
+ "role": "gnd",
100
+ "color": "#5A3A1A",
101
+ "gaugeMm": 0.8,
102
+ "fromXMm": 12,
103
+ "fromYMm": 24,
104
+ "direction": "down",
105
+ "lengthMm": 30
106
+ }
107
+ ],
108
+ "buses": [{ "id": "GND", "pins": ["gnd", "sgnd"] }]
109
+ }
110
+ ```
111
+
112
+ Notes: if the ESC's servo cable also has a BEC `+5V` wire, add it as a third thin lead with
113
+ `role: "power"`; do not bus it with VBAT (a regulator sits between them).
114
+
115
+ ## 1-channel 5 V relay module
116
+
117
+ A 3-pin 0.1 in input header on the left (a `headers` entry, vertical column) and a 3-way
118
+ screw terminal on the right (three `pins` with big holes, `type: "female"`, because the
119
+ terminal pitch is 5 mm and the terminal accepts wires). Mounting holes go in
120
+ `outline.holes`.
121
+
122
+ ```json
123
+ {
124
+ "slug": "relay-1ch-5v-example",
125
+ "title": "1-channel 5V relay module",
126
+ "description": "Top view. Input header on the left edge, screw terminal on the right edge.",
127
+ "label": "K",
128
+ "outline": {
129
+ "widthMm": 50,
130
+ "heightMm": 26,
131
+ "shape": "rect",
132
+ "holes": [
133
+ { "xMm": 3, "yMm": 3, "dMm": 3 },
134
+ { "xMm": 47, "yMm": 3, "dMm": 3 }
135
+ ]
136
+ },
137
+ "artwork": {
138
+ "boardColor": "#1B4F9C",
139
+ "components": [
140
+ {
141
+ "kind": "rect",
142
+ "xMm": 16,
143
+ "yMm": 5,
144
+ "wMm": 19,
145
+ "hMm": 15.5,
146
+ "color": "#2060D0",
147
+ "note": "relay can"
148
+ }
149
+ ]
150
+ },
151
+ "headers": [
152
+ {
153
+ "key": "in",
154
+ "kind": "male",
155
+ "cols": 3,
156
+ "orientation": "vertical",
157
+ "xMm": 2.5,
158
+ "yMm": 10.46,
159
+ "pins": [
160
+ { "key": "vcc", "name": "VCC", "role": "power" },
161
+ { "key": "gnd", "name": "GND", "role": "gnd" },
162
+ { "key": "in1", "name": "IN", "role": "io", "description": "relay driver input" }
163
+ ]
164
+ }
165
+ ],
166
+ "pins": [
167
+ {
168
+ "key": "no",
169
+ "name": "NO",
170
+ "type": "female",
171
+ "role": "other",
172
+ "xMm": 46,
173
+ "yMm": 8,
174
+ "pad": { "shape": "round", "wMm": 3, "hMm": 3, "holeMm": 1.3 },
175
+ "description": "screw terminal: normally open"
176
+ },
177
+ {
178
+ "key": "com",
179
+ "name": "COM",
180
+ "type": "female",
181
+ "role": "other",
182
+ "xMm": 46,
183
+ "yMm": 13,
184
+ "pad": { "shape": "round", "wMm": 3, "hMm": 3, "holeMm": 1.3 },
185
+ "description": "screw terminal: common"
186
+ },
187
+ {
188
+ "key": "nc",
189
+ "name": "NC",
190
+ "type": "female",
191
+ "role": "other",
192
+ "xMm": 46,
193
+ "yMm": 18,
194
+ "pad": { "shape": "round", "wMm": 3, "hMm": 3, "holeMm": 1.3 },
195
+ "description": "screw terminal: normally closed"
196
+ }
197
+ ]
198
+ }
199
+ ```
200
+
201
+ Notes: NO, COM and NC are three different nets; no bus. Modules with an extra `JD-VCC`
202
+ jumper header get those pins as a second header; bus `VCC` with `JD-VCC` only if the jumper
203
+ is a fixed trace, not a removable jumper.
204
+
205
+ ## Pads-only board measured from a photo (HW-391 2S 20A balance board)
206
+
207
+ The full spec is [examples/hw391-2s-20a-bms/part.json](../../../examples/hw391-2s-20a-bms/part.json)
208
+ (see its [README](../../../examples/hw391-2s-20a-bms/README.md) for the measured-vs-golden
209
+ table). It was built by a fresh agent from an Amazon listing using only this skill and the
210
+ CLI. What it shows:
211
+
212
+ - No headers, no leads: five through-hole solder pads, each a `pins` entry with
213
+ `type: "female"` and a measured `pad` (`rect`, `wMm`, `hMm`, `holeMm`). Outline 48 x 20 mm
214
+ from the listing's dimension callout; pad centres from the orthographic top photo at a
215
+ known px/mm (the `measure.py` `bbox` and `grid` helpers), good to about 0.3 mm.
216
+ - Conventional names for symbol-only silkscreen: the charger/load pads are printed as a plus
217
+ or minus inside a circle, so `name` is `P+` / `P-` and the `description` says what is
218
+ printed. The cell pads keep their printed text (`0V`, `4.2V`, `8.4V`).
219
+ - An INFERRED bus: `8.4V` and `P+` look like one copper pour on the back photo, so
220
+ `"buses": [{ "id": "BPLUS", "pins": ["bplus", "pplus"] }]`. `0V` and `P-` are NOT bused (the
221
+ MOSFETs sit between them). Inferred nets belong in the report as "check with a meter".
222
+ - Position and size, abridged (all in mm, origin top-left, x right, y down):
223
+
224
+ ```json
225
+ {
226
+ "outline": { "widthMm": 48, "heightMm": 20, "shape": "rect" },
227
+ "pins": [
228
+ {
229
+ "key": "pplus",
230
+ "name": "P+",
231
+ "description": "Charge/discharge plus (silkscreen: plus in a circle); same copper as 8.4V",
232
+ "role": "power",
233
+ "type": "female",
234
+ "xMm": 7.55,
235
+ "yMm": 12.6,
236
+ "pad": { "shape": "rect", "wMm": 4.3, "hMm": 3.1, "holeMm": 1.9 }
237
+ },
238
+ {
239
+ "key": "bplus",
240
+ "name": "8.4V",
241
+ "role": "power",
242
+ "type": "female",
243
+ "xMm": 45.85,
244
+ "yMm": 17.4,
245
+ "pad": { "shape": "rect", "wMm": 4.3, "hMm": 3.1, "holeMm": 1.6 }
246
+ }
247
+ ],
248
+ "buses": [{ "id": "BPLUS", "pins": ["bplus", "pplus"] }]
249
+ }
250
+ ```
251
+
252
+ Lessons: verify the silkscreen model in the photo matches the listing title before
253
+ measuring; use a dimension callout for the outline and the photo only for pad positions;
254
+ the rectangular pads render as solid copper in the PCB preview, the drill is in the SVG
255
+ ring (see "Reading the PCB preview" in [../SKILL.md](../SKILL.md)).
256
+
257
+ ## Starting from a KiCad footprint
258
+
259
+ When a `.kicad_mod` exists (manufacturer download, SnapEDA, KiCad library):
260
+
261
+ ```
262
+ fritzkit import-kicad path/to/Module.kicad_mod --slug my-module
263
+ ```
264
+
265
+ This writes a part with exact pad positions and sizes (through-hole pads -> `type: "female"`
266
+ with `holeMm`; SMD pads -> `type: "pad"`, `smd: true`; non-plated holes -> `outline.holes`;
267
+ pads that share a KiCad number -> tied by a bus). The outline comes from the board edge,
268
+ courtyard, fab layer or the pad bounding box, in that order. Pads on a 2.54 mm grid stay as
269
+ individual pins (exact geometry), they are not converted to headers.
270
+
271
+ What you still do: replace the KiCad pad numbers in `name` with the real signal names from
272
+ the datasheet, set every `role`, add buses for pads that share a net but have different
273
+ KiCad numbers, set `title`, `description`, `sources` and artwork, then validate and render
274
+ as usual. Check the orientation: KiCad footprints are drawn from the top, y down, the same
275
+ frame as fritzkit.
@@ -0,0 +1,333 @@
1
+ <!-- Copy of docs/part-json.md, shipped with the skill so it works when the skill folder is
2
+ copied on its own. Edit docs/part-json.md and copy it here; keep the two identical
3
+ except the relative links in "Worked examples". -->
4
+
5
+ # part.json reference
6
+
7
+ Every fritzkit part is one folder: `part.json` plus an optional `assets/` folder. This page
8
+ documents every field of `part.json`. The source of truth is the zod schema in
9
+ `src/core/schema/part.ts`; the published JSON Schema (`fritzkit schema`, or
10
+ `src/core/schema/part.schema.json`) is generated from it. If this page and the schema ever
11
+ disagree, the schema wins.
12
+
13
+ ```
14
+ <workspace>/<slug>/part.json the spec (you write this)
15
+ <workspace>/<slug>/assets/... photos, datasheet crops (artwork.photo.src points here)
16
+ <workspace>/<slug>/build/... render output (never read back)
17
+ <workspace>/<slug>/<moduleId>.fzpz export output (never read back)
18
+ ```
19
+
20
+ The workspace defaults to `~/fritzkit/parts`; override it with `--workspace <dir>` or the
21
+ `FRITZKIT_WORKSPACE` environment variable. Only `part.json` and `assets/` feed the
22
+ compiler.
23
+
24
+ ## Units and the coordinate frame
25
+
26
+ - **Every length is in millimetres** (fields end in `Mm`). fritzkit converts to mils when it
27
+ writes SVG; you never deal with inches, mils or pixels in the spec.
28
+ - **Origin = the top-left corner of the outline.** x grows to the RIGHT along `widthMm`,
29
+ y grows DOWN along `heightMm`. This is the orientation of a top-down photo of the board on
30
+ screen, so a pixel in an orthographic photo maps straight to mm:
31
+ `xMm = (px - boardLeftPx) / pxPerMm`, `yMm = (py - boardTopPx) / pxPerMm`.
32
+ - **Positions are centres.** A pin's `xMm`/`yMm` is the centre of its pad; a header's
33
+ `xMm`/`yMm` is the centre of its FIRST pin; a hole's `xMm`/`yMm` is its centre. Only
34
+ `rect` silk items use their top-left corner.
35
+ - **Pad sizes are in the part frame and never rotated:** `pad.wMm` is the extent along x,
36
+ `pad.hMm` the extent along y, whatever the header orientation.
37
+ - Pins, leads and overhanging parts may lie OUTSIDE the outline (negative coordinates or
38
+ beyond the outline size). The compiler grows the drawing canvas to fit them.
39
+ - If the part has headers, the compiler shifts the whole drawing so the first header pin
40
+ lands exactly on Fritzing's 0.1 in (100 mil) grid; that is what makes the part seat in a
41
+ breadboard. You do nothing for this; just keep header pins at their true pitch.
42
+ - The drawing shows the TOP (component) side. A pad that exists only on the back is placed
43
+ where it would be seen through the board from the top (same x/y as from the top).
44
+
45
+ ## Top level
46
+
47
+ | field | type | default | notes |
48
+ | ------------- | ------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `$schema` | string | - | optional; `fritzkit new` writes the published schema URL so editors validate as you type |
50
+ | `slug` | string, 3-60 chars | **required** | lowercase letters, digits and `-`, starting and ending with a letter or digit (`xiao-esp32-c6`). Names the folder and the moduleId |
51
+ | `title` | string, 1-120 | **required** | shown in Fritzing's Inspector and parts bin |
52
+ | `version` | integer >= 1 | `1` | bump by hand when the part changes materially; shown in the variant as `v<version> <hash8>` |
53
+ | `family` | string, 1-60 | the slug | Fritzing swap group: parts in one family can be swapped in the Inspector. Leave unset unless several slugs should swap with each other |
54
+ | `label` | string, 1-4 | `"A"` | reference designator prefix: `A` module/board, `U` IC or MCU board, `J` connector, `K` relay, `M` motor |
55
+ | `author` | string, max 80 | `"fritzkit"` | |
56
+ | `tags` | string[] (each 1-30) | `[]` | search keywords for Fritzing's parts search |
57
+ | `description` | string, max 4000 | `""` | plain text; say what the part is and which side and orientation the drawing shows |
58
+ | `sources` | URL[] | `[]` | listing, wiki and datasheet URLs (must be valid URLs); appended to the description in Fritzing |
59
+ | `properties` | object string -> string | `{}` | extra Inspector properties (`mpn`, `type`, `voltage`, `chip` ...). Keys and values must be non-empty. `family`/`variant` are automatic |
60
+ | `outline` | [Outline](#outline) | **required** | board size and shape |
61
+ | `artwork` | [Artwork](#artwork) | `{}` (vector) | how the breadboard view looks |
62
+ | `pins` | [Pin](#pin)[] | `[]` | free-standing pads or pins at arbitrary positions |
63
+ | `headers` | [Header](#header)[] | `[]` | rows/grids of pins at a fixed pitch (0.1 in headers, castellated edges) |
64
+ | `leads` | [Lead](#lead)[] | `[]` | flying wire leads with a connector at the free end |
65
+ | `pinIds` | object key -> integer >= 0 | `{}` | **managed by fritzkit; never edit by hand.** See [pinIds](#pinids) |
66
+ | `buses` | [Bus](#bus)[] | `[]` | connectors that are the same electrical node (e.g. several GND pads) |
67
+ | `spice` | [Spice](#spice) | - | optional simulator model |
68
+ | `schematic` | [schematic options](#schematic) | `{}` | schematic symbol layout |
69
+ | `pcb` | [pcb options](#pcb) | `{}` | PCB footprint options |
70
+
71
+ Unknown keys are rejected everywhere (every object is strict), so a typo such as
72
+ `"widthmm"` is an error instead of being silently ignored.
73
+
74
+ ## Fields shared by pins, header pins and leads
75
+
76
+ | field | type | default | notes |
77
+ | ------------- | --------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
78
+ | `key` | string | **required** | stable identifier, unique across the whole part. Letters, digits, `_ . + -`; must not start with `.`, `+` or `-`. Never rename |
79
+ | `name` | string, 1-40 | **required** | the connector name Fritzing shows on hover and in the Inspector. Use the silkscreen text (`GND`, `D7`, `NO`), never `pin1` |
80
+ | `label` | string, max 16 | the name | text drawn next to the pin (breadboard and schematic); `""` hides the breadboard label; the fzp connector name is always `name` |
81
+ | `description` | string, max 400 | the name | function, voltage, alternate functions (`D4 / SDA`) |
82
+ | `role` | enum | `"io"` | `power`, `gnd`, `io`, `analog`, `pwm`, `i2c`, `spi`, `uart`, `usb`, `clock`, `reset`, `enable`, `nc`, `other` |
83
+
84
+ `role` decides where the pin goes on the schematic symbol (see [schematic](#schematic)) and
85
+ its label colour. Use `power` for every supply pin (VCC, 5V, 3V3, VBAT, VIN), `gnd` for every
86
+ ground, `nc` for pins that are not connected, `other` for things like relay contacts and
87
+ motor phases.
88
+
89
+ Symbol-only or non-Latin silkscreen (a plus in a circle, Chinese text): `name` is the
90
+ conventional ASCII function name (`P+`, `P-`, `VCC`); the printed symbol or the translation
91
+ goes in `description`. Names and labels are ASCII only.
92
+
93
+ ## Pin
94
+
95
+ A pad or pin at an arbitrary position: solder pads, screw terminals, odd pin spacings, test
96
+ pads, anything not on a regular grid.
97
+
98
+ | field | type | default | notes |
99
+ | ----------------------------------- | ---------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------- |
100
+ | key, name, label, description, role | see above | | |
101
+ | `type` | `female` / `male` / `pad` / `wire` | `"female"` | see below |
102
+ | `xMm` | number | **required** | pad centre, part frame |
103
+ | `yMm` | number | **required** | pad centre, part frame |
104
+ | `pad` | [PadGeometry](#padgeometry) | `{ "shape": "rect", "wMm": 2, "hMm": 2, "holeMm": 1 }` | copper size and drill |
105
+ | `snap` | boolean | - | UI hint only: snap to the 0.1 in grid when dragged in `fritzkit ui` |
106
+
107
+ Connector `type` (what Fritzing lets attach to it):
108
+
109
+ - `female` - a wire or a male pin attaches to it: through-hole solder pads, female header
110
+ sockets, screw terminals. The default, and the right choice for most pads with a hole.
111
+ - `male` - it plugs into a breadboard or socket: header pins.
112
+ - `pad` - a surface pad with no hole: SMD pads, castellated edges, test pads.
113
+ - `wire` - reserved by Fritzing for its own wires; do not use it (leads are emitted as
114
+ `male` automatically).
115
+
116
+ ## PadGeometry
117
+
118
+ | field | type | default | notes |
119
+ | -------- | ------------------------- | ------------ | --------------------------------------------------------------- |
120
+ | `shape` | `rect` / `round` / `oval` | `"rect"` | copper shape |
121
+ | `wMm` | number > 0 | **required** | copper extent along x |
122
+ | `hMm` | number > 0 | **required** | copper extent along y |
123
+ | `holeMm` | number >= 0 | - | drill diameter; absent or 0 = no hole (SMD, top copper only) |
124
+ | `smd` | boolean | - | force SMD (no drill, top copper only) even when `holeMm` is set |
125
+
126
+ In the PCB view a pad with a hole becomes a through-hole pad on both copper layers. Fritzing
127
+ needs a minimum hole and annular ring, so very small holes are drawn slightly bigger.
128
+
129
+ The PCB SVG draws the pad as a ring: a `<circle fill="none">` whose stroke is the copper, so
130
+ `drill = 2 * (r - stroke-width / 2)` and `copper = 2 * (r + stroke-width / 2)` (mils). Oblong
131
+ and rectangular pads also get a solid copper rect under the ring, so a preview PNG can look
132
+ like a solid block; the drill is still the ring's empty centre.
133
+
134
+ ## Header
135
+
136
+ A row or grid of pins at a fixed pitch, drawn in Fritzing's house style.
137
+
138
+ | field | type | default | notes |
139
+ | ------------- | --------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------- |
140
+ | `key` | string | **required** | identifies the header (not a connector) |
141
+ | `kind` | `male` / `female` / `castellated` | `"male"` | male pins, female sockets, or castellated (half-hole) SMD edge pads |
142
+ | `pitchMm` | number > 0 | `2.54` | centre-to-centre spacing, both directions |
143
+ | `rows` | integer 1-8 | `1` | |
144
+ | `cols` | integer 1-64 | **required** | |
145
+ | `xMm`, `yMm` | number | **required** | centre of the FIRST pin (row 0, col 0) |
146
+ | `orientation` | `horizontal` / `vertical` | `"horizontal"` | horizontal: cols run along +x, rows along +y. vertical: cols run along +y (down), rows along +x (right) |
147
+ | `pins` | (HeaderPin or `null`)[] | **required** | exactly `rows * cols` entries, row-major (row 0 col 0, row 0 col 1, ...). `null` leaves a position empty |
148
+ | `pad` | [PadGeometry](#padgeometry) | see below | copper override |
149
+
150
+ A HeaderPin has only `key`, `name`, `label`, `description` and `role`. Its position comes
151
+ from the grid and its connector type from `kind` (male -> `male`, female -> `female`,
152
+ castellated -> `pad`).
153
+
154
+ Default copper: male/female = round 1.6 mm pad with a 1.0 mm hole (through-hole);
155
+ castellated = rect 1.6 x 2.4 mm SMD (`wMm` along x, `hMm` along y). The castellated default
156
+ suits a HORIZONTAL row along the top or bottom edge. For a VERTICAL column along the left or
157
+ right edge set `"pad": { "shape": "rect", "wMm": 2.4, "hMm": 1.6, "smd": true }` so the long
158
+ side points into the board.
159
+
160
+ Examples: a 1x7 row with pin 1 at the left is
161
+ `{ "key": "j1", "cols": 7, "xMm": <x of pin 1>, "yMm": <y>, "pins": [7 entries] }`; a 2x20
162
+ header is `{ "rows": 2, "cols": 20, ... }` with 40 entries (all of row 0, then all of row 1).
163
+ Two rows that are NOT one pitch apart (the two sides of a DIP-style module) are two separate
164
+ headers.
165
+
166
+ ## Lead
167
+
168
+ A flying wire (motor phases, battery leads, a servo cable). Drawn as a bendable leg; the
169
+ connector is the free end, which plugs into a breadboard like a male pin.
170
+
171
+ | field | type | default | notes |
172
+ | ----------------------------------- | -------------------------------- | ------------ | ------------------------------------------------------------- |
173
+ | key, name, label, description, role | see above | | |
174
+ | `fromXMm`, `fromYMm` | number | **required** | where the wire leaves the board (usually on the outline edge) |
175
+ | `direction` | `left` / `right` / `up` / `down` | `"left"` | which way the wire runs from that point |
176
+ | `lengthMm` | number > 0 | `40` | drawn length; the free end is `lengthMm` away in `direction` |
177
+ | `color` | `#RRGGBB` | `"#000000"` | insulation colour |
178
+ | `gaugeMm` | number > 0 | `1.6` | wire outer diameter, for drawing |
179
+
180
+ Leads have no `type` or `pad` field. In the PCB view a lead becomes a through-hole pad
181
+ (unless `pcb.mode` is `smd`).
182
+
183
+ ## Outline
184
+
185
+ | field | type | default | notes |
186
+ | ---------------- | ----------------------------- | ------------ | ------------------------------------------------------------------------------ |
187
+ | `widthMm` | number > 0 | **required** | extent along x. Convention: put the LONG edge on x |
188
+ | `heightMm` | number > 0 | **required** | extent along y |
189
+ | `shape` | `rect` / `rounded` / `custom` | `"rounded"` | |
190
+ | `cornerRadiusMm` | number >= 0 | `1` | used by `rounded` |
191
+ | `pathMm` | string | - | for `custom`: an SVG path in mm, part frame, e.g. `"M0 0 H30 V10 L25 15 H0 Z"` |
192
+ | `holes` | `{ xMm, yMm, dMm }`[] | `[]` | mounting holes (centre, diameter); drawn in breadboard and PCB, not connectors |
193
+
194
+ ## Artwork
195
+
196
+ How the breadboard view is drawn. The schematic, PCB and icon views are always generated
197
+ from the pins and the outline.
198
+
199
+ | field | type | default | notes |
200
+ | ------------ | ----------------------- | ----------- | ------------------------------------------------------------------------------------ |
201
+ | `style` | `vector` / `photo` | `"vector"` | vector = Fritzing house style; photo = embedded raster with vector pads on top |
202
+ | `boardColor` | `#RRGGBB` | `"#1B7F3B"` | PCB colour (green). Black `#1A1A1A`, blue `#1B4F9C`, red `#B0242A`, purple `#5B2A86` |
203
+ | `silkColor` | `#RRGGBB` | `"#FFFFFF"` | silkscreen colour |
204
+ | `silkscreen` | [SilkItem](#silkitem)[] | `[]` | text and lines printed on the board |
205
+ | `components` | [SilkItem](#silkitem)[] | `[]` | large components drawn as shapes (USB socket, relay can, chip, heatsink) |
206
+ | `photo` | [Photo](#photo) | - | required when `style` is `photo` |
207
+
208
+ Draw only the components that help a user recognise the board and find pin 1 (the USB
209
+ connector, a relay can, a big chip). Do not draw every resistor. fritzkit labels every pin
210
+ already, so do not repeat pin names in `silkscreen`. Component shapes are drawn on top of
211
+ silkscreen text (pads are drawn on top of both), so keep text off component shapes or it is
212
+ hidden. Silkscreen and components are drawn under header bodies, pads and pin labels, so
213
+ put text where pads are not.
214
+
215
+ ### SilkItem
216
+
217
+ A union on `kind`. All positions in mm, part frame.
218
+
219
+ | kind | fields (default) |
220
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
221
+ | `text` | `text` (1-80, required), `xMm`, `yMm`, `sizeMm` (`1.2`), `color` (`#FFFFFF`), `anchor` `start`/`middle`/`end` (`middle`), `rotate` degrees (`0`) |
222
+ | `rect` | `xMm`, `yMm` (TOP-LEFT corner), `wMm`, `hMm`, `color` (`#222222`), `rxMm` corner radius (`0`), `note` (not rendered) |
223
+ | `circle` | `xMm`, `yMm` (centre), `dMm` diameter, `color` (`#222222`), `note` |
224
+ | `line` | `x1Mm`, `y1Mm`, `x2Mm`, `y2Mm`, `widthMm` (`0.2`), `color` (`#FFFFFF`) |
225
+
226
+ ### Photo
227
+
228
+ | field | type | default | notes |
229
+ | ------------------ | -------------------------- | ------------ | ------------------------------------------------------------------------------- |
230
+ | `src` | string | **required** | path relative to the part folder, e.g. `"assets/top.jpg"` |
231
+ | `crop` | `[x, y, w, h]` (pixels) | - | crop rectangle in source pixels, applied BEFORE rotation |
232
+ | `rotate` | `0` / `90` / `180` / `270` | `0` | clockwise, applied after the crop, so the board ends up in the spec orientation |
233
+ | `removeBackground` | boolean | `true` | key out a near-white listing background and trim to the board |
234
+ | `opacity` | number 0-1 | `1` | |
235
+
236
+ After crop, rotate and trim the image is stretched over the outline rectangle, so the board
237
+ must fill the processed image edge to edge (that is what `removeBackground` trims to; with
238
+ `removeBackground: false`, crop exactly to the board). The image is downscaled to at most
239
+ 1600 px wide. Pads are still drawn as vector shapes on top, at the spec positions.
240
+
241
+ ## Bus
242
+
243
+ | field | type | notes |
244
+ | ------ | ----------------------- | ----------------------------------------------------- |
245
+ | `id` | string (`A-Za-z0-9_.-`) | unique, e.g. `GND`, `VBAT` |
246
+ | `pins` | pin key[] (>= 2) | keys of pins, header pins or leads that share one net |
247
+
248
+ A bus tells Fritzing that the connectors are one electrical node, so a wire to one GND pad
249
+ counts as connected to every GND pad (ratsnest, routing checks). A connector can be in at
250
+ most one bus. Bus pins that are really the same copper: several `GND` pads, two `VIN` pads
251
+ tied together, a screw terminal paralleled with a solder pad. Pins that merely have similar
252
+ names (two `+` pads on different nets, `IN1` and `IN2`, `VCC` and `5V` with a regulator
253
+ between them) are NOT a bus unless the copper is visibly shared or the datasheet says so.
254
+
255
+ ## pinIds
256
+
257
+ `pinIds` maps every pin key to its Fritzing connector number (`connector0`, `connector1`,
258
+ ...). fritzkit fills it in: the first `validate`, `render` or `export` after you add a pin
259
+ gives the new key the next free number and writes it back to `part.json`. Numbers are never
260
+ reused, so when you delete a pin its number stays reserved and every other pin keeps its id.
261
+ That is what lets Fritzing swap a placed copy for a newer version without dropping wires.
262
+
263
+ fritzkit manages `pinIds`: do not hand-edit them for a part that has ever been
264
+ exported/installed (connector ids must stay stable). For a brand-new part you may delete
265
+ the `pinIds` object before the first export to close gaps. Never reuse a key for a
266
+ different physical pin;
267
+ rename a pin by changing `name`, not `key`. A new spec may omit `pinIds` or write `{}`. Gaps
268
+ in the numbering after a pin was removed produce a harmless `connector-numbering` warning
269
+ (only a warning).
270
+
271
+ ## Spice
272
+
273
+ | field | type | notes |
274
+ | ------- | -------- | ---------------------------------- |
275
+ | `model` | string | optional `.model` / `.subckt` text |
276
+ | `lines` | string[] | at least one netlist line |
277
+
278
+ Tokens: `{instanceTitle}`, `{net connectorN}` (N from `pinIds`, so add spice AFTER the first
279
+ validate), and `{<property>}` for any entry in `properties`. A battery with internal
280
+ resistance, where connector0 is minus and connector1 is plus:
281
+
282
+ ```json
283
+ {
284
+ "properties": { "voltage": "3.7V", "internal resistance": "0.05" },
285
+ "spice": {
286
+ "lines": [
287
+ "V{instanceTitle} {net connector1}_{instanceTitle}_aux {net connector0} DC {voltage}",
288
+ "R{instanceTitle} {net connector1}_{instanceTitle}_aux {net connector1} {internal resistance}"
289
+ ]
290
+ }
291
+ }
292
+ ```
293
+
294
+ Parts without `spice` are greyed out in Fritzing's simulator, which is normal for modules
295
+ and MCU boards.
296
+
297
+ ## schematic
298
+
299
+ | field | type | default | notes |
300
+ | -------- | ------------------------------------------ | ----------- | ------------------------------------------ |
301
+ | `layout` | `by-role` | `"by-role"` | an auto-sized rectangle on the 0.1 in grid |
302
+ | `sides` | `{ left?, right?, top?, bottom? }`, role[] | - | override which side each role lands on |
303
+
304
+ Behaviour of `by-role`: one box; pins are grouped by role onto sides, in spec order within
305
+ a role; every bused GND pin still gets its own pin. Default sides: power, usb, enable and
306
+ reset on the left, gnd at the bottom, everything else on the right. An empty `label` is
307
+ drawn as an empty pin label (it does not fall back to `name`). An explicit per-pin ordering
308
+ hint is not available yet.
309
+ Example override:
310
+ `"schematic": { "sides": { "left": ["power", "gnd"], "right": ["io", "i2c", "spi", "uart", "analog", "pwm"] } }`.
311
+
312
+ ## pcb
313
+
314
+ | field | type | default | notes |
315
+ | ------------------- | ---------------------- | -------- | ------------------------------------------------------------------- |
316
+ | `mode` | `auto` / `tht` / `smd` | `"auto"` | auto: per pad (a hole means through-hole); tht / smd force all pads |
317
+ | `silkscreenOutline` | boolean | `true` | draw the board outline on the PCB silkscreen |
318
+
319
+ ## Identity (computed, not in the spec)
320
+
321
+ - `moduleId = fritzkit-<slug>-<hash8>`; `hash8` hashes part.json (minus `$schema`) and every
322
+ file in `assets/`. Any change gives a new moduleId, because Fritzing refuses to import a
323
+ moduleId it already has.
324
+ - `family = family ?? slug` (stable) and `variant = v<version> <hash8>` (unique per export).
325
+ In Fritzing, select a placed part and pick another variant in the Inspector to swap it.
326
+
327
+ ## Worked examples
328
+
329
+ Complete, valid specs live in the skill's
330
+ [examples.md](examples.md): the Seeed XIAO ESP32-C6 (headers and a
331
+ bus), an ESC with wire leads, a relay module, a KiCad import, and the pads-only HW-391
332
+ board measured from a listing photo. The full XIAO spec is also in
333
+ [SKILL.md](../SKILL.md) (a portrait board, USB at the top).