@tscircuit/schematic-viewer 1.3.0 → 1.4.1

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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/biome.json +45 -0
  3. package/dist/index.d.ts +3 -4
  4. package/dist/index.js +734 -688
  5. package/dist/index.js.map +1 -1
  6. package/package.json +8 -7
  7. package/src/Schematic.tsx +141 -65
  8. package/src/lib/render-context/index.ts +1 -1
  9. package/src/lib/types/core.ts +14 -49
  10. package/src/lib/types/source-component.ts +6 -1
  11. package/src/lib/utils/collect-element-refs.ts +5 -4
  12. package/src/lib/utils/colors.ts +235 -0
  13. package/src/lib/utils/direction-to-vec.ts +3 -3
  14. package/src/schematic-components/SVGPathComponent.tsx +71 -112
  15. package/src/schematic-components/SchematicChip.tsx +222 -0
  16. package/src/schematic-components/SchematicComponent.tsx +25 -22
  17. package/src/schematic-components/SchematicComponentFromSymbol.tsx +46 -0
  18. package/src/schematic-components/SchematicElement.tsx +0 -28
  19. package/src/schematic-components/SchematicNetLabel.tsx +3 -0
  20. package/src/schematic-components/SchematicText.tsx +4 -6
  21. package/src/schematic-components/SchematicTrace.tsx +4 -3
  22. package/src/schematic-components/TableViewer.tsx +1 -1
  23. package/src/schematic-components/index.tsx +6 -14
  24. package/src/stories/basics/schematic-net-label.stories.tsx +2 -0
  25. package/src/stories/basics/schematic-net-labels-2.stories.tsx +22 -20
  26. package/src/stories/bug-connections.stories.tsx +18 -13
  27. package/src/stories/bug-high-port-numbers.stories.tsx +107 -85
  28. package/src/stories/bug-one-sided.stories.tsx +17 -15
  29. package/src/stories/bug-pin-spacing.stories.tsx +19 -17
  30. package/src/stories/bugs/bug1-y-flip.stories.tsx +7 -5
  31. package/src/stories/bugs/bug3-scaling-trace.stories.tsx +11 -5
  32. package/src/stories/bugs/bug4-schematic-line.stories.tsx +0 -1
  33. package/src/stories/bugs/bug5-diode.stories.tsx +3 -2
  34. package/src/stories/bugs/bug6-trace-scaling.stories.tsx +5 -41
  35. package/src/stories/bugs/bug8-autolayout.stories.tsx +20 -29
  36. package/src/stories/circuit-components/diode.stories.tsx +3 -1
  37. package/src/stories/circuit-components/resistor.stories.tsx +3 -1
  38. package/src/stories/led-circuit-react.stories.tsx +35 -48
  39. package/src/stories/rotated-resistor.stories.tsx +10 -8
  40. package/src/stories/three-sided-bug.stories.tsx +17 -15
  41. package/src/pages/led-circuit.tsx +0 -96
  42. package/src/schematic-components/ProjectComponent.tsx +0 -70
  43. package/src/schematic-components/SchematicBox.tsx +0 -29
  44. package/src/schematic-components/SchematicBug.tsx +0 -107
  45. package/src/schematic-components/SchematicLine.tsx +0 -48
  46. package/src/schematic-components/SchematicPath.tsx +0 -51
  47. package/src/schematic-components/SchematicPort.tsx +0 -63
  48. package/src/schematic-components/SimpleCapacitor.tsx +0 -29
  49. package/src/schematic-components/SimpleDiode.tsx +0 -42
  50. package/src/schematic-components/SimpleGround.tsx +0 -30
  51. package/src/schematic-components/SimpleInductor.tsx +0 -29
  52. package/src/schematic-components/SimplePowerSource.tsx +0 -43
  53. package/src/schematic-components/SimpleResistor.tsx +0 -28
  54. package/src/stories/led-circuit-builder.stories.tsx +0 -104
