feat(schematic): expose justify directive on field labels via MCP

Add optional `justify` property to `fieldPositions` entries in
`edit_schematic_component` and to `set_schematic_component_property`.

Changes:
- `python/kicad_interface.py`: new `_set_justify_on_property()` helper
  that adds/replaces/removes the `(justify ...)` token inside a property's
  `(effects ...)` block. Passing "center" (the KiCad default) removes the
  directive entirely. Integrated into `_set_property_in_block()` (for the
  `properties` dict path) and into the `field_positions` loop in
  `_handle_edit_schematic_component()`. `_handle_set_schematic_component_property()`
  now forwards `justify` from params through to the spec dict.
  Also fixes pre-existing mypy type-ignore on `circle.radius` (kipy stub).
- `src/tools/schematic.ts`: extend the `fieldPositions` Zod schema to accept
  `justify?: string | string[]` (array form normalised to a space-separated
  string before the Python call). Add `justify?: string` to
  `set_schematic_component_property`.
- `python/commands/routing.py`: fix pre-existing mypy error — annotate `ex`
  and `ey` as `float` in `_point_to_segment_distance_nm`.
- `python/commands/pin_locator.py`: fix pre-existing mypy error — use explicit
  `str()` cast on `pin_data["number"]` before `dict.get()` call.
- `tests/test_schematic_field_justify.py`: 14 unit + integration tests
  covering add/replace/remove of the justify directive and backward
  compatibility (calls without justify leave existing directives untouched).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gavin Colonese
2026-05-22 13:43:43 -04:00
parent d2ea43d061
commit 7313037dc3
4 changed files with 409 additions and 7 deletions

View File

@@ -188,11 +188,21 @@ use edit_component instead.`,
x: z.number(),
y: z.number(),
angle: z.number().optional().default(0),
justify: z
.union([z.string(), z.array(z.string())])
.optional()
.describe(
'Text justification: "left", "right", "center", "top", "bottom", or combined ' +
'"left top" / "right bottom". Array form ["left", "top"] is also accepted. ' +
'Omit to leave the existing justify unchanged. Pass "center" to reset to ' +
"the KiCad default (removes the justify directive).",
),
}),
)
.optional()
.describe(
'Reposition field labels: map of field name to {x, y, angle} (e.g. {"Reference": {"x": 12.5, "y": 17.0}})',
"Reposition field labels: map of field name to {x, y, angle, justify?} " +
'(e.g. {"Reference": {"x": 12.5, "y": 17.0, "justify": "left"}})',
),
properties: z
.record(
@@ -240,7 +250,10 @@ use edit_component instead.`,
footprint?: string;
value?: string;
newReference?: string;
fieldPositions?: Record<string, { x: number; y: number; angle?: number }>;
fieldPositions?: Record<
string,
{ x: number; y: number; angle?: number; justify?: string | string[] }
>;
properties?: Record<
string,
| string
@@ -255,6 +268,16 @@ use edit_component instead.`,
>;
removeProperties?: string[];
}) => {
// Normalise array-form justify (["left", "top"]) to the space-separated
// string form ("left top") that the Python backend expects.
if (args.fieldPositions) {
for (const fieldName of Object.keys(args.fieldPositions)) {
const pos = args.fieldPositions[fieldName];
if (Array.isArray(pos.justify)) {
pos.justify = (pos.justify as string[]).join(" ");
}
}
}
const result = await callKicadScript("edit_schematic_component", args);
if (result.success) {
const updated = result.updated ?? {};
@@ -337,6 +360,14 @@ with the \`properties\` parameter instead.`,
"Hide the property text on the schematic canvas. Defaults to true for newly-created custom properties.",
),
fontSize: z.number().optional().describe("Font size in mm for the label (default: 1.27)"),
justify: z
.string()
.optional()
.describe(
'Text justification for the property label. KiCad alignment keywords: "left", "right", ' +
'"center", "top", "bottom", or combined e.g. "left top". Omit to leave unchanged. ' +
'Pass "center" to reset to the KiCad default.',
),
},
async (args: {
schematicPath: string;
@@ -348,6 +379,7 @@ with the \`properties\` parameter instead.`,
angle?: number;
hide?: boolean;
fontSize?: number;
justify?: string;
}) => {
const result = await callKicadScript("set_schematic_component_property", args);
if (result.success) {