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,197 @@
1
+ """measure.py - pixel-to-mm helper for fritzkit part measuring (Python 3, Pillow only).
2
+
3
+ uv run --with pillow python -I measure.py bbox top.jpg --width-mm 48 --height-mm 20
4
+ uv run --with pillow python -I measure.py grid top.jpg --bbox X,Y,W,H --width-mm 48 --height-mm 20 --out grid.png
5
+ uv run --with pillow python -I measure.py holes top.jpg --bbox X,Y,W,H --width-mm 48 --height-mm 20 --out holes.png
6
+
7
+ Pixel values printed (bbox) are in ORIGINAL image pixels; hole positions are part-frame mm
8
+ (origin = top-left of the board bbox, x right, y down). Run it on the same image you read.
9
+ """
10
+
11
+ import argparse
12
+ import sys
13
+ from collections import deque
14
+
15
+ from PIL import Image, ImageChops, ImageDraw, ImageFilter, ImageFont
16
+
17
+
18
+ def load(path):
19
+ img = Image.open(path)
20
+ img.load()
21
+ return img.convert("RGB")
22
+
23
+
24
+ def parse_bbox(text):
25
+ try:
26
+ x, y, w, h = (int(round(float(v))) for v in text.split(","))
27
+ except ValueError:
28
+ sys.exit("--bbox must be x,y,w,h (pixels), e.g. 212,140,1050,890")
29
+ return x, y, w, h
30
+
31
+
32
+ def detect_bbox(img, thr=235, work=900):
33
+ """Flood-fill the near-white background from the four corners; bbox of the rest."""
34
+ s = min(1.0, work / max(img.size))
35
+ small = img.resize((max(1, int(img.width * s)), max(1, int(img.height * s))))
36
+ r, g, b = small.split()
37
+ low = ImageChops.darker(ImageChops.darker(r, g), b) # min channel
38
+ mask = low.point(lambda v: 255 if v >= thr else 0).filter(ImageFilter.MedianFilter(5))
39
+ w, h = mask.size
40
+ for corner in ((0, 0), (w - 1, 0), (0, h - 1), (w - 1, h - 1)):
41
+ if mask.getpixel(corner) == 255:
42
+ ImageDraw.floodfill(mask, corner, 128)
43
+ board = mask.point(lambda v: 0 if v == 128 else 255)
44
+ box = board.getbbox()
45
+ if box is None:
46
+ sys.exit("no board found (image looks like all background); pass --bbox by hand")
47
+ x0, y0, x1, y1 = (round(v / s) for v in box)
48
+ return x0, y0, min(img.width, x1) - x0, min(img.height, y1) - y0
49
+
50
+
51
+ def scale_report(bbox, args):
52
+ _, _, w, h = bbox
53
+ lines = []
54
+ if args.width_mm:
55
+ lines.append("px/mm (from width) = %.3f" % (w / args.width_mm))
56
+ if args.height_mm:
57
+ lines.append("px/mm (from height) = %.3f" % (h / args.height_mm))
58
+ if args.width_mm and args.height_mm:
59
+ a, b = w / args.width_mm, h / args.height_mm
60
+ lines.append("disagreement = %.1f%% (over ~3%% means perspective or a wrong outline)"
61
+ % (100 * abs(a - b) / max(a, b)))
62
+ return lines
63
+
64
+
65
+ def need_dims(args):
66
+ if not (args.bbox and args.width_mm and args.height_mm):
67
+ sys.exit("this subcommand needs --bbox x,y,w,h --width-mm N --height-mm N")
68
+ return parse_bbox(args.bbox)
69
+
70
+
71
+ def font(px):
72
+ try:
73
+ return ImageFont.load_default(size=px)
74
+ except TypeError: # old Pillow
75
+ return ImageFont.load_default()
76
+
77
+
78
+ def cmd_bbox(args):
79
+ img = load(args.image)
80
+ box = detect_bbox(img, args.thr)
81
+ print("image %dx%d px" % img.size)
82
+ print("bbox x,y,w,h = %d,%d,%d,%d" % box)
83
+ for line in scale_report(box, args):
84
+ print(line)
85
+ print("NOTE: a drop shadow or a protruding connector can inflate the bbox; check by eye.")
86
+
87
+
88
+ def cmd_grid(args):
89
+ bx, by, bw, bh = need_dims(args)
90
+ img = load(args.image)
91
+ m = 20
92
+ crop = img.crop((bx - m, by - m, bx + bw + m, by + bh + m))
93
+ k = max(1.0, 1800 / crop.width)
94
+ crop = crop.resize((round(crop.width * k), round(crop.height * k)), Image.LANCZOS)
95
+ sx, sy = bw * k / args.width_mm, bh * k / args.height_mm # output px per mm
96
+ ox, oy = m * k, m * k
97
+ over = Image.new("RGBA", crop.size, (0, 0, 0, 0))
98
+ d = ImageDraw.Draw(over)
99
+ f = font(max(12, round(sy * 0.9)))
100
+ for i in range(int(args.width_mm) + 1):
101
+ x = ox + i * sx
102
+ major = i % 5 == 0
103
+ d.line([(x, oy), (x, oy + bh * k)], fill=(255, 0, 0, 200 if major else 70),
104
+ width=2 if major else 1)
105
+ if major:
106
+ d.text((x + 2, 2), str(i), fill=(255, 0, 0, 255), font=f)
107
+ for j in range(int(args.height_mm) + 1):
108
+ y = oy + j * sy
109
+ major = j % 5 == 0
110
+ d.line([(ox, y), (ox + bw * k, y)], fill=(0, 0, 255, 200 if major else 70),
111
+ width=2 if major else 1)
112
+ if major:
113
+ d.text((2, y + 2), str(j), fill=(0, 0, 255, 255), font=f)
114
+ Image.alpha_composite(crop.convert("RGBA"), over).convert("RGB").save(args.out)
115
+ print("wrote %s (1 mm grid, labels every 5 mm; red = x, blue = y, origin = board top-left)"
116
+ % args.out)
117
+
118
+
119
+ def components(mask):
120
+ """Connected components of a binary 'L' mask (255 = on): (area, x0, y0, x1, y1, cx, cy)."""
121
+ w, h = mask.size
122
+ px = mask.load()
123
+ seen = bytearray(w * h)
124
+ for y0 in range(h):
125
+ for x0 in range(w):
126
+ if px[x0, y0] != 255 or seen[y0 * w + x0]:
127
+ continue
128
+ q = deque([(x0, y0)])
129
+ seen[y0 * w + x0] = 1
130
+ n, sx, sy, mnx, mny, mxx, mxy = 0, 0, 0, x0, y0, x0, y0
131
+ while q:
132
+ x, y = q.popleft()
133
+ n, sx, sy = n + 1, sx + x, sy + y
134
+ mnx, mny, mxx, mxy = min(mnx, x), min(mny, y), max(mxx, x), max(mxy, y)
135
+ for nx, ny in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
136
+ if 0 <= nx < w and 0 <= ny < h and px[nx, ny] == 255 and not seen[ny * w + nx]:
137
+ seen[ny * w + nx] = 1
138
+ q.append((nx, ny))
139
+ yield n, mnx, mny, mxx, mxy, sx / n, sy / n
140
+
141
+
142
+ def cmd_holes(args):
143
+ bx, by, bw, bh = need_dims(args)
144
+ img = load(args.image)
145
+ crop = img.crop((bx, by, bx + bw, by + bh))
146
+ k = min(1.0, 700 / crop.width) # work at <= 700 px wide
147
+ small = crop.resize((max(1, round(crop.width * k)), max(1, round(crop.height * k))))
148
+ mask = small.convert("L").filter(ImageFilter.MedianFilter(3))
149
+ mask = mask.point(lambda v: 255 if v < args.dark else 0)
150
+ pmm = small.width / args.width_mm # work px per mm
151
+ found = []
152
+ for n, x0, y0, x1, y1, cx, cy in components(mask):
153
+ w, h = x1 - x0 + 1, y1 - y0 + 1
154
+ dia = (w + h) / 2 / pmm
155
+ fill = n / (w * h)
156
+ if args.min_d <= dia <= args.max_d and 0.75 <= w / h <= 1.33 and 0.6 <= fill <= 0.92:
157
+ found.append((cx / pmm, cy / (small.height / args.height_mm), dia))
158
+ out = crop.copy()
159
+ d = ImageDraw.Draw(out)
160
+ f = font(max(12, out.width // 80))
161
+ for i, (x, y, dia) in enumerate(sorted(found), 1):
162
+ print("hole %d: x=%.2f y=%.2f mm diameter ~%.2f mm" % (i, x, y, dia))
163
+ cx, cy = x * bw / args.width_mm, y * bh / args.height_mm
164
+ r = max(6, dia * bw / args.width_mm / 2 + 4)
165
+ d.ellipse([cx - r, cy - r, cx + r, cy + r], outline=(255, 0, 0), width=3)
166
+ d.text((cx + r + 2, cy - r), str(i), fill=(255, 0, 0), font=f)
167
+ if not found:
168
+ print("no circular dark blobs found; try --dark 90, or read pads off the grid image")
169
+ if args.out:
170
+ out.save(args.out)
171
+ print("wrote %s" % args.out)
172
+
173
+
174
+ def main():
175
+ p = argparse.ArgumentParser(description=__doc__,
176
+ formatter_class=argparse.RawDescriptionHelpFormatter)
177
+ sub = p.add_subparsers(dest="cmd", required=True)
178
+ for name, fn in (("bbox", cmd_bbox), ("grid", cmd_grid), ("holes", cmd_holes)):
179
+ s = sub.add_parser(name)
180
+ s.add_argument("image")
181
+ s.add_argument("--bbox", help="x,y,w,h in pixels (from the bbox subcommand)")
182
+ s.add_argument("--width-mm", type=float)
183
+ s.add_argument("--height-mm", type=float)
184
+ s.add_argument("--out")
185
+ s.add_argument("--thr", type=int, default=235, help="bbox: background min-channel level")
186
+ s.add_argument("--dark", type=int, default=60, help="holes: luma below this is dark")
187
+ s.add_argument("--min-d", type=float, default=0.5, help="holes: min diameter, mm")
188
+ s.add_argument("--max-d", type=float, default=4.0, help="holes: max diameter, mm")
189
+ s.set_defaults(fn=fn)
190
+ args = p.parse_args()
191
+ if args.cmd == "grid" and not args.out:
192
+ args.out = "grid.png"
193
+ args.fn(args)
194
+
195
+
196
+ if __name__ == "__main__":
197
+ main()
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,45 @@
1
+ # fzp-checker
2
+
3
+ Dev-only tool that vendors Fritzing's official part checker
4
+ (`fzp_checker.py` and `scripts/checks/` from fritzing-parts) so fritzkit can
5
+ validate generated `.fzpz` files. See `vendor/UPSTREAM.md` for provenance and
6
+ license. It is not part of the published package.
7
+
8
+ ## Run (from the repo root)
9
+
10
+ uv run --project tools/fzp-checker check-fzp <path-to.fzpz> [extra args]
11
+
12
+ Output (stdout/stderr) is the checker's, unchanged. Exit codes follow
13
+ sysexits: 0 = clean, 65 = errors found, 64 = usage error, 66 = no input;
14
+ an unexpected checker crash exits 1. Any non-zero exit means "not clean".
15
+ Warnings alone do not change the exit code.
16
+
17
+ `check-fzp --help` lists all checks; `-c <name> ...` selects checks and `-v`
18
+ is verbose. Note `-c` consumes following words, so put the path first.
19
+
20
+ ## How the TS validator calls it
21
+
22
+ uv run --project tools/fzp-checker check-fzp <file>
23
+
24
+ Spawn it, capture stdout/stderr, and treat a non-zero exit as failure.
25
+
26
+ ## Windows notes
27
+
28
+ The wrapper (`check_fzp.py`) shims `os.EX_*` (missing on Windows) and forces
29
+ UTF-8 output; the vendored files are left untouched.
30
+
31
+ ## WARNING: --fix rewrites files
32
+
33
+ `--fix` passes through to the checker and modifies the checked files in
34
+ place. Always run it on a copy.
35
+
36
+ ## Development
37
+
38
+ cd tools/fzp-checker
39
+ uv sync
40
+ uv run black --check . # vendor/ is excluded
41
+ uv run pytest
42
+
43
+ `tests/fixtures/adafruit-neokey-1x4.fzpz` is a copy of an untrusted
44
+ third-party part; it is only read, never executed. It is an old part that
45
+ the checker reports errors on (exit 65), and the smoke test pins that.
@@ -0,0 +1,48 @@
1
+ """Wrapper around the vendored Fritzing fzp_checker.
2
+
3
+ Usage: uv run --project tools/fzp-checker check-fzp <file.fzpz> [extra args]
4
+
5
+ The vendored checker uses os.EX_* exit codes, which do not exist on Windows,
6
+ so they are shimmed here before the checker is imported. Output and extra
7
+ arguments (including --fix) pass through unchanged. Exit code is non-zero on
8
+ errors.
9
+ """
10
+
11
+ import os
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ VENDOR = Path(__file__).resolve().parent / "vendor"
16
+
17
+ _EX_CODES = {
18
+ "EX_OK": 0,
19
+ "EX_USAGE": 64,
20
+ "EX_DATAERR": 65,
21
+ "EX_NOINPUT": 66,
22
+ }
23
+
24
+
25
+ def _shim_exit_codes() -> None:
26
+ for name, value in _EX_CODES.items():
27
+ if not hasattr(os, name):
28
+ setattr(os, name, value)
29
+
30
+
31
+ def _force_utf8_output() -> None:
32
+ # The checker prints non-ASCII marks; the default Windows codepage crashes.
33
+ for stream in (sys.stdout, sys.stderr):
34
+ stream.reconfigure(encoding="utf-8", errors="replace")
35
+
36
+
37
+ def main() -> None:
38
+ _shim_exit_codes()
39
+ _force_utf8_output()
40
+ sys.path.insert(0, str(VENDOR))
41
+ from scripts.checks.fzp_checker_runner import main as checker_main
42
+
43
+ sys.argv[0] = "check-fzp"
44
+ checker_main()
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "fzp-checker"
3
+ version = "0.1.0"
4
+ description = "Dev-only wrapper around Fritzing's vendored fzp_checker"
5
+ requires-python = ">=3.13"
6
+ dependencies = ["lxml"]
7
+
8
+ [project.scripts]
9
+ check-fzp = "check_fzp:main"
10
+
11
+ [dependency-groups]
12
+ dev = ["black", "pytest"]
13
+
14
+ [build-system]
15
+ requires = ["setuptools"]
16
+ build-backend = "setuptools.build_meta"
17
+
18
+ [tool.setuptools]
19
+ py-modules = ["check_fzp"]
20
+
21
+ [tool.black]
22
+ extend-exclude = "vendor"
23
+
24
+ [tool.pytest.ini_options]
25
+ testpaths = ["tests"]
26
+ pythonpath = ["."]