circuit-json-to-tscircuit 0.0.74 → 0.0.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -672,14 +672,9 @@ var convertBoxPortMarkers = ({
672
672
 
673
673
  // lib/generate-board-schematic-tsx/get-box-pin-display-label.ts
674
674
  var getBoxPinDisplayLabel = ({
675
- schematicPort,
676
- sourcePort
677
- }) => {
678
- if (schematicPort.display_pin_label !== void 0)
679
- return schematicPort.display_pin_label;
680
- const sourceName = sourcePort?.name;
681
- return sourceName && !/^\d+$/.test(sourceName) ? sourceName : void 0;
682
- };
675
+ schematicComponent,
676
+ schematicPort
677
+ }) => schematicPort.display_pin_label ?? schematicComponent.port_labels?.[String(schematicPort.pin_number)];
683
678
 
684
679
  // lib/generate-board-schematic-tsx/convert-box-components.ts
685
680
  var DEFAULT_BOX_BODY_STROKE_WIDTH = 0.02;
@@ -737,9 +732,6 @@ var convertBoxComponent = ({
737
732
  })
738
733
  );
739
734
  primitives.push(...markers);
740
- const sourcePort = su2(circuitJson).source_port.get(
741
- schematicPort.source_port_id
742
- );
743
735
  const labelPosition = { ...edge };