@@ -0,0 +1,222 @@
1
+ import {
2
+ AnyCircuitElement,
3
+ SchematicPort as OriginalSchematicPort,
4
+ SchematicComponent,
5
+ } from "circuit-json"
6
+ import * as Type from "lib/types"
7
+ import { colorMap } from "lib/utils/colors"
8
+ import React from "react"
9
+ import SVGPathComponent from "./SVGPathComponent"
10
+ import SchematicText from "./SchematicText"
11
+
12
+ interface Props {
13
+ component: {
14
+ source: Type.SimpleBug
15
+ schematic: SchematicComponent
16
+ allElements: AnyCircuitElement[]
17
+ }
18
+ }
19
+
20
+ type ExtendedCenter = OriginalSchematicPort["center"] & {
21
+ side: "left" | "right" | "top" | "bottom" | "center"
22
+ pinNumber: number
23
+ distanceFromEdge: number
24
+ trueIndex: number
25
+ }
26
+
27
+ type SchematicPort = Omit<OriginalSchematicPort, "center"> & {
28
+ center: ExtendedCenter
29
+ }
30
+
31
+ export const SchematicChip: React.FC<Props> = ({
32
+ component: { source, schematic, allElements },
33
+ }) => {
34
+ const { center, size, rotation, schematic_component_id } = schematic
35
+ const { manufacturerPartNumber, name } = source
36
+ const chipWidth = size.width
37
+ const chipHeight = size.height
38
+
39
+ const paths: Array<{
40
+ type?: "path" | "circle"
41
+ strokeWidth: number
42
+ stroke: string
43
+ fill?: string
44
+ d?: string
45
+ cx?: number
46
+ cy?: number
47
+ r?: number
48
+ }> = []
49
+
50
+ // Main chip rectangle
51
+ paths.push({
52
+ type: "path",
53
+ strokeWidth: 0.02,
54
+ stroke: colorMap.schematic.component_outline,
55
+ fill: colorMap.schematic.component_body,
56
+ d: `M ${-chipWidth / 2},${-chipHeight / 2} h ${chipWidth} v ${chipHeight} h ${-chipWidth} Z`,
57
+ })
58
+
59
+ const schematicPorts = allElements.filter(
60
+ (item): item is SchematicPort =>
61
+ item.type === "schematic_port" &&
62
+ item.schematic_component_id === schematic_component_id,
63
+ )
64
+
65
+ const portLength = 0.2
66
+ const circleRadius = 0.05
67
+ const labelOffset = 0.1
68
+
69
+ const pinLabels: Array<{
70
+ x: number
71
+ y: number
72
+ text: string
73
+ anchor: string
74
+ rotation: number
75
+ }> = []
76
+
77
+ schematicPorts.forEach((port) => {
78
+ const { side, pinNumber, distanceFromEdge } = port.center
79
+ let x = 0,
80
+ y = 0,
81
+ endX = 0,
82
+ endY = 0
83
+ let labelX = 0,
84
+ labelY = 0
85
+ let textAnchor = "middle"
86
+ let rotation = 0
87
+
88
+ if (side === "center") {
89
+ return
90
+ }
91
+
92
+ switch (side) {
93
+ case "left":
94
+ x = -chipWidth / 2
95
+ y = -chipHeight / 2 + distanceFromEdge
96
+ endX = x - portLength
97
+ endY = y
98
+ labelX = endX
99
+ labelY = y + labelOffset
100
+ textAnchor = "end"
101
+ break
102
+ case "right":
103
+ x = chipWidth / 2
104
+ y = chipHeight / 2 - distanceFromEdge
105
+ endX = x + portLength
106
+ endY = y
107
+ labelX = endX - labelOffset
108
+ labelY = y + labelOffset
109
+ textAnchor = "start"
110
+ break
111
+ case "bottom":
112
+ x = -chipWidth / 2 + distanceFromEdge
113
+ y = -chipHeight / 2
114
+ endX = x
115
+ endY = y - portLength
116
+ labelX = x
117
+ labelY = endY + labelOffset
118
+ rotation = -90
119
+ break
120
+ case "top":
121
+ x = chipWidth / 2 - distanceFromEdge
122
+ y = chipHeight / 2
123
+ endX = x
124
+ endY = y + portLength
125
+ labelX = x
126
+ labelY = endY + labelOffset
127
+ rotation = -90
128
+ break
129
+ }
130
+
131
+ // Port line
132
+ paths.push({
133
+ type: "path",
134
+ strokeWidth: 0.015,
135
+ stroke: colorMap.schematic.component_outline,
136
+ d: `M ${x},${y} L ${endX},${endY}`,
137
+ })
138
+
139
+ // Port circle at the end of the line
140
+ paths.push({
141
+ type: "circle",
142
+ cx: endX,
143
+ cy: endY,
144
+ r: circleRadius,
145
+ strokeWidth: 0.01,
146
+ stroke: colorMap.schematic.component_outline,
147
+ fill: colorMap.schematic.component_outline,
148
+ })
149
+
150
+ // Add pin label
151
+ if (pinNumber !== undefined) {
152
+ pinLabels.push({
153
+ x: labelX,
154
+ y: labelY,
155
+ text: `${pinNumber}`,
156
+ anchor: textAnchor,
157
+ rotation: rotation,
158
+ })
159
+ }
160
+ })
161
+
162
+ return (
163
+ <>
164
+ <SVGPathComponent
165
+ rotation={rotation}
166
+ center={center}
167
+ size={size}
168
+ paths={paths as any}
169
+ />
170
+ {pinLabels.map((label, index) => (
171
+ <SchematicText
172
+ key={index}
173
+ schematic_text={{
174
+ anchor: label.anchor as any,
175
+ rotation: 0,
176
+ position: {
177
+ x: center.x + label.x,
178
+ y: center.y + label.y,
179
+ },
180
+ schematic_component_id: "SYNTHETIC",
181
+ schematic_text_id: `PIN_LABEL_${index}`,
182
+ text: label.text,
183
+ type: "schematic_text",
184
+ color: colorMap.schematic.pin_number,
185
+ }}
186
+ />
187
+ ))}
188
+ <SchematicText
189
+ schematic_text={{
190
+ anchor: "right",
191
+ rotation: 0,
192
+ position: {
193
+ x: center.x,
194
+ y: center.y - chipHeight / 2 - 0.2,
195
+ },
196
+ schematic_component_id: "SYNTHETIC",
197
+ schematic_text_id: "SYNTHETIC_MPN",
198
+ text: manufacturerPartNumber,
199
+ type: "schematic_text",
200
+ color: colorMap.schematic.reference,
201
+ }}
202
+ />
203
+ <SchematicText
204
+ schematic_text={{
205
+ anchor: "right",
206
+ rotation: 0,
207
+ position: {
208
+ x: center.x,
209
+ y: center.y + chipHeight / 2 + 0.2,
210
+ },
211
+ schematic_component_id: "SYNTHETIC",
212
+ schematic_text_id: "SYNTHETIC_NAME",
213
+ text: name,
214
+ type: "schematic_text",
215
+ color: colorMap.schematic.reference,
216
+ }}
217
+ />
218
+ </>
219
+ )
220
+ }
221
+
222
+ export default SchematicChip
@@ -1,11 +1,15 @@
1
- import * as Type from "lib/types"
1
+ import {
2
+ AnyCircuitElement,
3
+ SchematicComponent as SchematicComponentType,
4
+ } from "circuit-json"
2
5
  import * as Component from "./"
