@tscircuit/core 0.0.4 → 0.0.6

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.
@@ -12,3 +12,4 @@ export { Net } from "./primitive-components/Net"
12
12
  export { Trace } from "./primitive-components/Trace"
13
13
  export { TraceHint } from "./primitive-components/TraceHint"
14
14
  export { Group } from "./primitive-components/Group"
15
+ export { Chip } from "./normal-components/Chip"
@@ -1,4 +1,113 @@
1
1
  import { NormalComponent } from "lib/components/base-components/NormalComponent"
2
2
  import { chipProps } from "@tscircuit/props"
3
+ import { Port } from "../primitive-components/Port"
4
+ import type { BaseSymbolName } from "lib/utils/constants"
5
+ import {
6
+ getAllDimensionsForSchematicBox,
7
+ type SchematicBoxDimensions,
8
+ } from "lib/utils/schematic/getAllDimensionsForSchematicBox"
9
+ import { underscorifyPortArrangement } from "lib/soup/underscorifyPortArrangement"
10
+ import { underscorifyPinStyles } from "lib/soup/underscorifyPinStyles"
3
11
 
4
- export class Chip extends NormalComponent<typeof chipProps> {}
12
+ export class Chip<PinLabels extends string = never> extends NormalComponent<
13
+ typeof chipProps,
14
+ PinLabels
15
+ > {
16
+ schematicDimensions: SchematicBoxDimensions | null = null
17
+
18
+ get config() {
19
+ return {
20
+ zodProps: chipProps,
21
+ }
22
+ }
23
+
24
+ initPorts() {
25
+ super.initPorts()
26
+
27
+ const { _parsedProps: props } = this
28
+
29
+ if (props.pinLabels) {
30
+ for (const [pinNumber, label] of Object.entries(props.pinLabels)) {
31
+ const port = this.selectOne(`port[pinNumber='${pinNumber}']`)
32
+ if (!port) {
33
+ throw new Error(
34
+ `Could not find port for pin number ${pinNumber} in chip ${this.getString()}`,
35
+ )
36
+ }
37
+ port.props.aliases.push(port.props.name)
38
+ port.props.name = label
39
+ }
40
+ }
41
+ }
42
+
43
+ doInitialSourceRender(): void {
44
+ const { db } = this.project!
45
+ const { _parsedProps: props } = this
46
+
47
+ const source_component = db.source_component.insert({
48
+ ftype: "simple_chip",
49
+ name: props.name,
50
+ manufacturer_part_number: props.manufacturerPartNumber,
51
+ supplier_part_numbers: props.supplierPartNumbers,
52
+ })
53
+
54
+ this.source_component_id = source_component.source_component_id!
55
+ }
56
+
57
+ doInitialSchematicComponentRender() {
58
+ const { db } = this.project!
59
+ const { _parsedProps: props } = this
60
+
61
+ const pinSpacing = props.schPinSpacing ?? 0.2
62
+
63
+ const dimensions = getAllDimensionsForSchematicBox({
64
+ schWidth: props.schWidth,
65
+ schHeight: props.schHeight,
66
+ schPinSpacing: pinSpacing,
67
+ schPinStyle: props.schPinStyle,
68
+
69
+ // @ts-ignore there's a subtley in the definition difference with
70
+ // leftSide/rightSide/topSide/bottomSide in how the direction is defined
71
+ // that doesn't really matter
72
+ schPortArrangement: props.schPortArrangement,
73
+ })
74
+ this.schematicDimensions = dimensions
75
+
76
+ const schematic_component = db.schematic_component.insert({
77
+ center: { x: props.schX ?? 0, y: props.schY ?? 0 },
78
+ rotation: props.schRotation ?? 0,
79
+ size: dimensions.getSize(),
80
+
81
+ port_arrangement: underscorifyPortArrangement(
82
+ props.schPortArrangement as any,
83
+ ),
84
+
85
+ pin_spacing: pinSpacing,
86
+
87
+ // @ts-ignore soup needs to support distance for pin_styles
88
+ pin_styles: underscorifyPinStyles(props.schPinStyle),
89
+
90
+ port_labels: props.pinLabels,
91
+
92
+ source_component_id: this.source_component_id!,
93
+ })
94
+
95
+ this.schematic_component_id = schematic_component.schematic_component_id
96
+ }
97
+
98
+ doInitialPcbComponentRender() {
99
+ const { db } = this.project!
100
+ const { _parsedProps: props } = this
101
+
102
+ const pcb_component = db.pcb_component.insert({
103
+ center: { x: props.pcbX ?? 0, y: props.pcbY ?? 0 },
104
+ width: 2, // Default width, adjust as needed
105
+ height: 3, // Default height, adjust as needed
106
+ layer: props.layer ?? "top",
107
+ rotation: props.pcbRotation ?? 0,
108
+ source_component_id: this.source_component_id!,
109
+ })
110
+
111
+ this.pcb_component_id = pcb_component.pcb_component_id
112
+ }
113
+ }
@@ -45,6 +45,10 @@ export class Port extends PrimitiveComponent<typeof portProps> {
45
45
 
46
46
  getGlobalSchematicPosition(): { x: number; y: number } {
47
47
  if (!this.schematicSymbolPortDef) {
48
+ return applyToPoint(this.parent!.computeSchematicGlobalTransform(), {
49
+ x: 0,
50
+ y: 0,
51
+ })
48
52
  throw new Error(
49
53
  `Could not find schematic symbol port for port ${this} so couldn't determine port position`,
50
54
  )
@@ -0,0 +1,23 @@
1
+ import type { SchematicPinStyles } from "@tscircuit/props"
2
+ import { schematic_component } from "@tscircuit/soup"
3
+ import { z } from "zod"
4
+
5
+ type UnderscorePinStyles = z.input<typeof schematic_component>["pin_styles"]
6
+
7
+ export const underscorifyPinStyles = (
8
+ pinStyles: SchematicPinStyles | undefined,
9
+ ): UnderscorePinStyles | undefined => {
10
+ if (!pinStyles) return undefined
11
+ const underscorePinStyles: UnderscorePinStyles = {}
12
+
13
+ for (const [pinName, pinStyle] of Object.entries(pinStyles)) {
14
+ underscorePinStyles[pinName] = {
15
+ bottom_margin: pinStyle.bottomMargin,
16
+ left_margin: pinStyle.leftMargin,
17
+ right_margin: pinStyle.rightMargin,
18
+ top_margin: pinStyle.topMargin,
19
+ }
20
+ }
21
+
22
+ return underscorePinStyles
23
+ }
@@ -0,0 +1,51 @@
1
+ import type { SchematicComponentInput } from "@tscircuit/soup"
2
+ import type { PortArrangement } from "lib/utils/schematic/getAllDimensionsForSchematicBox"
3
+
4
+ export const underscorifyPortArrangement = (
5
+ portArrangement?: PortArrangement | undefined,
6
+ ): SchematicComponentInput["port_arrangement"] | undefined => {
7
+ if (!portArrangement) return undefined
8
+ if (
9
+ "leftSide" in portArrangement ||
10
+ "rightSide" in portArrangement ||
11
+ "topSide" in portArrangement ||
12
+ "bottomSide" in portArrangement
13
+ ) {
14
+ return {
15
+ left_side: portArrangement.leftSide,
16
+ right_side: portArrangement.rightSide,
17
+ top_side: portArrangement.topSide,
18
+ bottom_side: portArrangement.bottomSide,
19
+ }
20
+ }
21
+
22
+ if (
23
+ "leftPinCount" in portArrangement ||
24
+ "rightPinCount" in portArrangement ||
25
+ "topPinCount" in portArrangement ||
26
+ "bottomPinCount" in portArrangement
27
+ ) {
28
+ return {
29
+ left_size: portArrangement.leftPinCount!,
30
+ right_size: portArrangement.rightPinCount!,
31
+ top_size: portArrangement.topPinCount,
32
+ bottom_size: portArrangement.bottomPinCount,
33
+ }
34
+ }
35
+
36
+ if (
37
+ "leftSize" in portArrangement ||
38
+ "rightSize" in portArrangement ||
39
+ "topSize" in portArrangement ||
40
+ "bottomSize" in portArrangement
41
+ ) {
42
+ return {
43
+ left_size: portArrangement.leftSize!,
44
+ right_size: portArrangement.rightSize!,
45
+ top_size: portArrangement.topSize,
46
+ bottom_size: portArrangement.bottomSize,
47
+ }
48
+ }
49
+
50
+ return undefined
51
+ }
@@ -0,0 +1,8 @@
1
+ // same signature as lodash.range
2
+ export const range = (start: number, end: number, step = 1) => {
3
+ const result: number[] = []
4
+ for (let i = start; i < end; i += step) {
5
+ result.push(i)
6
+ }
7
+ return result
8
+ }
@@ -0,0 +1,330 @@
1
+ import { current } from "@tscircuit/soup"
2
+ import { getSizeOfSidesFromPortArrangement } from "./getSizeOfSidesFromPortArrangement"
3
+ import { schematicPortArrangement } from "@tscircuit/props"
4
+ import { z } from "zod"
5
+
6
+ export type VerticalPortSideConfiguration = {
7
+ direction?: "top-to-bottom" | "bottom-to-top"
8
+ pins: number[]
9
+ }
10
+ export type HorizontalPortSideConfiguration = {
11
+ direction?: "left-to-right" | "right-to-left"
12
+ pins: number[]
13
+ }
14
+
15
+ export type ExplicitPinMappingArrangement = {
16
+ leftSide?: VerticalPortSideConfiguration
17
+ rightSide?: VerticalPortSideConfiguration
18
+ topSide?: HorizontalPortSideConfiguration
19
+ bottomSide?: HorizontalPortSideConfiguration
20
+ }
21
+ /** @deprecated prefer SidePinCounts */
22
+ export interface SideSizes {
23
+ leftSize?: number
24
+ rightSize?: number
25
+ topSize?: number
26
+ bottomSize?: number
27
+ }
28
+
29
+ export interface SidePinCounts {
30
+ leftPinCount?: number
31
+ rightPinCount?: number
32
+ topPinCount?: number
33
+ bottomPinCount?: number
34
+ }
35
+
36
+ export type PortArrangement =
37
+ | SideSizes
38
+ | SidePinCounts
39
+ | ExplicitPinMappingArrangement
40
+
41
+ interface Params {
42
+ schWidth?: number
43
+ schHeight?: number
44
+ portDistanceFromEdge?: number
45
+ schPinSpacing: number
46
+ schPinStyle?: Record<
47
+ `pin${number}` | number | `${number}`,
48
+ {
49
+ leftMargin?: number
50
+ rightMargin?: number
51
+ topMargin?: number
52
+ bottomMargin?: number
53
+ }
54
+ >
55
+ pinCount?: number
56
+ schPortArrangement?: PortArrangement
57
+ }
58
+
59
+ type Side = "left" | "right" | "top" | "bottom"
60
+
61
+ export interface SchematicBoxDimensions {
62
+ pinCount: number
63
+ getPortPositionByPinNumber(pinNumber: number): { x: number; y: number }
64
+ getSize(): { width: number; height: number }
65
+ }
66
+
67
+ /**
68
+ * Get the dimensions of a schematic box based on the provided parameters.
69
+ *
70
+ * A schematic box is a rectangular box with a set of pins on the sides.
71
+ *
72
+ * The user can customize the position of the pins and the spacing between them,
73
+ * this makes computing the box fairly complicated.
74
+ *
75
+ * A note on the internals:
76
+ * * A "true port" refers to a pin/port before it's remapped by the user to a
77
+ * different position. True Pin 1 is guaranteed to be in the top-left corner
78
+ * * We basically iterate over each side and compute how far we are from the
79
+ * edge for that side by adding margins together
80
+ */
81
+ export const getAllDimensionsForSchematicBox = (
82
+ params: Params,
83
+ ): SchematicBoxDimensions => {
84
+ const portDistanceFromEdge =
85
+ params.portDistanceFromEdge ?? params.schPinSpacing * 2
86
+
87
+ let sidePinCounts = params.schPortArrangement
88
+ ? getSizeOfSidesFromPortArrangement(params.schPortArrangement)
89
+ : null
90
+
91
+ const sideLengths: Record<Side, number> = {
92
+ left: 0,
93
+ right: 0,
94
+ top: 0,
95
+ bottom: 0,
96
+ }
97
+
98
+ let pinCount: number | null = params.pinCount ?? null
99
+
100
+ if (pinCount === null) {
101
+ if (sidePinCounts) {
102
+ pinCount =
103
+ sidePinCounts.leftSize + sidePinCounts.rightSize + sidePinCounts.topSize
104
+ } else {
105
+ throw new Error("Could not determine pin count for the schematic box")
106
+ }
107
+ }
108
+
109
+ if (pinCount && !sidePinCounts) {
110
+ const rightSize = Math.floor(pinCount / 2)
111
+ sidePinCounts = {
112
+ leftSize: pinCount - rightSize,
113
+ rightSize: rightSize,
114
+ topSize: 0,
115
+ bottomSize: 0,
116
+ }
117
+ }
118
+
119
+ if (!sidePinCounts) {
120
+ throw new Error("Could not determine side sizes for the schematic box")
121
+ }
122
+
123
+ // Map the indices to the sides they correspond to
124
+ const orderedTruePorts: Array<{
125
+ trueIndex: number
126
+ pinNumber: number
127
+ side: "left" | "right" | "top" | "bottom"
128
+ distanceFromEdge: number
129
+ }> = []
130
+ let currentDistanceFromEdge = 0
131
+ let truePinIndex = 0
132
+ // moving downward from the top-left corner
133
+ for (let sideIndex = 0; sideIndex < sidePinCounts.leftSize; sideIndex++) {
134
+ const pinNumber = truePinIndex + 1 // TODO check mapping from schPortArrangement
135
+ const pinStyle =
136
+ params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber]
137
+
138
+ if (pinStyle?.topMargin) {
139
+ currentDistanceFromEdge += pinStyle.topMargin
140
+ }
141
+
142
+ orderedTruePorts.push({
143
+ trueIndex: truePinIndex,
144
+ pinNumber,
145
+ side: "left",
146
+ distanceFromEdge: currentDistanceFromEdge,
147
+ })
148
+
149
+ if (pinStyle?.bottomMargin) {
150
+ currentDistanceFromEdge += pinStyle.bottomMargin
151
+ }
152
+
153
+ const isLastPinOnSide = sideIndex === sidePinCounts.leftSize - 1
154
+ if (!isLastPinOnSide) {
155
+ currentDistanceFromEdge += params.schPinSpacing
156
+ } else {
157
+ sideLengths.left = currentDistanceFromEdge
158
+ }
159
+ truePinIndex++
160
+ }
161
+
162
+ currentDistanceFromEdge = 0
163
+ // moving rightward from the left-bottom corner
164
+ for (let sideIndex = 0; sideIndex < sidePinCounts.bottomSize; sideIndex++) {
165
+ const pinNumber = truePinIndex + 1 // TODO check mapping from schPortArrangement
166
+ const pinStyle =
167
+ params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber]
168
+
169
+ if (pinStyle?.leftMargin) {
170
+ currentDistanceFromEdge += pinStyle.leftMargin
171
+ }
172
+
173
+ orderedTruePorts.push({
174
+ trueIndex: truePinIndex,
175
+ pinNumber,
176
+ side: "bottom",
177
+ distanceFromEdge: currentDistanceFromEdge,
178
+ })
179
+
180
+ if (pinStyle?.rightMargin) {
181
+ currentDistanceFromEdge += pinStyle.rightMargin
182
+ }
183
+
184
+ const isLastPinOnSide = sideIndex === sidePinCounts.bottomSize - 1
185
+ if (!isLastPinOnSide) {
186
+ currentDistanceFromEdge += params.schPinSpacing
187
+ } else {
188
+ sideLengths.bottom = currentDistanceFromEdge
189
+ }
190
+ truePinIndex++
191
+ }
192
+
193
+ currentDistanceFromEdge = 0
194
+ // moving upward from the bottom-right corner
195
+ for (let sideIndex = 0; sideIndex < sidePinCounts.rightSize; sideIndex++) {
196
+ const pinNumber = truePinIndex + 1 // TODO check mapping from schPortArrangement
197
+ const pinStyle =
198
+ params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber]
199
+
200
+ if (pinStyle?.bottomMargin) {
201
+ currentDistanceFromEdge += pinStyle.bottomMargin
202
+ }
203
+
204
+ orderedTruePorts.push({
205
+ trueIndex: truePinIndex,
206
+ pinNumber,
207
+ side: "right",
208
+ distanceFromEdge: currentDistanceFromEdge,
209
+ })
210
+
211
+ if (pinStyle?.topMargin) {
212
+ currentDistanceFromEdge += pinStyle.topMargin
213
+ }
214
+
215
+ const isLastPinOnSide = sideIndex === sidePinCounts.rightSize - 1
216
+ if (!isLastPinOnSide) {
217
+ currentDistanceFromEdge += params.schPinSpacing
218
+ } else {
219
+ sideLengths.right = currentDistanceFromEdge
220
+ }
221
+ truePinIndex++
222
+ }
223
+
224
+ currentDistanceFromEdge = 0
225
+ // moving leftward from the top-right corner
226
+ for (let sideIndex = 0; sideIndex < sidePinCounts.topSize; sideIndex++) {
227
+ const pinNumber = truePinIndex + 1 // TODO check mapping from schPortArrangement
228
+ const pinStyle =
229
+ params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber]
230
+
231
+ if (pinStyle?.rightMargin) {
232
+ currentDistanceFromEdge += pinStyle.rightMargin
233
+ }
234
+
235
+ orderedTruePorts.push({
236
+ trueIndex: truePinIndex,
237
+ pinNumber,
238
+ side: "top",
239
+ distanceFromEdge: currentDistanceFromEdge,
240
+ })
241
+
242
+ if (pinStyle?.leftMargin) {
243
+ currentDistanceFromEdge += pinStyle.leftMargin
244
+ }
245
+
246
+ const isLastPinOnSide = sideIndex === sidePinCounts.topSize - 1
247
+ if (!isLastPinOnSide) {
248
+ currentDistanceFromEdge += params.schPinSpacing
249
+ } else {
250
+ sideLengths.top = currentDistanceFromEdge
251
+ }
252
+ truePinIndex++
253
+ }
254
+
255
+ // Use lengths to determine schWidth and schHeight
256
+ let schWidth = params.schWidth
257
+ if (!schWidth) {
258
+ schWidth = Math.max(
259
+ sideLengths.top + params.schPinSpacing * 2,
260
+ sideLengths.bottom + params.schPinSpacing * 2,
261
+ )
262
+ }
263
+ let schHeight = params.schHeight
264
+ if (!schHeight) {
265
+ schHeight = Math.max(
266
+ sideLengths.left + params.schPinSpacing * 2,
267
+ sideLengths.right + params.schPinSpacing * 2,
268
+ )
269
+ }
270
+
271
+ const trueEdgePositions = {
272
+ // Top left corner
273
+ left: {
274
+ x: -schWidth / 2 - portDistanceFromEdge,
275
+ y: sideLengths.left / 2,
276
+ },
277
+ // bottom left corner
278
+ bottom: {
279
+ x: -sideLengths.bottom / 2,
280
+ y: -schHeight / 2 - portDistanceFromEdge,
281
+ },
282
+ // bottom right corner
283
+ right: {
284
+ x: schWidth / 2 + portDistanceFromEdge,
285
+ y: -sideLengths.right / 2,
286
+ },
287
+ // top right corner
288
+ top: {
289
+ x: sideLengths.top / 2,
290
+ y: schHeight / 2 + portDistanceFromEdge,
291
+ },
292
+ }
293
+
294
+ const trueEdgeTraversalDirections = {
295
+ left: { x: 0, y: -1 },
296
+ right: { x: 0, y: 1 },
297
+ top: { x: -1, y: 0 },
298
+ bottom: { x: 1, y: 0 },
299
+ }
300
+
301
+ const truePortsWithPositions = orderedTruePorts.map((p) => {
302
+ const { distanceFromEdge, side } = p
303
+ const edgePos = trueEdgePositions[side]
304
+ const edgeDir = trueEdgeTraversalDirections[side]
305
+
306
+ return {
307
+ x: edgePos.x + distanceFromEdge * edgeDir.x,
308
+ y: edgePos.y + distanceFromEdge * edgeDir.y,
309
+ ...p,
310
+ }
311
+ })
312
+
313
+ return {
314
+ getPortPositionByPinNumber(pinNumber: number): { x: number; y: number } {
315
+ const port = truePortsWithPositions.find(
316
+ (p) => p.pinNumber.toString() === pinNumber.toString(),
317
+ )
318
+ if (!port) {
319
+ throw new Error(
320
+ `Could not find port for pin number ${pinNumber}, available pins: ${truePortsWithPositions.map((tp) => tp.pinNumber).join(", ")}`,
321
+ )
322
+ }
323
+ return port
324
+ },
325
+ getSize(): { width: number; height: number } {
326
+ return { width: schWidth, height: schHeight }
327
+ },
328
+ pinCount,
329
+ }
330
+ }
@@ -0,0 +1,47 @@
1
+ import type {
2
+ ExplicitPinMappingArrangement,
3
+ PortArrangement,
4
+ } from "./getAllDimensionsForSchematicBox"
5
+
6
+ export const hasExplicitPinMapping = (
7
+ pa: PortArrangement,
8
+ ): pa is ExplicitPinMappingArrangement => {
9
+ for (const side of [
10
+ "leftSide",
11
+ "rightSide",
12
+ "topSide",
13
+ "bottomSide",
14
+ ] as const) {
15
+ if (side in pa && typeof (pa as any)[side] === "number") {
16
+ throw new Error(
17
+ `A number was specified for "${side}", you probably meant to use "size" not "side"`,
18
+ )
19
+ }
20
+ }
21
+ return (
22
+ "leftSide" in pa ||
23
+ "rightSide" in pa ||
24
+ "topSide" in pa ||
25
+ "bottomSide" in pa
26
+ )
27
+ }
28
+
29
+ export const getSizeOfSidesFromPortArrangement = (
30
+ pa: PortArrangement,
31
+ ): {
32
+ leftSize: number
33
+ rightSize: number
34
+ topSize: number
35
+ bottomSize: number
36
+ } => {
37
+ if (hasExplicitPinMapping(pa)) {
38
+ return {
39
+ leftSize: pa.leftSide?.pins.length ?? 0,
40
+ rightSize: pa.rightSide?.pins.length ?? 0,
41
+ topSize: pa.topSide?.pins.length ?? 0,
42
+ bottomSize: pa.bottomSide?.pins.length ?? 0,
43
+ }
44
+ }
45
+ const { leftSize = 0, rightSize = 0, topSize = 0, bottomSize = 0 } = pa as any
46
+ return { leftSize, rightSize, topSize, bottomSize }
47
+ }
@@ -57,6 +57,6 @@ export function isMatchingSelector(
57
57
  const match = condition.match(/\[(\w+)=['"]?(.+?)['"]?\]/)
58
58
  if (!match) return true
59
59
  const [, prop, value] = match
60
- return component.props[prop] === value
60
+ return component.props[prop].toString() === value
61
61
  })
62
62
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
5
  "types": "index.ts",
6
- "version": "0.0.4",
6
+ "version": "0.0.6",
7
7
  "main": "dist/index.cjs",
8
8
  "files": [
9
9
  "index.ts",
@@ -11,7 +11,7 @@
11
11
  "dist"
12
12
  ],
13
13
  "scripts": {
14
- "build": "tsup index.ts"
14
+ "build": "tsup-node index.ts"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@biomejs/biome": "^1.8.3",
@@ -28,13 +28,14 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@tscircuit/infgrid-ijump-astar": "0.0.4",
31
- "@tscircuit/props": "^0.0.37",
32
- "@tscircuit/soup": "^0.0.52",
31
+ "@tscircuit/props": "^0.0.46",
32
+ "@tscircuit/soup": "^0.0.58",
33
33
  "@tscircuit/soup-util": "0.0.18",
34
34
  "footprinter": "^0.0.44",
35
35
  "react": "^18.3.1",
36
36
  "react-reconciler": "^0.29.2",
37
37
  "schematic-symbols": "0.0.10",
38
- "transformation-matrix": "^2.16.1"
38
+ "transformation-matrix": "^2.16.1",
39
+ "zod": "^3.23.8"
39
40
  }
40
41
  }