744
736
  const pinNumberPosition = {
745
737
  x: (edge.x + schematicPort.center.x) / 2,
@@ -772,8 +764,8 @@ var convertBoxComponent = ({
772
764
  break;
773
765
  }
774
766
  const displayPinLabel = getBoxPinDisplayLabel({
775
- schematicPort,
776
- sourcePort
767
+ schematicComponent,
768
+ schematicPort
777
769
  });
778
770
  if (displayPinLabel)
779
771
  primitives.push(
@@ -835,6 +827,13 @@ import { su as su4 } from "@tscircuit/soup-util";
835
827
  import { schematic_text as schematic_text3 } from "circuit-json";
836
828
  import { symbols as symbols2 } from "schematic-symbols";
837
829
  import { translate as translate3 } from "transformation-matrix";
830
+ function canRenderNativeNetLabel(name) {
831
+ const selector = `net.${name}`;
832
+ if (/net\.[^\s>]*\./.test(selector) || /net\.[^\s>]*[+-]/.test(selector) || /net\.[0-9]/.test(selector)) {
833
+ return false;
834
+ }
835
+ return /^\/?[A-Za-z0-9_]+$/.test(name);
836
+ }
838
837
  var convertNetLabels = (circuitJson) => su4(circuitJson).schematic_net_label.list().flatMap((schematicNetLabel) => {
839
838
  const anchorPosition = schematicNetLabel.anchor_position ?? schematicNetLabel.center;
840
839
  const symbolName = schematicNetLabel.symbol_name;
@@ -851,6 +850,16 @@ var convertNetLabels = (circuitJson) => su4(circuitJson).schematic_net_label.lis
851
850
  displayText: schematicNetLabel.text
852
851
  });
853
852
  }
853
+ if (!symbolName && schematicNetLabel.anchor_position && canRenderNativeNetLabel(schematicNetLabel.text)) {
854
+ return [
855
+ formatElement("netlabel", {
856
+ net: schematicNetLabel.text,
857
+ schX: anchorPosition.x,
858
+ schY: anchorPosition.y,
859
+ anchorSide: schematicNetLabel.anchor_side
860
+ })
861
+ ];
862
+ }
854
863
  return [
855
864
  formatElement("schematictext", {
856
865
  text: schematicNetLabel.text,
package/dist/cli/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  convertCircuitJsonToTscircuit
4
- } from "../chunk-KDQXBFSE.js";
4
+ } from "../chunk-NQTXLCEN.js";
5
5
 
6
6
  // cli/main.ts
7
7
  import { Command } from "commander";
@@ -16,7 +16,7 @@ var package_default = {
16
16
  "./cli": "./dist/cli/main.js"
17
17
  },
18
18
  type: "module",
19
- version: "0.0.73",
19
+ version: "0.0.75",
20
20
  repository: {
21
21
  type: "git",
22
22
  url: "https://github.com/tscircuit/circuit-json-to-tscircuit.git"
package/dist/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  convertCircuitJsonToTscircuit
3
- } from "../chunk-KDQXBFSE.js";
3
+ } from "../chunk-NQTXLCEN.js";
4
4
  export {
5
5
  convertCircuitJsonToTscircuit
6
6
  };
@@ -71,9 +71,6 @@ export const convertBoxComponent = ({
71
71
  }),
72
72
  )
73
73
  primitives.push(...markers)
74
- const sourcePort = su(circuitJson).source_port.get(
75
- schematicPort.source_port_id,
76
- )
77
74
  const labelPosition = { ...edge }
78
75
  const pinNumberPosition = {
79
76
  x: (edge.x + schematicPort.center.x) / 2,
@@ -106,8 +103,8 @@ export const convertBoxComponent = ({
106
103
  break
107
104
  }
108
105
  const displayPinLabel = getBoxPinDisplayLabel({
106
+ schematicComponent,
109
107
  schematicPort,
110
- sourcePort,
111
108
  })
112
109
  if (displayPinLabel)
113
110
  primitives.push(
@@ -6,6 +6,23 @@ import type { SchematicElementConverter } from "./converter-types"
6
6
  import { convertSymbolPrimitives } from "./convert-symbol-primitives"
7
7
  import { formatElement } from "./format-attributes"
8
8
 
9
+ // Match core's preprocessSelector checks for net names. The validator is not
10
+ // exported by @tscircuit/core; runtime regression tests keep these in sync.
11
+ // https://github.com/tscircuit/core/blob/main/lib/components/base-components/PrimitiveComponent/preprocessSelector.ts
12
+ function canRenderNativeNetLabel(name: string): boolean {
13
+ const selector = `net.${name}`
14
+ if (
15
+ /net\.[^\s>]*\./.test(selector) ||
16
+ /net\.[^\s>]*[+-]/.test(selector) ||
17
+ /net\.[0-9]/.test(selector)
18
+ ) {
19
+ return false
20
+ }
21
+ // A label is one literal name, not a selector expression. Whitespace and CSS
22
+ // punctuation can parse as a different selection even when core accepts it.
23
+ return /^\/?[A-Za-z0-9_]+$/.test(name)
24
+ }
25
+
9
26
  export const convertNetLabels: SchematicElementConverter = (circuitJson) =>
10
27
  su(circuitJson)
11
28
  .schematic_net_label.list()
@@ -26,6 +43,23 @@ export const convertNetLabels: SchematicElementConverter = (circuitJson) =>
26
43
  displayText: schematicNetLabel.text,
27
44
  })
28
45
  }
46
+ // Native labels own their outline, typography and anchor-side layout.
47
+ // Core resolves net names through selectors; retain the text fallback
48
+ // for names that cannot safely be used by that runtime path.
49
+ if (
50
+ !symbolName &&
51
+ schematicNetLabel.anchor_position &&
52
+ canRenderNativeNetLabel(schematicNetLabel.text)
53
+ ) {
54
+ return [
55
+ formatElement("netlabel", {
56
+ net: schematicNetLabel.text,
57
+ schX: anchorPosition.x,
58
+ schY: anchorPosition.y,
59
+ anchorSide: schematicNetLabel.anchor_side,
60
+ }),
61
+ ]
62
+ }
29
63
  return [
30
64
  formatElement("schematictext", {
31
65
  text: schematicNetLabel.text,
@@ -1,15 +1,11 @@
1
- import type { SchematicPort, SourcePort } from "circuit-json"
1
+ import type { SchematicComponent, SchematicPort } from "circuit-json"
2
2
 
3
3
  export const getBoxPinDisplayLabel = ({
4
+ schematicComponent,
4
5
  schematicPort,
5
- sourcePort,
6
6
  }: {
7
+ schematicComponent: SchematicComponent
7
8
  schematicPort: SchematicPort
8
- sourcePort: SourcePort | null
9
- }): string | undefined => {
10
- if (schematicPort.display_pin_label !== undefined)
11
- return schematicPort.display_pin_label
12
-
13
- const sourceName = sourcePort?.name
14
- return sourceName && !/^\d+$/.test(sourceName) ? sourceName : undefined
15
- }
9
+ }): string | undefined =>
10
+ schematicPort.display_pin_label ??
11
+ schematicComponent.port_labels?.[String(schematicPort.pin_number)]
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "./cli": "./dist/cli/main.js"
7
7
  },
8
8
  "type": "module",
9
- "version": "0.0.74",
9
+ "version": "0.0.76",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "https://github.com/tscircuit/circuit-json-to-tscircuit.git"
@@ -0,0 +1,86 @@
1
+ import { expect, test } from "bun:test"
2
+ import { convertNetLabels } from "../lib/generate-board-schematic-tsx/convert-net-labels"
3
+ import { runTscircuitCode } from "tscircuit"
4
+
5
+ function convertPlainNetLabel(text: string) {
6
+ return convertNetLabels([
7
+ {
8
+ type: "schematic_net_label",
9
+ schematic_net_label_id: "label",
10
+ source_net_id: "net",
11
+ text,
12
+ center: { x: 2, y: 3 },
13
+ anchor_position: { x: 2, y: 3 },
14
+ anchor_side: "left",
15
+ },
16
+ ]).join("\n")
17
+ }
18
+
19
+ for (const name of ["VCC-P", "VCC+P", "3V3", "1", "-VCC", "net.name"]) {
20
+ test(`conversion preserves text when core rejects ${name}`, async () => {
21
+ await expect(
22
+ runTscircuitCode(
23
+ `export default () => <board><netlabel net={${JSON.stringify(name)}} /></board>`,
24
+ ),
25
+ ).rejects.toThrow(/Net name/)
26
+ const generated = convertPlainNetLabel(name)
27
+ expect(generated).not.toContain("<netlabel")
28
+ const output = await runTscircuitCode(
29
+ `export default () => <board><symbol>${generated}</symbol></board>`,
30
+ )
31
+ expect(output).toEqual(
32
+ expect.arrayContaining([
33
+ expect.objectContaining({ type: "schematic_text", text: name }),
34
+ ]),
35
+ )
36
+ })
37
+ }
38
+
39
+ for (const name of ["SDA", "V3_3", "_RESET", "/RESET", "/3V3", "A1"]) {
40
+ test(`conversion preserves native labels accepted by core: ${name}`, async () => {
41
+ const source = await runTscircuitCode(
42
+ `export default () => <board><netlabel net={${JSON.stringify(name)}} schX={2} schY={3} anchorSide="left" /></board>`,
43
+ )
44
+ const generated = convertNetLabels(source).join("\n")
45
+ expect(generated).toContain("<netlabel")
46
+ const output = await runTscircuitCode(
47
+ `export default () => <board><symbol>${generated}</symbol></board>`,
48
+ )
49
+ expect(output).toEqual(
50
+ expect.arrayContaining([
51
+ expect.objectContaining({
52
+ type: "schematic_net_label",
53
+ text: name,
54
+ anchor_position: { x: 2, y: 3 },
55
+ anchor_side: "left",
56
+ }),
57
+ ]),
58
+ )
59
+ })
60
+ }
61
+
62
+ for (const name of [
63
+ "BUS/DATA",
64
+ "//RESET",
65
+ "HV VSYS",
66
+ "A>B",
67
+ "A,B",
68
+ "A[1]",
69
+ "A:B",
70
+ 'A"B',
71
+ "",
72
+ "電源",
73
+ ]) {
74
+ test(`conversion preserves selector-unsafe label text: ${name}`, async () => {
75
+ const generated = convertPlainNetLabel(name)
76
+ expect(generated).not.toContain("<netlabel")
77
+ const output = await runTscircuitCode(
78
+ `export default () => <board><symbol>${generated}</symbol></board>`,
79
+ )
80
+ expect(output).toEqual(
81
+ expect.arrayContaining([
82
+ expect.objectContaining({ type: "schematic_text", text: name }),
83
+ ]),
84
+ )
85
+ })
86
+ }
@@ -0,0 +1,82 @@
1
+ import { expect, test } from "bun:test"
2
+ import type { CircuitJson } from "circuit-json"
3
+ import { convertCircuitJsonToSchematicSvg } from "circuit-to-svg"
4
+ import { convertCircuitJsonToTscircuit } from "lib"
5
+ import { runTscircuitCode } from "tscircuit"
6
+
7
+ test("native plain labels preserve rendering on every connection side", async () => {
8
+ for (const side of ["left", "right", "top", "bottom"] as const) {
9
+ const input = await runTscircuitCode(`export default () => (
10
+ <board width={10} height={10}>
11
+ <netlabel net="DATA" schX={2} schY={3} anchorSide="${side}" />
12
+ </board>
13
+ )`)
14
+ const output = await runTscircuitCode(
15
+ convertCircuitJsonToTscircuit(input, { componentName: "LabelBoard" }),
16
+ )
17
+ const sourceLabels = input.filter((e) => e.type === "schematic_net_label")
18
+ const renderedLabels = output.filter(
19
+ (e) => e.type === "schematic_net_label",
20
+ )
21
+ expect(renderedLabels).toHaveLength(1)
22
+ const original = sourceLabels[0]!
23
+ expect(renderedLabels[0]).toMatchObject({
24
+ text: original.text,
25
+ anchor_side: original.anchor_side,
26
+ anchor_position: original.anchor_position,
27
+ center: original.center,
28
+ })
29
+ expect(
30
+ convertCircuitJsonToSchematicSvg(renderedLabels, {
31
+ includeVersion: false,
32
+ }),
33
+ ).toBe(
34
+ convertCircuitJsonToSchematicSvg(sourceLabels, { includeVersion: false }),
35
+ )
36
+ }
37
+ for (const name of [
38
+ "HV VSYS",
39
+ "BUS/DATA",
40
+ "net.name",
41
+ "",
42
+ 'A"B',
43
+ "CENTER_ONLY",
44
+ "VCC-P",
45
+ "VCC+P",
46
+ "3V3",
47
+ "1",
48
+ "-VCC",
49
+ ]) {
50
+ const input: CircuitJson = [
51
+ {
52
+ type: "pcb_board",
53
+ pcb_board_id: "board",
54
+ center: { x: 0, y: 0 },
55
+ width: 10,
56
+ height: 10,
57
+ num_layers: 2,
58
+ thickness: 1.6,
59
+ material: "fr4",
60
+ },
61
+ {
62
+ type: "schematic_net_label",
63
+ schematic_net_label_id: "label",
64
+ text: name,
65
+ center: { x: 2, y: 3 },
66
+ anchor_position: name === "CENTER_ONLY" ? undefined : { x: 2, y: 3 },
67
+ anchor_side: "left",
68
+ source_net_id: "net",
69
+ },
70
+ ]
71
+ const tsx = convertCircuitJsonToTscircuit(input, {
72
+ componentName: "Fallback",
73
+ })
74
+ expect(tsx).not.toContain("<netlabel")
75
+ const fallback = await runTscircuitCode(tsx)
76
+ expect(fallback).toEqual(
77
+ expect.arrayContaining([
78
+ expect.objectContaining({ type: "schematic_text", text: name }),
79
+ ]),
80
+ )
81
+ }
82
+ })
@@ -38,6 +38,7 @@ test("uses the compiled schematic color for box pin text", async () => {
38
38
  side_of_component: "left",
39
39
  facing_direction: "left",
40
40
  pin_number: 1,
41
+ display_pin_label: "FB",
41
42
  },
42
43
  ]
43
44
  const renderedCircuitJson = await runTscircuitCode(
@@ -0,0 +1,73 @@
1
+ import { expect, test } from "bun:test"
2
+ import type { CircuitJson } from "circuit-json"
3
+ import { convertCircuitJsonToTscircuit } from "lib"
4
+ import { runTscircuitCode } from "tscircuit"
5
+
6
+ test("preserves component-level pin labels and explicit suppression", async () => {
7
+ const circuitJson: CircuitJson = [
8
+ {
9
+ type: "pcb_board",
10
+ pcb_board_id: "board",
11
+ center: { x: 0, y: 0 },
12
+ width: 10,
13
+ height: 10,
14
+ num_layers: 2,
15
+ thickness: 1.6,
16
+ material: "fr4",
17
+ },
18
+ {
19
+ type: "schematic_component",
20
+ schematic_component_id: "controller",
21
+ center: { x: 0, y: 0 },
22
+ size: { width: 2, height: 1 },
23
+ is_box_with_pins: true,
24
+ port_labels: { "1": "ENABLE", "2": "DISABLED" },
25
+ },
26
+ {
27
+ type: "source_port",
28
+ source_port_id: "enable_source",
29
+ name: "ENABLE",
30
+ pin_number: 1,
31
+ },
32
+ {
33
+ type: "source_port",
34
+ source_port_id: "disabled_source",
35
+ name: "DISABLED",
36
+ pin_number: 2,
37
+ },
38
+ {
39
+ type: "schematic_port",
40
+ schematic_port_id: "enable_pin",
41
+ source_port_id: "enable_source",
42
+ schematic_component_id: "controller",
43
+ center: { x: -1.4, y: 0.2 },
44
+ distance_from_component_edge: 0.4,
45
+ side_of_component: "left",
46
+ facing_direction: "left",
47
+ pin_number: 1,
48
+ },
49
+ {
50
+ type: "schematic_port",
51
+ schematic_port_id: "disabled_pin",
52
+ source_port_id: "disabled_source",
53
+ schematic_component_id: "controller",
54
+ center: { x: -1.4, y: -0.2 },
55
+ distance_from_component_edge: 0.4,
56
+ side_of_component: "left",
57
+ facing_direction: "left",
58
+ pin_number: 2,
59
+ display_pin_label: "",
60
+ },
61
+ ]
62
+ const renderedCircuitJson = await runTscircuitCode(
63
+ convertCircuitJsonToTscircuit(circuitJson, {
64
+ componentName: "ComponentPinLabelsBoard",
65
+ }),
66
+ )
67
+ const renderedTexts = renderedCircuitJson
68
+ .filter((element) => element.type === "schematic_text")
69
+ .map((element) => element.text)
70
+
71
+ expect(renderedTexts).toContain("ENABLE")
72
+ expect(renderedTexts).not.toContain("DISABLED")
73
+ })
@@ -0,0 +1,37 @@
1
+ import { expect, test } from "bun:test"
2
+ import { readFile } from "node:fs/promises"
3
+ import { join } from "node:path"
4
+ import { gunzipSync } from "node:zlib"
5
+ import type { CircuitJson } from "circuit-json"
6
+ import { convertCircuitJsonToTscircuit } from "lib"
7
+
8
+ test("does not display hidden source port names on the LM5155 U3 symbol", async () => {
9
+ const fixturePath = join(
10
+ import.meta.dir,
11
+ "fixtures/ti-evms/lm5155evm-fly.circuit.json.gz",
12
+ )
13
+ const circuitJson = JSON.parse(
14
+ gunzipSync(await readFile(fixturePath)).toString("utf8"),
15
+ ) as CircuitJson
16
+ const refSourcePort = circuitJson.find(
17
+ (element) => element.type === "source_port" && element.name === "REF",
18
+ )
19
+ if (refSourcePort?.type !== "source_port") {
20
+ throw new Error("LM5155 fixture is missing the U3 REF source port")
21
+ }
22
+ const refSchematicPort = circuitJson.find(
23
+ (element) =>
24
+ element.type === "schematic_port" &&
25
+ element.source_port_id === refSourcePort.source_port_id,
26
+ )
27
+ if (refSchematicPort?.type !== "schematic_port") {
28
+ throw new Error("LM5155 fixture is missing the U3 REF schematic port")
29
+ }
30
+ expect(refSchematicPort.display_pin_label).toBeUndefined()
31
+
32
+ const generatedTscircuit = convertCircuitJsonToTscircuit(circuitJson, {
33
+ componentName: "Lm5155EvmFly",
34
+ })
35
+
36
+ expect(generatedTscircuit).not.toContain('<schematictext text={"REF"}')
37
+ })