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.
- package/.claude-plugin/marketplace.json +12 -0
- package/.claude-plugin/plugin.json +13 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +429 -2
- package/dist/cli/commands/doctor.d.ts +4 -0
- package/dist/cli/commands/doctor.js +7 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +17 -0
- package/dist/cli/commands/export.js +81 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/kicad.d.ts +10 -0
- package/dist/cli/commands/kicad.js +22 -0
- package/dist/cli/commands/kicad.js.map +1 -0
- package/dist/cli/commands/parts.d.ts +22 -0
- package/dist/cli/commands/parts.js +46 -0
- package/dist/cli/commands/parts.js.map +1 -0
- package/dist/cli/commands/prune.d.ts +14 -0
- package/dist/cli/commands/prune.js +67 -0
- package/dist/cli/commands/prune.js.map +1 -0
- package/dist/cli/commands/render.d.ts +24 -0
- package/dist/cli/commands/render.js +62 -0
- package/dist/cli/commands/render.js.map +1 -0
- package/dist/cli/commands/schema.d.ts +10 -0
- package/dist/cli/commands/schema.js +35 -0
- package/dist/cli/commands/schema.js.map +1 -0
- package/dist/cli/commands/ui.d.ts +14 -0
- package/dist/cli/commands/ui.js +67 -0
- package/dist/cli/commands/ui.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +24 -0
- package/dist/cli/commands/validate.js +91 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/context.d.ts +81 -0
- package/dist/cli/context.js +139 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +8 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +21 -0
- package/dist/cli/program.js +158 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/raster.d.ts +4 -0
- package/dist/cli/raster.js +9 -0
- package/dist/cli/raster.js.map +1 -0
- package/dist/core/compile/breadboard-util.d.ts +12 -0
- package/dist/core/compile/breadboard-util.js +107 -0
- package/dist/core/compile/breadboard-util.js.map +1 -0
- package/dist/core/compile/breadboard.d.ts +60 -0
- package/dist/core/compile/breadboard.js +513 -0
- package/dist/core/compile/breadboard.js.map +1 -0
- package/dist/core/compile/fzp.d.ts +12 -0
- package/dist/core/compile/fzp.js +183 -0
- package/dist/core/compile/fzp.js.map +1 -0
- package/dist/core/compile/icon.d.ts +17 -0
- package/dist/core/compile/icon.js +27 -0
- package/dist/core/compile/icon.js.map +1 -0
- package/dist/core/compile/index.d.ts +4 -0
- package/dist/core/compile/index.js +62 -0
- package/dist/core/compile/index.js.map +1 -0
- package/dist/core/compile/pcb.d.ts +39 -0
- package/dist/core/compile/pcb.js +164 -0
- package/dist/core/compile/pcb.js.map +1 -0
- package/dist/core/compile/schematic.d.ts +69 -0
- package/dist/core/compile/schematic.js +240 -0
- package/dist/core/compile/schematic.js.map +1 -0
- package/dist/core/compile/style.d.ts +52 -0
- package/dist/core/compile/style.js +72 -0
- package/dist/core/compile/style.js.map +1 -0
- package/dist/core/compile/types.d.ts +50 -0
- package/dist/core/compile/types.js +15 -0
- package/dist/core/compile/types.js.map +1 -0
- package/dist/core/export/bin.d.ts +61 -0
- package/dist/core/export/bin.js +221 -0
- package/dist/core/export/bin.js.map +1 -0
- package/dist/core/export/fzpz.d.ts +37 -0
- package/dist/core/export/fzpz.js +121 -0
- package/dist/core/export/fzpz.js.map +1 -0
- package/dist/core/export/index.d.ts +5 -0
- package/dist/core/export/index.js +6 -0
- package/dist/core/export/index.js.map +1 -0
- package/dist/core/export/install.d.ts +77 -0
- package/dist/core/export/install.js +183 -0
- package/dist/core/export/install.js.map +1 -0
- package/dist/core/export/prune.d.ts +72 -0
- package/dist/core/export/prune.js +165 -0
- package/dist/core/export/prune.js.map +1 -0
- package/dist/core/fritzing.d.ts +22 -0
- package/dist/core/fritzing.js +77 -0
- package/dist/core/fritzing.js.map +1 -0
- package/dist/core/identity.d.ts +29 -0
- package/dist/core/identity.js +70 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/image/bbox.d.ts +27 -0
- package/dist/core/image/bbox.js +24 -0
- package/dist/core/image/bbox.js.map +1 -0
- package/dist/core/image/index.d.ts +8 -0
- package/dist/core/image/index.js +9 -0
- package/dist/core/image/index.js.map +1 -0
- package/dist/core/image/key.d.ts +72 -0
- package/dist/core/image/key.js +317 -0
- package/dist/core/image/key.js.map +1 -0
- package/dist/core/image/prepare.d.ts +31 -0
- package/dist/core/image/prepare.js +98 -0
- package/dist/core/image/prepare.js.map +1 -0
- package/dist/core/image/remover.d.ts +17 -0
- package/dist/core/image/remover.js +51 -0
- package/dist/core/image/remover.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.js +17 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/kicad/footprint.d.ts +104 -0
- package/dist/core/kicad/footprint.js +317 -0
- package/dist/core/kicad/footprint.js.map +1 -0
- package/dist/core/kicad/index.d.ts +25 -0
- package/dist/core/kicad/index.js +197 -0
- package/dist/core/kicad/index.js.map +1 -0
- package/dist/core/kicad/sexpr.d.ts +35 -0
- package/dist/core/kicad/sexpr.js +163 -0
- package/dist/core/kicad/sexpr.js.map +1 -0
- package/dist/core/resolve.d.ts +113 -0
- package/dist/core/resolve.js +301 -0
- package/dist/core/resolve.js.map +1 -0
- package/dist/core/schema/part.d.ts +995 -0
- package/dist/core/schema/part.js +278 -0
- package/dist/core/schema/part.js.map +1 -0
- package/dist/core/schema/part.schema.json +1122 -0
- package/dist/core/svg.d.ts +20 -0
- package/dist/core/svg.js +56 -0
- package/dist/core/svg.js.map +1 -0
- package/dist/core/units.d.ts +28 -0
- package/dist/core/units.js +42 -0
- package/dist/core/units.js.map +1 -0
- package/dist/core/validate/checker.d.ts +46 -0
- package/dist/core/validate/checker.js +142 -0
- package/dist/core/validate/checker.js.map +1 -0
- package/dist/core/validate/doctor.d.ts +25 -0
- package/dist/core/validate/doctor.js +156 -0
- package/dist/core/validate/doctor.js.map +1 -0
- package/dist/core/validate/explain.d.ts +10 -0
- package/dist/core/validate/explain.js +135 -0
- package/dist/core/validate/explain.js.map +1 -0
- package/dist/core/validate/index.d.ts +21 -0
- package/dist/core/validate/index.js +96 -0
- package/dist/core/validate/index.js.map +1 -0
- package/dist/core/validate/structural.d.ts +74 -0
- package/dist/core/validate/structural.js +664 -0
- package/dist/core/validate/structural.js.map +1 -0
- package/dist/core/workspace.d.ts +69 -0
- package/dist/core/workspace.js +213 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/mcp/jsonpatch.d.ts +30 -0
- package/dist/mcp/jsonpatch.js +145 -0
- package/dist/mcp/jsonpatch.js.map +1 -0
- package/dist/mcp/main.d.ts +2 -0
- package/dist/mcp/main.js +5 -0
- package/dist/mcp/main.js.map +1 -0
- package/dist/mcp/server.d.ts +28 -0
- package/dist/mcp/server.js +310 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/server/api.d.ts +72 -0
- package/dist/server/api.js +507 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/events.d.ts +28 -0
- package/dist/server/events.js +134 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/http.d.ts +36 -0
- package/dist/server/http.js +127 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +65 -0
- package/dist/server/index.js +193 -0
- package/dist/server/index.js.map +1 -0
- package/dist/ui/assets/index-DLU8opyI.js +63 -0
- package/dist/ui/assets/index-GhUA-T3b.css +1 -0
- package/dist/ui/index.html +14 -0
- package/package.json +89 -4
- package/skills/fritzkit/SKILL.md +690 -0
- package/skills/fritzkit/reference/examples.md +275 -0
- package/skills/fritzkit/reference/part-json.md +333 -0
- package/skills/fritzkit/scripts/measure.py +197 -0
- package/tools/fzp-checker/.python-version +1 -0
- package/tools/fzp-checker/README.md +45 -0
- package/tools/fzp-checker/check_fzp.py +48 -0
- package/tools/fzp-checker/pyproject.toml +26 -0
- package/tools/fzp-checker/uv.lock +266 -0
- package/tools/fzp-checker/vendor/LICENSE.txt +8 -0
- package/tools/fzp-checker/vendor/UPSTREAM.md +25 -0
- package/tools/fzp-checker/vendor/fzp_checker.py +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/__init__.py +26 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/check_missing_leg_ids.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/connector_numbering.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checker_runner.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_svg_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_utils.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_utils.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
- package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
- package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
- package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
- package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
- package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
- package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
- 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
|
+
/**
|
|
2
|
+
* KiCad footprint import (decision D18): turn a .kicad_mod into a part.json fragment
|
|
3
|
+
* with the footprint's exact pad geometry.
|
|
4
|
+
*
|
|
5
|
+
* Mapping (footprint frame -> part frame): the chosen outline bbox's top-left corner
|
|
6
|
+
* becomes the part origin, so every coordinate is shifted by (-minX, -minY). Both
|
|
7
|
+
* frames are y-down (see footprint.ts), so nothing is flipped. Every emitted length
|
|
8
|
+
* is rounded to 1e-4 mm, which keeps the output deterministic and free of float noise.
|
|
9
|
+
*
|
|
10
|
+
* - thru_hole pad -> Pin type "female" with holeMm = drill (smaller axis for an oval
|
|
11
|
+
* drill); smd and connect pads (and a thru_hole pad without a drill) -> Pin type "pad",
|
|
12
|
+
* smd: true, no hole.
|
|
13
|
+
* - np_thru_hole pad -> outline.holes entry, not a pin.
|
|
14
|
+
* - A pad with no copper layer (only F.Mask / F.Paste ...) is skipped: not a pin.
|
|
15
|
+
* - Pad shape circle -> round, rect/roundrect -> rect, oval -> oval, custom/trapezoid
|
|
16
|
+
* -> rect using the anchor (size) box. 90/270 degree rotations swap w and h.
|
|
17
|
+
* - Pads sharing a number (KiCad: electrically the same pad) get keys p1, p1_2, ...
|
|
18
|
+
* and are tied together with a bus (bus ids pad_<n>, deduped the same way).
|
|
19
|
+
* - Pads on a regular 2.54 mm grid are NOT turned into headers; every pad stays an
|
|
20
|
+
* individual pin so the geometry is exact.
|
|
21
|
+
*
|
|
22
|
+
* importKicadFile is the one function here that touches the filesystem (it only reads
|
|
23
|
+
* the file); everything else is pure.
|
|
24
|
+
*/
|
|
25
|
+
import { readFile } from "node:fs/promises";
|
|
26
|
+
import { padExtent, parseKicadMod } from "./footprint.js";
|
|
27
|
+
export * from "./sexpr.js";
|
|
28
|
+
export * from "./footprint.js";
|
|
29
|
+
export class KicadImportError extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "KicadImportError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Margin added around the pads when the outline comes from the pad bbox. */
|
|
36
|
+
export const PAD_OUTLINE_MARGIN_MM = 1;
|
|
37
|
+
const r4 = (v) => {
|
|
38
|
+
const r = Math.round(v * 10000) / 10000;
|
|
39
|
+
return Object.is(r, -0) ? 0 : r;
|
|
40
|
+
};
|
|
41
|
+
function usable(b) {
|
|
42
|
+
return b !== undefined && b.maxX - b.minX > 1e-6 && b.maxY - b.minY > 1e-6;
|
|
43
|
+
}
|
|
44
|
+
/** The bbox the outline is taken from, and which source it came from. */
|
|
45
|
+
export function chooseOutline(fp, want) {
|
|
46
|
+
const padBox = () => {
|
|
47
|
+
const b = fp.bbox.pads;
|
|
48
|
+
if (!b)
|
|
49
|
+
return undefined;
|
|
50
|
+
const m = PAD_OUTLINE_MARGIN_MM;
|
|
51
|
+
return { minX: b.minX - m, minY: b.minY - m, maxX: b.maxX + m, maxY: b.maxY + m };
|
|
52
|
+
};
|
|
53
|
+
const get = (s) => (s === "pads" ? padBox() : fp.bbox[s]);
|
|
54
|
+
if (want) {
|
|
55
|
+
const box = get(want);
|
|
56
|
+
if (!usable(box)) {
|
|
57
|
+
throw new KicadImportError(`footprint "${fp.name}" has no usable ${want} outline` +
|
|
58
|
+
(want === "pads" ? " (no pads)" : ` (no non-degenerate graphics on that layer)`));
|
|
59
|
+
}
|
|
60
|
+
return { source: want, box };
|
|
61
|
+
}
|
|
62
|
+
for (const s of ["edge", "courtyard", "fab", "pads"]) {
|
|
63
|
+
const box = get(s);
|
|
64
|
+
if (usable(box))
|
|
65
|
+
return { source: s, box };
|
|
66
|
+
}
|
|
67
|
+
throw new KicadImportError(`footprint "${fp.name}" has no pads and no Edge.Cuts, courtyard or fab graphics`);
|
|
68
|
+
}
|
|
69
|
+
function sanitize(s, re) {
|
|
70
|
+
return s.replace(re, "_");
|
|
71
|
+
}
|
|
72
|
+
const OUTLINE_LABEL = {
|
|
73
|
+
edge: "Edge.Cuts",
|
|
74
|
+
courtyard: "courtyard",
|
|
75
|
+
fab: "fab layer",
|
|
76
|
+
pads: `pad bounding box + ${PAD_OUTLINE_MARGIN_MM} mm`,
|
|
77
|
+
};
|
|
78
|
+
/** Convert a parsed footprint into a part.json fragment (PartSpecInput). */
|
|
79
|
+
export function kicadToSpecFragment(fp, opts) {
|
|
80
|
+
const { source, box } = chooseOutline(fp, opts.outline);
|
|
81
|
+
const ox = box.minX;
|
|
82
|
+
const oy = box.minY;
|
|
83
|
+
const pins = [];
|
|
84
|
+
const holes = [];
|
|
85
|
+
const usedKeys = new Set();
|
|
86
|
+
const byNumber = new Map();
|
|
87
|
+
const order = [];
|
|
88
|
+
for (const pad of fp.pads) {
|
|
89
|
+
const xMm = r4(pad.x - ox);
|
|
90
|
+
const yMm = r4(pad.y - oy);
|
|
91
|
+
if (pad.type === "np_thru_hole") {
|
|
92
|
+
const dMm = r4(pad.drill ?? Math.min(pad.w, pad.h));
|
|
93
|
+
if (!(dMm > 0)) {
|
|
94
|
+
throw new KicadImportError(`line ${pad.line}: np_thru_hole pad has no positive drill or size, cannot make a hole`);
|
|
95
|
+
}
|
|
96
|
+
holes.push({ xMm, yMm, dMm });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// Aperture-only pads (F.Mask / F.Paste with no copper layer) are not electrical: skip
|
|
100
|
+
// them. A pad without a (layers) list is treated as copper.
|
|
101
|
+
if (pad.layers.length > 0 && !pad.layers.some((l) => l.endsWith(".Cu")))
|
|
102
|
+
continue;
|
|
103
|
+
const base = "p" + sanitize(pad.number, /[^A-Za-z0-9_.+-]/g);
|
|
104
|
+
let key = base;
|
|
105
|
+
let name = pad.number || "pad";
|
|
106
|
+
for (let i = 2; usedKeys.has(key); i++)
|
|
107
|
+
key = `${base}_${i}`;
|
|
108
|
+
usedKeys.add(key);
|
|
109
|
+
if (pad.number) {
|
|
110
|
+
if (!byNumber.has(pad.number)) {
|
|
111
|
+
byNumber.set(pad.number, []);
|
|
112
|
+
order.push(pad.number);
|
|
113
|
+
}
|
|
114
|
+
byNumber.get(pad.number).push(key);
|
|
115
|
+
const n = byNumber.get(pad.number).length;
|
|
116
|
+
// A repeated number is named "9 (2)", not "9" or "9_2" (Python int() accepts "9_2"):
|
|
117
|
+
// the Fritzing checker's connector_numbering rule warns when an integer name is not
|
|
118
|
+
// its connector index + 1, which a second copy can never satisfy. The bus ties the
|
|
119
|
+
// copies together. (That rule still warns if integer pads are listed out of order.)
|
|
120
|
+
if (n > 1)
|
|
121
|
+
name = `${pad.number} (${n})`;
|
|
122
|
+
}
|
|
123
|
+
const ext = padExtent(pad);
|
|
124
|
+
if (!(r4(ext.w) > 0 && r4(ext.h) > 0)) {
|
|
125
|
+
throw new KicadImportError(`line ${pad.line}: pad "${pad.number}" has a zero size`);
|
|
126
|
+
}
|
|
127
|
+
const shape = pad.shape === "circle" ? "round" : pad.shape === "oval" ? "oval" : "rect";
|
|
128
|
+
const tht = pad.type === "thru_hole" && pad.drill !== undefined;
|
|
129
|
+
const geom = {
|
|
130
|
+
shape,
|
|
131
|
+
wMm: r4(ext.w),
|
|
132
|
+
hMm: r4(ext.h),
|
|
133
|
+
};
|
|
134
|
+
if (tht)
|
|
135
|
+
geom.holeMm = r4(pad.drill);
|
|
136
|
+
else
|
|
137
|
+
geom.smd = true;
|
|
138
|
+
pins.push({
|
|
139
|
+
key,
|
|
140
|
+
name: name.slice(0, 40),
|
|
141
|
+
type: tht ? "female" : "pad",
|
|
142
|
+
role: "io",
|
|
143
|
+
xMm,
|
|
144
|
+
yMm,
|
|
145
|
+
pad: geom,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const usedBusIds = new Set();
|
|
149
|
+
const buses = order
|
|
150
|
+
.filter((n) => byNumber.get(n).length > 1)
|
|
151
|
+
.map((n) => {
|
|
152
|
+
const base = "pad_" + sanitize(n, /[^A-Za-z0-9_.-]/g);
|
|
153
|
+
let id = base;
|
|
154
|
+
for (let i = 2; usedBusIds.has(id); i++)
|
|
155
|
+
id = `${base}_${i}`;
|
|
156
|
+
usedBusIds.add(id);
|
|
157
|
+
return { id, pins: byNumber.get(n) };
|
|
158
|
+
});
|
|
159
|
+
const tags = new Set(["kicad"]);
|
|
160
|
+
for (const t of (fp.tags ?? "").split(/[\s,]+/))
|
|
161
|
+
if (t && t.length <= 30)
|
|
162
|
+
tags.add(t);
|
|
163
|
+
const description = [
|
|
164
|
+
`Imported from KiCad footprint "${fp.name}" (exact pad geometry).`,
|
|
165
|
+
fp.descr ?? "",
|
|
166
|
+
`Outline from the ${OUTLINE_LABEL[source]}. Pads are kept as individual pins (pads on a 2.54 mm grid are not converted to headers).`,
|
|
167
|
+
]
|
|
168
|
+
.filter(Boolean)
|
|
169
|
+
.join("\n")
|
|
170
|
+
.slice(0, 4000);
|
|
171
|
+
const title = (opts.title ?? (fp.name || opts.slug)).slice(0, 120);
|
|
172
|
+
const spec = {
|
|
173
|
+
slug: opts.slug,
|
|
174
|
+
title,
|
|
175
|
+
tags: [...tags],
|
|
176
|
+
description,
|
|
177
|
+
outline: {
|
|
178
|
+
widthMm: r4(box.maxX - box.minX),
|
|
179
|
+
heightMm: r4(box.maxY - box.minY),
|
|
180
|
+
shape: "rect",
|
|
181
|
+
cornerRadiusMm: 0,
|
|
182
|
+
holes,
|
|
183
|
+
},
|
|
184
|
+
pins,
|
|
185
|
+
};
|
|
186
|
+
if (fp.name)
|
|
187
|
+
spec.properties = { "kicad footprint": fp.name };
|
|
188
|
+
if (buses.length)
|
|
189
|
+
spec.buses = buses;
|
|
190
|
+
return spec;
|
|
191
|
+
}
|
|
192
|
+
/** Read a .kicad_mod (utf8), parse it and convert it to a part.json fragment. */
|
|
193
|
+
export async function importKicadFile(path, opts) {
|
|
194
|
+
const text = await readFile(path, "utf8");
|
|
195
|
+
return kicadToSpecFragment(parseKicadMod(text), opts);
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/kicad/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small s-expression parser for KiCad files (.kicad_mod, KiCad 5 to 8).
|
|
3
|
+
*
|
|
4
|
+
* Grammar: a file is one top-level list. A list is `(` items `)`. An item is a list,
|
|
5
|
+
* a quoted string ("..." with backslash escapes \" \ \n \t \r) or an unquoted atom
|
|
6
|
+
* (any run of characters other than whitespace, parens and double quotes, e.g. REF**,
|
|
7
|
+
* *.Cu, Edge.Cuts, ${REFERENCE}). An unquoted atom that is entirely a decimal number
|
|
8
|
+
* becomes a JS number; everything else (including every quoted string, even "1")
|
|
9
|
+
* stays a string. KiCad files have no comments.
|
|
10
|
+
*
|
|
11
|
+
* Line numbers (1-based, counting "\n" only so CRLF files work) are kept for every
|
|
12
|
+
* list in a side table; use lineOf(list) to report errors against the source.
|
|
13
|
+
*/
|
|
14
|
+
export type SExpr = string | number | SExpr[];
|
|
15
|
+
export declare class KicadParseError extends Error {
|
|
16
|
+
/** 1-based source line the error refers to. */
|
|
17
|
+
readonly line: number;
|
|
18
|
+
constructor(message: string,
|
|
19
|
+
/** 1-based source line the error refers to. */
|
|
20
|
+
line: number);
|
|
21
|
+
}
|
|
22
|
+
/** Source line of a list produced by parseSExpr (1 for lists built by hand). */
|
|
23
|
+
export declare function lineOf(list: SExpr[]): number;
|
|
24
|
+
/** Parse a KiCad s-expression document. Exactly one top-level list is allowed. */
|
|
25
|
+
export declare function parseSExpr(text: string): SExpr[];
|
|
26
|
+
/** The head (first item) of a list if it is an atom/string, else undefined. */
|
|
27
|
+
export declare function head(list: SExpr): string | undefined;
|
|
28
|
+
/** First direct child list whose head is `name`. */
|
|
29
|
+
export declare function find(list: SExpr[], name: string): SExpr[] | undefined;
|
|
30
|
+
/** Every direct child list whose head is `name`, in order. */
|
|
31
|
+
export declare function findAll(list: SExpr[], name: string): SExpr[][];
|
|
32
|
+
/** The number at list[idx], or undefined when absent or not a number. */
|
|
33
|
+
export declare function num(list: SExpr[] | undefined, idx: number): number | undefined;
|
|
34
|
+
/** The atom at list[idx] as a string (numbers are stringified), or undefined. */
|
|
35
|
+
export declare function str(list: SExpr[] | undefined, idx: number): string | undefined;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small s-expression parser for KiCad files (.kicad_mod, KiCad 5 to 8).
|
|
3
|
+
*
|
|
4
|
+
* Grammar: a file is one top-level list. A list is `(` items `)`. An item is a list,
|
|
5
|
+
* a quoted string ("..." with backslash escapes \" \ \n \t \r) or an unquoted atom
|
|
6
|
+
* (any run of characters other than whitespace, parens and double quotes, e.g. REF**,
|
|
7
|
+
* *.Cu, Edge.Cuts, ${REFERENCE}). An unquoted atom that is entirely a decimal number
|
|
8
|
+
* becomes a JS number; everything else (including every quoted string, even "1")
|
|
9
|
+
* stays a string. KiCad files have no comments.
|
|
10
|
+
*
|
|
11
|
+
* Line numbers (1-based, counting "\n" only so CRLF files work) are kept for every
|
|
12
|
+
* list in a side table; use lineOf(list) to report errors against the source.
|
|
13
|
+
*/
|
|
14
|
+
export class KicadParseError extends Error {
|
|
15
|
+
line;
|
|
16
|
+
constructor(message,
|
|
17
|
+
/** 1-based source line the error refers to. */
|
|
18
|
+
line) {
|
|
19
|
+
super(`line ${line}: ${message}`);
|
|
20
|
+
this.line = line;
|
|
21
|
+
this.name = "KicadParseError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const NUMBER_RE = /^[-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?$/;
|
|
25
|
+
const lines = new WeakMap();
|
|
26
|
+
/** Source line of a list produced by parseSExpr (1 for lists built by hand). */
|
|
27
|
+
export function lineOf(list) {
|
|
28
|
+
return lines.get(list) ?? 1;
|
|
29
|
+
}
|
|
30
|
+
function isSpace(c) {
|
|
31
|
+
return c === " " || c === "\t" || c === "\n" || c === "\r" || c === "\f" || c === "\v";
|
|
32
|
+
}
|
|
33
|
+
/** Parse a KiCad s-expression document. Exactly one top-level list is allowed. */
|
|
34
|
+
export function parseSExpr(text) {
|
|
35
|
+
let i = text.charCodeAt(0) === 0xfeff ? 1 : 0; // skip a UTF-8 BOM
|
|
36
|
+
let line = 1;
|
|
37
|
+
const n = text.length;
|
|
38
|
+
const stack = [];
|
|
39
|
+
let root;
|
|
40
|
+
const push = (item) => {
|
|
41
|
+
const top = stack[stack.length - 1];
|
|
42
|
+
if (!top)
|
|
43
|
+
throw new KicadParseError("atom outside of any list", line);
|
|
44
|
+
top.push(item);
|
|
45
|
+
};
|
|
46
|
+
while (i < n) {
|
|
47
|
+
const c = text[i];
|
|
48
|
+
if (c === "\n") {
|
|
49
|
+
line++;
|
|
50
|
+
i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (isSpace(c)) {
|
|
54
|
+
i++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (c === "(") {
|
|
58
|
+
if (root && stack.length === 0) {
|
|
59
|
+
throw new KicadParseError("unexpected content after the top-level list", line);
|
|
60
|
+
}
|
|
61
|
+
const list = [];
|
|
62
|
+
lines.set(list, line);
|
|
63
|
+
if (stack.length)
|
|
64
|
+
stack[stack.length - 1].push(list);
|
|
65
|
+
else
|
|
66
|
+
root = list;
|
|
67
|
+
stack.push(list);
|
|
68
|
+
i++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (c === ")") {
|
|
72
|
+
if (!stack.length)
|
|
73
|
+
throw new KicadParseError("unbalanced ')'", line);
|
|
74
|
+
stack.pop();
|
|
75
|
+
i++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (c === '"') {
|
|
79
|
+
const startLine = line;
|
|
80
|
+
let s = "";
|
|
81
|
+
i++;
|
|
82
|
+
for (;;) {
|
|
83
|
+
if (i >= n)
|
|
84
|
+
throw new KicadParseError("unterminated string", startLine);
|
|
85
|
+
const d = text[i];
|
|
86
|
+
if (d === '"') {
|
|
87
|
+
i++;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (d === "\\" && i + 1 < n) {
|
|
91
|
+
const e = text[i + 1];
|
|
92
|
+
s += e === "n" ? "\n" : e === "t" ? "\t" : e === "r" ? "\r" : e;
|
|
93
|
+
if (e === "\n")
|
|
94
|
+
line++;
|
|
95
|
+
i += 2;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (d === "\n")
|
|
99
|
+
line++;
|
|
100
|
+
s += d;
|
|
101
|
+
i++;
|
|
102
|
+
}
|
|
103
|
+
if (root && stack.length === 0) {
|
|
104
|
+
throw new KicadParseError("unexpected content after the top-level list", startLine);
|
|
105
|
+
}
|
|
106
|
+
push(s);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
let j = i;
|
|
110
|
+
while (j < n) {
|
|
111
|
+
const d = text[j];
|
|
112
|
+
if (isSpace(d) || d === "(" || d === ")" || d === '"')
|
|
113
|
+
break;
|
|
114
|
+
j++;
|
|
115
|
+
}
|
|
116
|
+
const tok = text.slice(i, j);
|
|
117
|
+
i = j;
|
|
118
|
+
if (root && stack.length === 0) {
|
|
119
|
+
throw new KicadParseError("unexpected content after the top-level list", line);
|
|
120
|
+
}
|
|
121
|
+
push(NUMBER_RE.test(tok) ? Number(tok) : tok);
|
|
122
|
+
}
|
|
123
|
+
if (stack.length) {
|
|
124
|
+
throw new KicadParseError(`unbalanced '(': ${stack.length} list(s) not closed (opened here)`, lineOf(stack[stack.length - 1]));
|
|
125
|
+
}
|
|
126
|
+
if (!root)
|
|
127
|
+
throw new KicadParseError("empty document: no top-level list", 1);
|
|
128
|
+
return root;
|
|
129
|
+
}
|
|
130
|
+
/** The head (first item) of a list if it is an atom/string, else undefined. */
|
|
131
|
+
export function head(list) {
|
|
132
|
+
return Array.isArray(list) && typeof list[0] === "string" ? list[0] : undefined;
|
|
133
|
+
}
|
|
134
|
+
/** First direct child list whose head is `name`. */
|
|
135
|
+
export function find(list, name) {
|
|
136
|
+
for (const item of list)
|
|
137
|
+
if (Array.isArray(item) && item[0] === name)
|
|
138
|
+
return item;
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
/** Every direct child list whose head is `name`, in order. */
|
|
142
|
+
export function findAll(list, name) {
|
|
143
|
+
const out = [];
|
|
144
|
+
for (const item of list)
|
|
145
|
+
if (Array.isArray(item) && item[0] === name)
|
|
146
|
+
out.push(item);
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
/** The number at list[idx], or undefined when absent or not a number. */
|
|
150
|
+
export function num(list, idx) {
|
|
151
|
+
const v = list?.[idx];
|
|
152
|
+
return typeof v === "number" ? v : undefined;
|
|
153
|
+
}
|
|
154
|
+
/** The atom at list[idx] as a string (numbers are stringified), or undefined. */
|
|
155
|
+
export function str(list, idx) {
|
|
156
|
+
const v = list?.[idx];
|
|
157
|
+
if (typeof v === "string")
|
|
158
|
+
return v;
|
|
159
|
+
if (typeof v === "number")
|
|
160
|
+
return String(v);
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=sexpr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sexpr.js","sourceRoot":"","sources":["../../../src/core/kicad/sexpr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { ConnectorType, Header, Lead, PadGeometry, PartSpec, PinRole } from "./schema/part.js";
|
|
2
|
+
export type ConnectorSource = "pin" | "header" | "lead";
|
|
3
|
+
export interface ResolvedLead {
|
|
4
|
+
color: string;
|
|
5
|
+
lengthMm: number;
|
|
6
|
+
/** Where the lead leaves the board, part frame. */
|
|
7
|
+
fromXMm: number;
|
|
8
|
+
fromYMm: number;
|
|
9
|
+
direction: Lead["direction"];
|
|
10
|
+
gaugeMm: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ResolvedHeaderRef {
|
|
13
|
+
key: string;
|
|
14
|
+
kind: Header["kind"];
|
|
15
|
+
row: number;
|
|
16
|
+
col: number;
|
|
17
|
+
pitchMm: number;
|
|
18
|
+
orientation: Header["orientation"];
|
|
19
|
+
}
|
|
20
|
+
export interface ResolvedConnector {
|
|
21
|
+
/** Connector number N. */
|
|
22
|
+
id: number;
|
|
23
|
+
/** fzp connector id: connectorN. */
|
|
24
|
+
fzpId: string;
|
|
25
|
+
/** SVG element ids. */
|
|
26
|
+
svgId: string;
|
|
27
|
+
terminalId: string;
|
|
28
|
+
/** Only leads have a leg (bendable wire). A connector has EITHER a leg or a terminal. */
|
|
29
|
+
legId?: string;
|
|
30
|
+
key: string;
|
|
31
|
+
name: string;
|
|
32
|
+
label: string;
|
|
33
|
+
description: string;
|
|
34
|
+
role: PinRole;
|
|
35
|
+
type: ConnectorType;
|
|
36
|
+
source: ConnectorSource;
|
|
37
|
+
/** Connector centre in the PART frame (mm). For a lead: its free end. */
|
|
38
|
+
xMm: number;
|
|
39
|
+
yMm: number;
|
|
40
|
+
pad: PadGeometry;
|
|
41
|
+
/** true = through hole (copper0 + copper1); false = SMD (copper1 only). */
|
|
42
|
+
tht: boolean;
|
|
43
|
+
header?: ResolvedHeaderRef;
|
|
44
|
+
lead?: ResolvedLead;
|
|
45
|
+
}
|
|
46
|
+
export interface ResolvedBus {
|
|
47
|
+
id: string;
|
|
48
|
+
connectorIds: number[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Canvas frame. A part-frame point (x, y) lands on the canvas at
|
|
52
|
+
* (x + offsetXMm, y + offsetYMm). The canvas is the SVG size; it grows past the
|
|
53
|
+
* outline to hold leads and overhanging pins, and shifts so the first header pin
|
|
54
|
+
* sits on a 100 mil multiple (parts with headers must seat in a breadboard).
|
|
55
|
+
*/
|
|
56
|
+
export interface Frame {
|
|
57
|
+
offsetXMm: number;
|
|
58
|
+
offsetYMm: number;
|
|
59
|
+
widthMm: number;
|
|
60
|
+
heightMm: number;
|
|
61
|
+
}
|
|
62
|
+
export interface ResolvedPart {
|
|
63
|
+
spec: PartSpec;
|
|
64
|
+
/** Sorted by id ascending. */
|
|
65
|
+
connectors: ResolvedConnector[];
|
|
66
|
+
buses: ResolvedBus[];
|
|
67
|
+
frame: Frame;
|
|
68
|
+
/** True when at least one connector is through-hole (pcb view lists copper0). */
|
|
69
|
+
hasTht: boolean;
|
|
70
|
+
/** pinIds after allocation (may contain new keys compared to spec.pinIds). */
|
|
71
|
+
pinIds: Record<string, number>;
|
|
72
|
+
/** Keys that were newly allocated in this resolve (the caller should persist them). */
|
|
73
|
+
newPinKeys: string[];
|
|
74
|
+
}
|
|
75
|
+
export declare class ResolveError extends Error {
|
|
76
|
+
readonly path: string;
|
|
77
|
+
constructor(message: string, path: string);
|
|
78
|
+
}
|
|
79
|
+
/** Every connector key in spec order (pins, then headers row-major, then leads). */
|
|
80
|
+
export declare function listPinKeys(spec: PartSpec): string[];
|
|
81
|
+
/**
|
|
82
|
+
* Allocate connector numbers: keep existing pinIds entries, give each new key the
|
|
83
|
+
* next free number (max + 1), never reuse a number. Keys that disappeared from the
|
|
84
|
+
* spec keep their entry so the number stays reserved.
|
|
85
|
+
*/
|
|
86
|
+
export declare function allocatePinIds(spec: PartSpec): {
|
|
87
|
+
pinIds: Record<string, number>;
|
|
88
|
+
added: string[];
|
|
89
|
+
};
|
|
90
|
+
export declare function leadEnd(l: Lead): {
|
|
91
|
+
xMm: number;
|
|
92
|
+
yMm: number;
|
|
93
|
+
};
|
|
94
|
+
/** Axis-aligned bbox of everything that must fit on the canvas, part frame, mm. */
|
|
95
|
+
export declare function contentBounds(spec: PartSpec, connectors: ResolvedConnector[]): {
|
|
96
|
+
minX: number;
|
|
97
|
+
minY: number;
|
|
98
|
+
maxX: number;
|
|
99
|
+
maxY: number;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Compute the canvas frame. Offsets are chosen so (a) all content has non-negative
|
|
103
|
+
* canvas coordinates and (b) if the part has headers, the first header pin lands on
|
|
104
|
+
* a 100 mil multiple (the smallest extra shift that achieves it). The canvas size is
|
|
105
|
+
* rounded up to whole mils so the viewBox has clean numbers.
|
|
106
|
+
*/
|
|
107
|
+
export declare function computeFrame(spec: PartSpec, connectors: ResolvedConnector[]): Frame;
|
|
108
|
+
export declare function resolvePart(spec: PartSpec): ResolvedPart;
|
|
109
|
+
/** Part-frame mm -> canvas mm. */
|
|
110
|
+
export declare function toCanvas(frame: Frame, xMm: number, yMm: number): {
|
|
111
|
+
x: number;
|
|
112
|
+
y: number;
|
|
113
|
+
};
|