Skip to content

Add headermodule footprints with silkscreen labels - #761

Merged
seveibar merged 5 commits into
mainfrom
agent/pinrow-silkscreen-label
Aug 2, 2026
Merged

Add headermodule footprints with silkscreen labels#761
seveibar merged 5 commits into
mainfrom
agent/pinrow-silkscreen-label

Conversation

@seveibar

@seveibar seveibar commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Add a dedicated headermodule footprint function for pluggable female-header board modules.
  • Keep pinrow available for ordinary pin headers; headermodule shares its geometry and pin-label behavior.
  • Add silkscreenborder to draw a rectangular top-silkscreen outline around the header.
  • Add silkscreenlabel(NAME) to replace the default reference text with a literal, centered module label.
  • Keep the existing {PINn} placeholders so core can resolve each pin to its logical label.
  • Add circuit-JSON/SVG regression coverage for both pinrow and headermodule, including the independent p=2.54mm / py=15.24mm XIAO row spacing.

This lets module footprints communicate their purpose directly in the footprint string while providing a consistent outline, a readable title such as XIAO RP2040, and per-pin silkscreen labels.

Validation

  • bun test tests/pinrow.test.ts
  • bun run build
  • bunx biome check src/fn/headermodule.ts src/fn/index.ts src/footprinter.ts tests/pinrow.test.ts

@seveibar seveibar changed the title Add pinrow silkscreen borders and labels Add headermodule footprints with silkscreen labels Aug 2, 2026
@seveibar
seveibar marked this pull request as ready for review August 2, 2026 23:01
@seveibar
seveibar requested a review from techmannih as a code owner August 2, 2026 23:01
@seveibar
seveibar merged commit 384a000 into main Aug 2, 2026
5 checks passed
Comment thread tests/pinrow.test.ts
Comment on lines +26 to +133
test("pinrow silkscreen border and custom module label", () => {
const definition =
"pinrow14_rows2_p2.54mm_py15.24mm_female_silkscreenborder_silkscreenlabel(XIAO RP2040)"
const circuitJson = fp.string(definition).circuitJson()
const svgContent = convertCircuitJsonToPcbSvg(circuitJson, {
showCourtyards: true,
})
const params = fp.string(definition).json()

expect(params).toMatchObject({
fn: "pinrow",
num_pins: 14,
rows: 2,
female: true,
silkscreenborder: true,
silkscreenlabel: "XIAO RP2040",
})
expect(
circuitJson.filter((element) => element.type === "pcb_silkscreen_path"),
).toHaveLength(1)
expect(
circuitJson.find(
(element) =>
element.type === "pcb_silkscreen_text" &&
element.text === "XIAO RP2040",
),
).toMatchObject({
anchor_position: { x: 0, y: 0 },
anchor_alignment: "center",
})
expect(svgContent).toMatchSvgSnapshot(
import.meta.path,
"pinrow14_silkscreenborder_silkscreenlabel",
)
})

test("headermodule silkscreen border and custom module label", () => {
const definition =
"headermodule14_rows2_p2.54mm_py15.24mm_female_silkscreenborder_silkscreenlabel(XIAO RP2040)"
const circuitJson = fp.string(definition).circuitJson()
const svgContent = convertCircuitJsonToPcbSvg(circuitJson, {
showCourtyards: true,
})
const params = fp.string(definition).json()

expect(params).toMatchObject({
fn: "headermodule",
num_pins: 14,
rows: 2,
p: 2.54,
py: 15.24,
female: true,
silkscreenborder: true,
silkscreenlabel: "XIAO RP2040",
})
expect(
circuitJson.filter(
(element) =>
element.type === "pcb_silkscreen_path" &&
element.pcb_component_id === "",
),
).toHaveLength(1)
const pin1Arrow = circuitJson.find(
(element) =>
element.type === "pcb_silkscreen_path" &&
element.pcb_silkscreen_path_id === "pin_marker_1",
)
expect(pin1Arrow).toMatchObject({ pcb_component_id: "pin_marker_1" })
if (pin1Arrow?.type === "pcb_silkscreen_path") {
expect(pin1Arrow.route).toHaveLength(4)
expect(pin1Arrow.route[0]).toMatchObject({ x: -8.52, y: 7.62 })
expect(pin1Arrow.route[1]?.x).toBe(-9.12)
expect(pin1Arrow.route[1]?.y).toBeCloseTo(7.02)
expect(pin1Arrow.route[2]?.x).toBe(-9.12)
expect(pin1Arrow.route[2]?.y).toBeCloseTo(8.22)
expect(pin1Arrow.route[3]).toMatchObject({ x: -8.52, y: 7.62 })
}
const platedHoleYs = circuitJson
.filter((element) => element.type === "pcb_plated_hole")
.map((element) => element.y)
.filter((y): y is number => typeof y === "number")
expect([...new Set(platedHoleYs)].sort((a, b) => a - b)).toEqual([
-7.62, 7.62,
])
const platedHoleXs = circuitJson
.filter((element) => element.type === "pcb_plated_hole")
.map((element) => element.x)
.filter((x): x is number => typeof x === "number")
expect(
[...new Set(platedHoleXs)]
.sort((a, b) => a - b)
.map((x) => Number(x.toFixed(2))),
).toEqual([-7.62, -5.08, -2.54, 0, 2.54, 5.08, 7.62])
expect(
circuitJson.find(
(element) =>
element.type === "pcb_silkscreen_text" &&
element.text === "XIAO RP2040",
),
).toMatchObject({
anchor_position: { x: 0, y: 0 },
anchor_alignment: "center",
})
expect(svgContent).toMatchSvgSnapshot(
import.meta.path,
"headermodule14_silkscreenborder_silkscreenlabel",
)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A *.test.ts file may have AT MOST one test(...). This file (pinrow.test.ts) already contained at least one test(...) before this diff, and this PR adds two more: "pinrow silkscreen border and custom module label" (line 26) and "headermodule silkscreen border and custom module label" (line 62). The new tests must be split into separate, numbered files — e.g., pinrow2.test.ts and pinrow3.test.ts (or similarly named files). Each file should contain exactly one test(...) call.

Spotted by Graphite (based on custom rule: Custom rule)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant