diff --git a/lib/components/primitive-components/SilkscreenText.ts b/lib/components/primitive-components/SilkscreenText.ts index 3cf358c4d..a546967ec 100644 --- a/lib/components/primitive-components/SilkscreenText.ts +++ b/lib/components/primitive-components/SilkscreenText.ts @@ -152,6 +152,9 @@ export class SilkscreenText extends PrimitiveComponent< subcircuit_id: subcircuit?.subcircuit_id ?? undefined, pcb_group_id: this.getGroup()?.pcb_group_id ?? undefined, is_knockout: props.isKnockout, + ...(props.mirrored === undefined + ? {} + : { is_mirrored: props.mirrored }), knockout_padding: knockoutPadding, }) this.pcb_silkscreen_text_ids.push( diff --git a/lib/utils/createComponentsFromCircuitJson.ts b/lib/utils/createComponentsFromCircuitJson.ts index 7c7660223..e7709870c 100644 --- a/lib/utils/createComponentsFromCircuitJson.ts +++ b/lib/utils/createComponentsFromCircuitJson.ts @@ -586,6 +586,7 @@ export const createComponentsFromCircuitJson = ( ) } else { const silkscreenText = new SilkscreenText({ + mirrored: elm.is_mirrored, anchorAlignment: elm.anchor_alignment || "center", // Footprinter-generated reference text is a placeholder that should // resolve to the component name. A literal from the explicit diff --git a/lib/utils/createPinrowSilkscreenText.ts b/lib/utils/createPinrowSilkscreenText.ts index 924aa2d82..792cee50e 100644 --- a/lib/utils/createPinrowSilkscreenText.ts +++ b/lib/utils/createPinrowSilkscreenText.ts @@ -31,6 +31,7 @@ export const createPinrowSilkscreenText = ({ pcbX: isNaN(elm.anchor_position.x) ? 0 : elm.anchor_position.x, pcbY: elm.anchor_position.y, pcbRotation: readableRotation ?? 0, + mirrored: elm.is_mirrored, }) silkscreenText._footprinterFontSize = elm.font_size + 0.2 return silkscreenText diff --git a/package.json b/package.json index 9017c9eb2..3d02bbc49 100644 --- a/package.json +++ b/package.json @@ -138,5 +138,8 @@ "@tscircuit/circuit-json-util": "^0.0.117", "@tscircuit/props": "^0.0.672", "circuit-json": "^0.0.507" + }, + "patchedDependencies": { + "@tscircuit/props@0.0.672": "patches/@tscircuit%2Fprops@0.0.672.patch" } } diff --git a/patches/@tscircuit%2Fprops@0.0.672.patch b/patches/@tscircuit%2Fprops@0.0.672.patch new file mode 100644 index 000000000..a914c7f57 --- /dev/null +++ b/patches/@tscircuit%2Fprops@0.0.672.patch @@ -0,0 +1,38 @@ +--- a/dist/index.js ++++ b/dist/index.js +@@ -19228,6 +19228,8 @@ + anchorAlignment: ninePointAnchor.default("center"), + font: z142.enum(["tscircuit2024"]).optional(), + fontSize: length9.optional(), ++ /** Explicit text mirroring, independent of layer. Omitted preserves the renderer default. */ ++ mirrored: z142.boolean().optional(), + /** + * If true, text will knock out underlying silkscreen + */ +--- a/dist/index.d.ts ++++ b/dist/index.d.ts +@@ -239229,6 +239229,8 @@ + anchorAlignment: z.ZodDefault>; + font: z.ZodOptional>; + fontSize: z.ZodOptional, number, string | number>>; ++ /** Explicit text mirroring, independent of layer. Omitted preserves the renderer default. */ ++ mirrored: z.ZodOptional; + /** + * If true, text will knock out underlying silkscreen + */ +@@ -239285,6 +239287,7 @@ + pcbSx?: PcbSx | undefined; + pcbRelative?: boolean | undefined; + relative?: boolean | undefined; ++ mirrored?: boolean | undefined; + isKnockout?: boolean | undefined; + knockoutPadding?: number | undefined; + knockoutPaddingLeft?: number | undefined; +@@ -239333,6 +239336,7 @@ + pcbRelative?: boolean | undefined; + relative?: boolean | undefined; + anchorAlignment?: "top_left" | "top_center" | "top_right" | "center_left" | "center" | "center_right" | "bottom_left" | "bottom_center" | "bottom_right" | undefined; ++ mirrored?: boolean | undefined; + isKnockout?: boolean | undefined; + knockoutPadding?: string | number | undefined; + knockoutPaddingLeft?: string | number | undefined; diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 000000000..b49f8600b --- /dev/null +++ b/patches/README.md @@ -0,0 +1,3 @@ +The props patch mirrors https://github.com/tscircuit/props/pull/885 and allows this PR's mirroring regression tests and type declarations to run against the currently published `@tscircuit/props@0.0.672`. + +Before merging, replace the patch with a released props version containing that PR (update both the development dependency and the override). The patch is only used for local/CI development; consumers need the corresponding props release. diff --git a/tests/components/primitive-components/__snapshots__/silkscreen-text-mirrored-pcb.snap.svg b/tests/components/primitive-components/__snapshots__/silkscreen-text-mirrored-pcb.snap.svg new file mode 100644 index 000000000..33ac5caac --- /dev/null +++ b/tests/components/primitive-components/__snapshots__/silkscreen-text-mirrored-pcb.snap.svg @@ -0,0 +1 @@ +ABC trueABC falseABC undefinedABC trueABC falseABC undefined \ No newline at end of file diff --git a/tests/components/primitive-components/silkscreen-text-mirrored.test.tsx b/tests/components/primitive-components/silkscreen-text-mirrored.test.tsx new file mode 100644 index 000000000..a266c3d0a --- /dev/null +++ b/tests/components/primitive-components/silkscreen-text-mirrored.test.tsx @@ -0,0 +1,43 @@ +import { expect, test } from "bun:test" +import { getTestFixture } from "tests/fixtures/get-test-fixture" + +test("silkscreen text preserves explicit mirroring through layer flips", () => { + const { circuit } = getTestFixture() + const mirrors = [true, false, undefined] + circuit.add( + + {(["top", "bottom"] as const).flatMap((layer, row) => + mirrors.map((mirrored, i) => ( + + + + } + /> + )), + )} + , + ) + circuit.render() + const texts = circuit.db.pcb_silkscreen_text + .list() + .filter((t) => t.text.startsWith("ABC")) + expect(texts).toHaveLength(6) + texts.forEach((text, i) => { + expect(text.is_mirrored).toBe(mirrors[i % 3]) + if (mirrors[i % 3] === undefined) + expect(text).not.toHaveProperty("is_mirrored") + expect(text.layer).toBe(i < 3 ? "top" : "bottom") + }) + expect(circuit).toMatchPcbSnapshot(import.meta.path) +})