3
6
 
4
7
  interface Props {
5
8
  component: {
6
- source: Type.SourceComponent
7
- schematic: Type.SchematicComponent
9
+ source: any
10
+ schematic: SchematicComponentType
8
11
  schematic_children: any[]
12
+ allElements: AnyCircuitElement[]
9
13
  }
10
14
  }
11
15
 
@@ -14,30 +18,29 @@ interface Props {
14
18
  * generating schematic lines directly
15
19
  */
16
20
  export const SchematicComponent = ({ component }: Props) => {
17
- const { source, schematic } = component
21
+ const { source, schematic, allElements } = component
18
22
  if (!source.ftype) return null
19
23
 
20
24
  switch (source.ftype) {
21
- case "simple_resistor": {
22
- return <Component.SimpleResistor component={{ source, schematic }} />
23
- }
24
- case "simple_capacitor": {
25
- return <Component.SimpleCapacitor component={{ source, schematic }} />
26
- }
27
- case "simple_power_source": {
28
- return <Component.SimplePowerSource component={{ source, schematic }} />
29
- }
30
- case "simple_ground": {
31
- return <Component.SimpleGround component={{ source, schematic }} />
32
- }
33
- case "simple_inductor": {
34
- return <Component.SimpleInductor component={{ source, schematic }} />
25
+ case "simple_resistor":
26
+ case "simple_capacitor":
27
+ case "simple_power_source":
28
+ case "simple_ground":
29
+ case "simple_inductor":
30
+ case "simple_diode": {
31
+ return (
32
+ <Component.SchematicComponentFromSymbol
33
+ component={{ source, schematic }}
34
+ />
35
+ )
35
36
  }
37
+ case "simple_chip":
36
38
  case "simple_bug": {
37
- return <Component.SchematicBug component={{ source, schematic }} />
38
- }
39
- case "simple_diode": {
40
- return <Component.SimpleDiode component={{ source, schematic }} />
39
+ return (
40
+ <Component.SchematicChip
41
+ component={{ source, schematic, allElements }}
42
+ />
43
+ )
41
44
  }
42
45
  default: {
43
46
  return <div>unknown ftype: {component.source.ftype}</div>
@@ -0,0 +1,46 @@
1
+ import { SchematicComponent, SourceSimpleResistor } from "circuit-json"
2
+ import { colorMap } from "lib/utils/colors"
3
+ import { symbols } from "schematic-symbols"
4
+ import SVGPathComponent from "./SVGPathComponent"
5
+
6
+ interface Props {
7
+ component: {
8
+ source: SourceSimpleResistor
9
+ schematic: SchematicComponent
10
+ }
11
+ }
12
+
13
+ export const SchematicComponentFromSymbol = ({
14
+ component: { source, schematic },
15
+ }: Props) => {
16
+ const { center, rotation } = schematic
17
+ // Get the resistor symbol paths
18
+ const symbol = symbols[schematic.symbol_name]
19
+ const paths = symbol.primitives
20
+ .filter((p: any) => p.type === "path")
21
+ .map((p: any) => ({
22
+ stroke: colorMap.schematic.component_outline,
23
+ strokeWidth: 0.02,
24
+ d: p.points.reduce(
25
+ (acc: string, point: { x: number; y: number }, index: number) => {
26
+ const command = index === 0 ? "M" : "L"
27
+ return `${acc} ${command} ${point.x} ${point.y}`
28
+ },
29
+ "",
30
+ ),
31
+ }))
32
+
33
+ return (
34
+ <SVGPathComponent
35
+ rotation={rotation}
36
+ center={center}
37
+ size={{
38
+ width: symbol?.size.width,
39
+ height: symbol?.size.height,
40
+ }}
41
+ paths={paths}
42
+ />
43
+ )
44
+ }
45
+
46
+ export default SchematicComponentFromSymbol
@@ -1,11 +1,7 @@
1
1
  import { AnyCircuitElement } from "circuit-json"
2
2
  import { collectElementRefs } from "lib/utils/collect-element-refs"
3
- import SchematicBox from "./SchematicBox"
4
3
  import SchematicComponent from "./SchematicComponent"
5
- import SchematicLine from "./SchematicLine"
6
4
  import { SchematicNetLabel } from "./SchematicNetLabel"
7
- import SchematicPath from "./SchematicPath"
8
- import SchematicPort from "./SchematicPort"
9
5
  import SchematicText from "./SchematicText"
10
6
  import SchematicTrace from "./SchematicTrace"
11
7
 
@@ -35,30 +31,6 @@ export const SchematicElement = ({
35
31
  )
36
32
  }
37
33
 
38
- if (element.type === "schematic_port") {
39
- return (
40
- <SchematicPort port={collectElementRefs(element, allElements) as any} />
41
- )
42
- }
43
-
44
- if (element.type === "schematic_box") {
45
- return (
46
- <SchematicBox box={collectElementRefs(element, allElements) as any} />
47
- )
48
- }
49
-
50
- if (element.type === "schematic_line") {
51
- return (
52
- <SchematicLine line={collectElementRefs(element, allElements) as any} />
53
- )
54
- }
55
-
56
- if (element.type === "schematic_path") {
57
- return (
58
- <SchematicPath path={collectElementRefs(element, allElements) as any} />
59
- )
60
- }
61
-
62
34
  if (element.type === "schematic_text") {
63
35
  return <SchematicText schematic_text={element} />
64
36
  }
@@ -3,6 +3,7 @@ import SVGPathComponent from "./SVGPathComponent"
3
3
  import SchematicText from "./SchematicText"
4
4
  import { getRotationFromAnchorSide } from "lib/utils/get-rotation-from-anchor-side"
5
5
  import { getVecFromAnchorSide } from "lib/utils/get-vec-from-anchor-side"
6
+ import { colorMap } from "lib/utils/colors"
6
7
 
7
8
  export const SchematicNetLabel = ({
8
9
  net_label,
@@ -44,6 +45,7 @@ export const SchematicNetLabel = ({
44
45
  />
45
46
  <SchematicText
46
47
  schematic_text={{
48
+ rotation: is_vertical ? 0 : getRotationFromAnchorSide(anchor_side),
47
49
  anchor: is_vertical ? "center" : anchor_side,
48
50
  position: {
49
51
  x: net_label.center.x + anchor_vec.x,
@@ -53,6 +55,7 @@ export const SchematicNetLabel = ({
53
55
  schematic_text_id: "SYNTHETIC",
54
56
  text: net_label.text,
55
57
  type: "schematic_text",
58
+ color: colorMap.schematic.net_name,
56
59
  }}
57
60
  />
58
61
  </>
@@ -1,13 +1,10 @@
1
- import * as Type from "lib/types"
2
- import SVGPathComponent from "./SVGPathComponent"
3
- import Path from "svg-path-generator"
4
- import getSVGPathBounds from "lib/utils/get-svg-path-bounds"
1
+ import { SchematicText as SchematicTextType } from "circuit-json"
5
2
  import { useGlobalStore } from "lib/render-context"
6
- import { applyToPoint } from "transformation-matrix"
7
3
  import useMeasure from "react-use-measure"
4
+ import { applyToPoint } from "transformation-matrix"
8
5
 
9
6
  interface Props {
10
- schematic_text: Type.SchematicText
7
+ schematic_text: SchematicTextType
11
8
  }
12
9
 
13
10
  export const SchematicText = ({ schematic_text }: Props) => {
@@ -36,6 +33,7 @@ export const SchematicText = ({ schematic_text }: Props) => {
36
33
  fontSize: projectedTextSize,
37
34
  left: tPos.x + offset[0],
38
35
  top: tPos.y + offset[1],
36
+ color: schematic_text.color,
39
37
  }}
40
38
  >
41
39
  {text}
@@ -4,6 +4,7 @@ import Path from "svg-path-generator"
4
4
  import getSVGPathBounds from "lib/utils/get-svg-path-bounds"
5
5
  import RenderError from "./RenderError"
6
6
  import SVGPathComponent2 from "./SVGPathComponent2"
7
+ import { colorMap } from "lib/utils/colors"
7
8
 
8
9
  interface Props {
9
10
  trace: {
@@ -33,14 +34,14 @@ export const SchematicTrace = ({ trace: { source, schematic } }: Props) => {
33
34
  y: pathBounds.minY + pathBounds.height / 2,
34
35
  }
35
36
  return (
36
- <SVGPathComponent2
37
+ <SVGPathComponent
37
38
  rotation={0}
38
39
  center={center}
39
40
  size={pathBounds}
40
41
  paths={[
41
42
  {
42
- stroke: "green",
43
- strokeWidth: 0.02,
43
+ stroke: colorMap.schematic.wire,
44
+ strokeWidth: 0.01,
44
45
  d,
45
46
  },
46
47
  ]}
@@ -3,7 +3,7 @@ import { lazy, Suspense } from "react"
3
3
  const LazyTableViewer = lazy(() =>
4
4
  import("@tscircuit/table-viewer").then((m) => ({
5
5
  default: m.SoupTableViewer,
6
- }))
6
+ })),
7
7
  )
8
8
 
9
9
  export const TableViewer = (params: Parameters<typeof LazyTableViewer>[0]) => (
@@ -1,17 +1,9 @@
1
- export * from "./SchematicGroup"
2
- export * from "./SimpleResistor"
3
- export * from "./SimpleCapacitor"
4
- export * from "./SVGPathComponent"
5
- export * from "./ProjectComponent"
1
+ export * from "./ContextProviders"
2
+ export * from "./RenderError"
3
+ export * from "./SchematicChip"
6
4
  export * from "./SchematicComponent"
7
- export * from "./SchematicPort"
5
+ export * from "./SchematicComponentFromSymbol"
6
+ export * from "./SchematicGroup"
8
7
  export * from "./SchematicText"
9
- export * from "./ProjectComponent"
10
8
  export * from "./SchematicTrace"
11
- export * from "./SchematicBug"
12
- export * from "./SimplePowerSource"
13
- export * from "./SimpleGround"
14
- export * from "./SimpleInductor"
15
- export * from "./RenderError"
16
- export * from "./SimpleDiode"
17
- export * from "./ContextProviders"
9
+ export * from "./SVGPathComponent"
@@ -72,6 +72,7 @@ export const SchematicNetLabel = () => {
72
72
  y: -0.5,
73
73
  },
74
74
  rotation: 0,
75
+ color: "black",
75
76
  },
76
77
  {
77
78
  type: "schematic_text",
@@ -84,6 +85,7 @@ export const SchematicNetLabel = () => {
84
85
  y: -0.3,
85
86
  },
86
87
  rotation: 0,
88
+ color: "black",
87
89
  },
88
90
  {
89
91
  type: "source_net",
@@ -3,26 +3,28 @@ import { Schematic } from "../../Schematic"
3
3
  export const SchematicNetLabel2 = () => {
4
4
  return (
5
5
  <Schematic style={{ height: 500 }}>
6
- <resistor resistance="1k" name="R1" schX={-2} schY={0} />
7
- <resistor resistance="1k" name="R2" schX={2} schY={0} />
8
- <resistor
9
- schRotation="90deg"
10
- resistance="1k"
11
- name="R3"
12
- schX={0}
13
- schY={2}
14
- />
15
- <resistor
16
- schRotation="90deg"
17
- resistance="1k"
18
- name="R4"
19
- schX={0}
20
- schY={-2}
21
- />
22
- <trace from=".R1 > .right" to="net.N1" />
23
- <trace from=".R2 > .left" to="net.N2" />
24
- <trace from=".R3 > .left" to="net.N3" />
25
- <trace from=".R4 > .right" to="net.GND2" />
6
+ <board width={10} height={10}>
7
+ <resistor resistance="1k" name="R1" schX={-2} schY={0} />
8
+ <resistor resistance="1k" name="R2" schX={2} schY={0} />
9
+ <resistor
10
+ schRotation="90deg"
11
+ resistance="1k"
12
+ name="R3"
13
+ schX={0}
14
+ schY={2}
15
+ />
16
+ <resistor
17
+ schRotation="90deg"
18
+ resistance="1k"
19
+ name="R4"
20
+ schX={0}
21
+ schY={-2}
22
+ />
23
+ <trace from=".R1 > .right" to="net.N1" />
24
+ <trace from=".R2 > .left" to="net.N2" />
25
+ <trace from=".R3 > .left" to="net.N3" />
26
+ <trace from=".R4 > .right" to="net.GND2" />
27
+ </board>
26
28
  </Schematic>
27
29
  )
28
30
  }
@@ -3,19 +3,24 @@ import { Schematic } from "Schematic"
3
3
  export const BugConnections = () => {
4
4
  return (
5
5
  <Schematic style={{ height: 600 }}>
6
- <bug
7
- name="B1"
8
- schPortArrangement={{
9
- leftSize: 3,
10
- rightSize: 2,
11
- }}
12
- schX={8}
13
- schY={3}
14
- pinLabels={{
15
- "1": "D0",
16
- "2": "D1",
17
- }}
18
- />
6
+ <board width={10} height={10}>
7
+ <bug
8
+ name="B1"
9
+ schPortArrangement={{
10
+ leftSize: 3,
11
+ rightSize: 2,
12
+ }}
13
+ schHeight={5}
14
+ schWidth={3}
15
+ schPinSpacing={2}
16
+ schX={8}
17
+ schY={3}
18
+ pinLabels={{
19
+ "1": "D0",
20
+ "2": "D1",
21
+ }}
22
+ />
23
+ </board>
19
24
  </Schematic>
20
25
  )
21
26
  }