From 6fff8823e41376de6d3e1ea979e6db44df3632a5 Mon Sep 17 00:00:00 2001 From: lucaferri-dev Date: Sun, 8 Mar 2026 15:39:38 +0000 Subject: [PATCH 1/2] fix: support jst_ph_N format and dynamic PH silkscreen sizing Parse trailing pin count from footprint strings like jst_ph_4, jst_ph_6. Also compute PH silkscreen body width from pin count instead of hardcoding. Closes #495 Co-Authored-By: Claude Opus 4.6 --- src/fn/jst.ts | 23 ++++++++++++++----- .../__snapshots__/jst.test.tsjst2_ph.snap.svg | 2 +- tests/__snapshots__/jst_ph_4.snap.svg | 1 + tests/jst_ph_4.test.ts | 15 ++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 tests/__snapshots__/jst_ph_4.snap.svg create mode 100644 tests/jst_ph_4.test.ts diff --git a/src/fn/jst.ts b/src/fn/jst.ts index 39a49981..e5cf5537 100644 --- a/src/fn/jst.ts +++ b/src/fn/jst.ts @@ -150,17 +150,20 @@ function generateSilkscreenBody( numPins?: number, p?: number, ): PcbSilkscreenPath { - if (variant === "ph") { + if (variant === "ph" && numPins && p) { + const pinSpan = (numPins - 1) * p + const bodyLeft = -pinSpan / 2 - 1.5 + const bodyRight = pinSpan / 2 + 1.5 return { type: "pcb_silkscreen_path", layer: "top", pcb_component_id: "", route: [ - { x: -3, y: 3 }, - { x: 3, y: 3 }, - { x: 3, y: -2 }, - { x: -3, y: -2 }, - { x: -3, y: 3 }, + { x: bodyLeft, y: 3 }, + { x: bodyRight, y: 3 }, + { x: bodyRight, y: -2 }, + { x: bodyLeft, y: -2 }, + { x: bodyLeft, y: 3 }, ], stroke_width: 0.1, pcb_silkscreen_path_id: "", @@ -222,6 +225,8 @@ export const jst = ( const str = typeof raw_params.string === "string" ? raw_params.string : "" const match = str.match(/(?:^|_)jst(\d+)(?:_|$)/) const zhMatch = str.match(/(?:^|_)zh(\d+)(?:_|$)/) + // Match trailing pin count after variant, e.g. jst_ph_4, jst_sh_6 + const trailingMatch = str.match(/(?:^|_)(\d+)(?:_|$)/) if (match && match[1]) { const parsed = Number.parseInt(match[1], 10) if (!Number.isNaN(parsed)) { @@ -234,6 +239,12 @@ export const jst = ( numPins = parsed } } + if (typeof numPins !== "number" && trailingMatch && trailingMatch[1]) { + const parsed = Number.parseInt(trailingMatch[1], 10) + if (!Number.isNaN(parsed)) { + numPins = parsed + } + } if (typeof numPins !== "number") { throw new Error( diff --git a/tests/__snapshots__/jst.test.tsjst2_ph.snap.svg b/tests/__snapshots__/jst.test.tsjst2_ph.snap.svg index e01f15a3..fdda7f65 100644 --- a/tests/__snapshots__/jst.test.tsjst2_ph.snap.svg +++ b/tests/__snapshots__/jst.test.tsjst2_ph.snap.svg @@ -1 +1 @@ -{REF} \ No newline at end of file +{REF} \ No newline at end of file diff --git a/tests/__snapshots__/jst_ph_4.snap.svg b/tests/__snapshots__/jst_ph_4.snap.svg new file mode 100644 index 00000000..e341b829 --- /dev/null +++ b/tests/__snapshots__/jst_ph_4.snap.svg @@ -0,0 +1 @@ +{REF} \ No newline at end of file diff --git a/tests/jst_ph_4.test.ts b/tests/jst_ph_4.test.ts new file mode 100644 index 00000000..32e9c093 --- /dev/null +++ b/tests/jst_ph_4.test.ts @@ -0,0 +1,15 @@ +import { expect, test } from "bun:test" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" +import { fp } from "../src/footprinter" + +test("jst_ph_4 generates 4 pads", () => { + const circuitJson = fp.string("jst_ph_4").circuitJson() + + const platedHoles = circuitJson.filter( + (e) => e.type === "pcb_plated_hole", + ) + expect(platedHoles.length).toBe(4) + + const svgContent = convertCircuitJsonToPcbSvg(circuitJson) + expect(svgContent).toMatchSvgSnapshot(import.meta.path) +}) From 022a391a3310b9ea65c43a03ca8505a5e8eea294 Mon Sep 17 00:00:00 2001 From: lucaferri-dev Date: Sun, 8 Mar 2026 19:19:46 +0000 Subject: [PATCH 2/2] fix: add kicad parity test for jst_ph_4 and fix formatting Add kicad parity test comparing jst_ph_4 against JST_PH_B4B-PH-K_1x04_P2.00mm_Vertical from KiCad library. Also fix biome formatting in jst_ph_4.test.ts. Co-Authored-By: Claude Opus 4.6 --- tests/jst_ph_4.test.ts | 4 +--- .../__snapshots__/jst_ph_4.snap.svg | 1 + .../jst_ph_4_boolean_difference.snap.svg | 9 +++++++++ .../kicad-parity/jst_ph4_kicad_parity.test.ts | 18 ++++++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 tests/kicad-parity/__snapshots__/jst_ph_4.snap.svg create mode 100644 tests/kicad-parity/__snapshots__/jst_ph_4_boolean_difference.snap.svg create mode 100644 tests/kicad-parity/jst_ph4_kicad_parity.test.ts diff --git a/tests/jst_ph_4.test.ts b/tests/jst_ph_4.test.ts index 32e9c093..e157ac60 100644 --- a/tests/jst_ph_4.test.ts +++ b/tests/jst_ph_4.test.ts @@ -5,9 +5,7 @@ import { fp } from "../src/footprinter" test("jst_ph_4 generates 4 pads", () => { const circuitJson = fp.string("jst_ph_4").circuitJson() - const platedHoles = circuitJson.filter( - (e) => e.type === "pcb_plated_hole", - ) + const platedHoles = circuitJson.filter((e) => e.type === "pcb_plated_hole") expect(platedHoles.length).toBe(4) const svgContent = convertCircuitJsonToPcbSvg(circuitJson) diff --git a/tests/kicad-parity/__snapshots__/jst_ph_4.snap.svg b/tests/kicad-parity/__snapshots__/jst_ph_4.snap.svg new file mode 100644 index 00000000..4e22b9e2 --- /dev/null +++ b/tests/kicad-parity/__snapshots__/jst_ph_4.snap.svg @@ -0,0 +1 @@ +{REF}Diff: 43.59% \ No newline at end of file diff --git a/tests/kicad-parity/__snapshots__/jst_ph_4_boolean_difference.snap.svg b/tests/kicad-parity/__snapshots__/jst_ph_4_boolean_difference.snap.svg new file mode 100644 index 00000000..bc378818 --- /dev/null +++ b/tests/kicad-parity/__snapshots__/jst_ph_4_boolean_difference.snap.svg @@ -0,0 +1,9 @@ +JST_PH_B4B-PH-K_1x04_P2.00mm_Vertical - Alignment Analysis (Footprinter vs KiCad)jst_ph_4KiCad: JST_PH_B4B-PH-K_1x04_P2.00mm_VerticalPerfect alignment = complete overlap \ No newline at end of file diff --git a/tests/kicad-parity/jst_ph4_kicad_parity.test.ts b/tests/kicad-parity/jst_ph4_kicad_parity.test.ts new file mode 100644 index 00000000..7c04248c --- /dev/null +++ b/tests/kicad-parity/jst_ph4_kicad_parity.test.ts @@ -0,0 +1,18 @@ +import { expect, test } from "bun:test" +import { compareFootprinterVsKicad } from "../fixtures/compareFootprinterVsKicad" +import { convertCircuitJsonToPcbSvg } from "circuit-to-svg" + +test("parity/jst_ph_4", async () => { + const { avgRelDiff, combinedFootprintElements, booleanDifferenceSvg } = + await compareFootprinterVsKicad( + "jst_ph_4", + "Connector_JST.pretty/JST_PH_B4B-PH-K_1x04_P2.00mm_Vertical.circuit.json", + ) + + const svgContent = convertCircuitJsonToPcbSvg(combinedFootprintElements) + expect(svgContent).toMatchSvgSnapshot(import.meta.path, "jst_ph_4") + expect(booleanDifferenceSvg).toMatchSvgSnapshot( + import.meta.path, + "jst_ph_4_boolean_difference", + ) +}, 10000)