feat: add mil unit support across position/coordinate commands (#162)

* feat(units): add mil unit support across all position/coordinate commands

KiCad natively supports mils, so the MCP server should too. Added "mil"
as a valid unit option in tool schemas and updated all unit-to-nanometer
scale conversions across component, routing, outline, view, and IPC
handler code paths. 1 mil = 25400 nm (0.0254 mm).

Also fixes a pre-existing mypy overload error in pin_locator.py (str cast
on dict.get key) that was blocking pre-commit on any Python file change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(units): add mil to TypeScript tool schemas

The Python-side mil support was added but the actual input validation
happens in the TypeScript/Zod schemas. Updated all z.enum(["mm", "inch"])
to include "mil" across board, component, routing, design-rules, and
export tool definitions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tools): replace CP-1252 mojibake with correct Unicode in board.ts

Replace U+00C3 U+00D7 (×) with U+00D7 (×) in add_logo size output string.
Character was mangled when file was saved as CP-1252 instead of UTF-8.

* fix: restore em-dash and fix pre-commit mypy in component/routing

component.py: replace CP-1252 mojibake (â€") with correct Unicode
em-dash (—) in the 'Add to board first' comment. Addresses
maintainer review on PR #162.

routing.py: annotate ex/ey as float at first assignment site in
_point_to_segment_distance_nm so mypy pre-commit hook passes
cleanly on this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gavin Colonese
2026-05-22 17:29:34 -04:00
committed by GitHub
parent 76e644e4ef
commit fa6cdcc0cd
13 changed files with 239 additions and 71 deletions

View File

@@ -96,7 +96,11 @@ class ComponentCommands:
}
# Set position
scale = 1000000 if position["unit"] == "mm" else 25400000 # mm or inch to nm
scale = (
1000000
if position["unit"] == "mm"
else (25400 if position["unit"] == "mil" else 25400000)
) # mm, mil, or inch to nm
x_nm = int(position["x"] * scale)
y_nm = int(position["y"] * scale)
module.SetPosition(pcbnew.VECTOR2I(x_nm, y_nm))
@@ -196,7 +200,11 @@ class ComponentCommands:
}
# Set new position
scale = 1000000 if position["unit"] == "mm" else 25400000 # mm or inch to nm
scale = (
1000000
if position["unit"] == "mm"
else (25400 if position["unit"] == "mil" else 25400000)
) # mm, mil, or inch to nm
x_nm = int(position["x"] * scale)
y_nm = int(position["y"] * scale)
module.SetPosition(pcbnew.VECTOR2I(x_nm, y_nm))
@@ -987,7 +995,11 @@ class ComponentCommands:
# Set position if provided, otherwise use offset from original
if position:
scale = 1000000 if position.get("unit", "mm") == "mm" else 25400000
scale = (
1000000
if position.get("unit", "mm") == "mm"
else (25400 if position.get("unit", "mm") == "mil" else 25400000)
) # mm, mil, or inch to nm
x_nm = int(position["x"] * scale)
y_nm = int(position["y"] * scale)
new_module.SetPosition(pcbnew.VECTOR2I(x_nm, y_nm))
@@ -1048,7 +1060,9 @@ class ComponentCommands:
# Convert spacing to nm
unit = start_position.get("unit", "mm")
scale = 1000000 if unit == "mm" else 25400000 # mm or inch to nm
scale = (
1000000 if unit == "mm" else (25400 if unit == "mil" else 25400000)
) # mm, mil, or inch to nm
spacing_x_nm = int(spacing_x * scale)
spacing_y_nm = int(spacing_y * scale)