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,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 = ["."]