@tscircuit/props 0.0.697 → 0.0.699
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.
- package/README.md +164 -112
- package/dist/index.d.ts +20026 -2704
- package/dist/index.js +1544 -1375
- package/dist/index.js.map +1 -1
- package/lib/assembly/bolt.ts +79 -0
- package/lib/assembly/fastener.ts +78 -0
- package/lib/assembly/index.ts +6 -0
- package/lib/components/pcb-return-current-excitation.ts +110 -0
- package/lib/components/pcb-return-current-simulation.ts +25 -0
- package/lib/index.ts +1 -0
- package/lib/simulation/index.ts +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { distance, type Distance } from "lib/common/distance"
|
|
2
|
+
import { expectTypesMatch } from "lib/typecheck"
|
|
3
|
+
import { z } from "zod"
|
|
4
|
+
import { assemblyFastenerProps, type AssemblyFastenerProps } from "./fastener"
|
|
5
|
+
|
|
6
|
+
export type AssemblyBoltHeadType = "socket" | "hex" | "pan" | "countersunk"
|
|
7
|
+
|
|
8
|
+
/** A bolt specified by dimensions, or by one existing assembly CAD source. */
|
|
9
|
+
export interface AssemblyBoltProps extends AssemblyFastenerProps {
|
|
10
|
+
/** Nominal thread diameter in mm or a unit string. Required with length
|
|
11
|
+
* when no model, modelUrl, or cadModel is supplied.
|
|
12
|
+
*/
|
|
13
|
+
diameter?: Distance
|
|
14
|
+
/** Shaft length in mm or a unit string, excluding the head (including the
|
|
15
|
+
* head for countersunk bolts). Required with diameter for dimensional bolts.
|
|
16
|
+
*/
|
|
17
|
+
length?: Distance
|
|
18
|
+
/** Optional thread pitch in mm or a unit string. No pitch is inferred. */
|
|
19
|
+
threadPitch?: Distance
|
|
20
|
+
/** Head shape for dimensional bolts; defaults to socket. */
|
|
21
|
+
headType?: AssemblyBoltHeadType
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const positiveDistance = distance.pipe(z.number().positive().finite())
|
|
25
|
+
|
|
26
|
+
export const assemblyBoltProps = assemblyFastenerProps
|
|
27
|
+
.and(
|
|
28
|
+
z.object({
|
|
29
|
+
diameter: positiveDistance.optional(),
|
|
30
|
+
length: positiveDistance.optional(),
|
|
31
|
+
threadPitch: positiveDistance.optional(),
|
|
32
|
+
headType: z.enum(["socket", "hex", "pan", "countersunk"]).optional(),
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
.superRefine((bolt, ctx) => {
|
|
36
|
+
const hasModel = [bolt.model, bolt.modelUrl, bolt.cadModel].some(
|
|
37
|
+
(value) => value !== undefined,
|
|
38
|
+
)
|
|
39
|
+
if (hasModel) {
|
|
40
|
+
for (const prop of [
|
|
41
|
+
"diameter",
|
|
42
|
+
"length",
|
|
43
|
+
"threadPitch",
|
|
44
|
+
"headType",
|
|
45
|
+
] as const) {
|
|
46
|
+
if (bolt[prop] !== undefined) {
|
|
47
|
+
ctx.addIssue({
|
|
48
|
+
code: z.ZodIssueCode.custom,
|
|
49
|
+
path: [prop],
|
|
50
|
+
message:
|
|
51
|
+
"Bolt dimensions and headType are exclusive with CAD sources",
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
for (const prop of ["diameter", "length"] as const) {
|
|
57
|
+
if (bolt[prop] === undefined) {
|
|
58
|
+
ctx.addIssue({
|
|
59
|
+
code: z.ZodIssueCode.custom,
|
|
60
|
+
path: [prop],
|
|
61
|
+
message: "Provide diameter and length, or an assembly CAD source",
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.transform((bolt) =>
|
|
68
|
+
bolt.diameter === undefined
|
|
69
|
+
? bolt
|
|
70
|
+
: { ...bolt, headType: bolt.headType ?? ("socket" as const) },
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
export type AssemblyBoltPropsInput = {
|
|
74
|
+
[K in keyof z.input<typeof assemblyBoltProps>]: z.input<
|
|
75
|
+
typeof assemblyBoltProps
|
|
76
|
+
>[K]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
expectTypesMatch<AssemblyBoltProps, AssemblyBoltPropsInput>(true)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { distance, type Distance } from "lib/common/distance"
|
|
2
|
+
import { expectTypesMatch } from "lib/typecheck"
|
|
3
|
+
import { z } from "zod"
|
|
4
|
+
import { assemblyPartProps, type AssemblyPartProps } from "./part"
|
|
5
|
+
|
|
6
|
+
/** An assembly part that fastens to its mounting target, even without geometry. */
|
|
7
|
+
export interface AssemblyFastenerProps extends AssemblyPartProps {
|
|
8
|
+
/** Qualified reference surface, e.g. "FRAME.boltHole". Implies fastening.
|
|
9
|
+
* Without mountFace, use the fastener's authored local mounting origin.
|
|
10
|
+
*/
|
|
11
|
+
mountedTo?: string
|
|
12
|
+
/** Common fastener vocabulary for mountedTo; parsed output uses mountedTo.
|
|
13
|
+
* Both may be supplied only when their trimmed values agree.
|
|
14
|
+
*/
|
|
15
|
+
fastenedTo?: string
|
|
16
|
+
/** Optional named mating face on this fastener; requires a mounting target.
|
|
17
|
+
* When supplied, outward normals oppose and in-plane X directions align.
|
|
18
|
+
*/
|
|
19
|
+
mountFace?: string
|
|
20
|
+
/** Nonnegative surface clearance in mm or a unit string; defaults to zero
|
|
21
|
+
* in the consumer. Requires a mounting target; omitted values stay omitted.
|
|
22
|
+
*/
|
|
23
|
+
mountGap?: Distance
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const mountingTarget = z
|
|
27
|
+
.string()
|
|
28
|
+
.trim()
|
|
29
|
+
.regex(
|
|
30
|
+
/^[A-Za-z_][A-Za-z0-9_-]*(?:\.[A-Za-z_][A-Za-z0-9_-]*)+$/,
|
|
31
|
+
"Expected a qualified reference surface, e.g. part.face",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export const assemblyFastenerProps = assemblyPartProps
|
|
35
|
+
.and(
|
|
36
|
+
z.object({
|
|
37
|
+
mountedTo: mountingTarget.optional(),
|
|
38
|
+
fastenedTo: mountingTarget.optional(),
|
|
39
|
+
mountFace: z.string().trim().min(1).optional(),
|
|
40
|
+
mountGap: distance.pipe(z.number().nonnegative().finite()).optional(),
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
43
|
+
.superRefine((fastener, ctx) => {
|
|
44
|
+
if (
|
|
45
|
+
fastener.mountedTo !== undefined &&
|
|
46
|
+
fastener.fastenedTo !== undefined &&
|
|
47
|
+
fastener.mountedTo !== fastener.fastenedTo
|
|
48
|
+
) {
|
|
49
|
+
ctx.addIssue({
|
|
50
|
+
code: z.ZodIssueCode.custom,
|
|
51
|
+
path: ["fastenedTo"],
|
|
52
|
+
message: "mountedTo and fastenedTo must refer to the same surface",
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
if (fastener.mountedTo === undefined && fastener.fastenedTo === undefined) {
|
|
56
|
+
for (const prop of ["mountFace", "mountGap"] as const) {
|
|
57
|
+
if (fastener[prop] !== undefined) {
|
|
58
|
+
ctx.addIssue({
|
|
59
|
+
code: z.ZodIssueCode.custom,
|
|
60
|
+
path: [prop],
|
|
61
|
+
message: `${prop} requires mountedTo or fastenedTo`,
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.transform(({ fastenedTo, ...fastener }) => {
|
|
68
|
+
const mountedTo = fastener.mountedTo ?? fastenedTo
|
|
69
|
+
return mountedTo === undefined ? fastener : { ...fastener, mountedTo }
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
export type AssemblyFastenerPropsInput = {
|
|
73
|
+
[K in keyof z.input<typeof assemblyFastenerProps>]: z.input<
|
|
74
|
+
typeof assemblyFastenerProps
|
|
75
|
+
>[K]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
expectTypesMatch<AssemblyFastenerProps, AssemblyFastenerPropsInput>(true)
|
package/lib/assembly/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { assemblyCadAssemblyProps } from "./cadassembly"
|
|
2
2
|
import { assemblyCableProps } from "./cable"
|
|
3
|
+
import { assemblyBoltProps } from "./bolt"
|
|
4
|
+
import { assemblyFastenerProps } from "./fastener"
|
|
3
5
|
import { assemblyDeviceProps } from "./device"
|
|
4
6
|
import { assemblyPartProps } from "./part"
|
|
5
7
|
import { assemblyReferenceSurfaceProps } from "./referencesurface"
|
|
@@ -9,6 +11,8 @@ import { assemblyScreenProps } from "./screen"
|
|
|
9
11
|
import { assemblySubassemblyProps } from "./subassembly"
|
|
10
12
|
|
|
11
13
|
export * from "./board-mounting"
|
|
14
|
+
export * from "./bolt"
|
|
15
|
+
export * from "./fastener"
|
|
12
16
|
export * from "./cable"
|
|
13
17
|
export * from "./device"
|
|
14
18
|
export * from "./motor"
|
|
@@ -20,6 +24,8 @@ export * from "./subassembly"
|
|
|
20
24
|
export * from "./cadassembly"
|
|
21
25
|
|
|
22
26
|
export const assemblyProps = {
|
|
27
|
+
bolt: assemblyBoltProps,
|
|
28
|
+
fastener: assemblyFastenerProps,
|
|
23
29
|
cable: assemblyCableProps,
|
|
24
30
|
device: assemblyDeviceProps,
|
|
25
31
|
motor: assemblyMotorProps,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { layer_ref, type LayerRefInput } from "circuit-json"
|
|
2
|
+
import { expectTypesMatch } from "lib/typecheck"
|
|
3
|
+
import { z } from "zod"
|
|
4
|
+
|
|
5
|
+
/** A signal driver/load pair and its explicit ground return terminals.
|
|
6
|
+
* Selectors resolve inside the containing board/subcircuit. The initial core
|
|
7
|
+
* implementation accepts physical PCB ports/pads, not standalone via contacts.
|
|
8
|
+
* This definition neither connects the selected pins nor runs a simulation.
|
|
9
|
+
*/
|
|
10
|
+
export interface PcbReturnCurrentExcitationProps {
|
|
11
|
+
/** Readable excitation name for diagnostics; not stored on its Circuit JSON record. */
|
|
12
|
+
name?: string
|
|
13
|
+
/** Signal driver port selector, e.g. ".U1 > .OUT". */
|
|
14
|
+
source: string
|
|
15
|
+
/** Signal receiving port selector, e.g. ".U2 > .IN". */
|
|
16
|
+
load: string
|
|
17
|
+
/** Ground net selector, e.g. "net.GND"; its copper and connections must already exist. */
|
|
18
|
+
ground: string
|
|
19
|
+
/** Positive in-phase peak signal current. Numbers are amperes, not RMS; strings use ampere units. */
|
|
20
|
+
current: number | string
|
|
21
|
+
/** Load-side GND port where positive return current enters the return conductor. */
|
|
22
|
+
returnSource: string
|
|
23
|
+
/** Driver-side GND port where positive return current leaves the return conductor. */
|
|
24
|
+
returnSink: string
|
|
25
|
+
/** Positive real source-port resistance. Numbers are ohms; complex impedances are unsupported. */
|
|
26
|
+
sourceImpedance: number | string
|
|
27
|
+
/** Positive real load-port resistance. Numbers are ohms; complex impedances are unsupported. */
|
|
28
|
+
loadImpedance: number | string
|
|
29
|
+
/** Optional trace selector to disambiguate multiple routes between the signal ports. */
|
|
30
|
+
trace?: string
|
|
31
|
+
/** Contact layer for a load-side PCB port spanning multiple copper layers. */
|
|
32
|
+
returnSourceLayer?: LayerRefInput
|
|
33
|
+
/** Contact layer for a driver-side PCB port spanning multiple copper layers. */
|
|
34
|
+
returnSinkLayer?: LayerRefInput
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const selector = z.string().trim().min(1, "Provide a nonempty selector")
|
|
38
|
+
const quantity = "[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?"
|
|
39
|
+
const prefixScale: Record<string, number> = {
|
|
40
|
+
"": 1,
|
|
41
|
+
y: 1e-24,
|
|
42
|
+
z: 1e-21,
|
|
43
|
+
a: 1e-18,
|
|
44
|
+
f: 1e-15,
|
|
45
|
+
p: 1e-12,
|
|
46
|
+
n: 1e-9,
|
|
47
|
+
u: 1e-6,
|
|
48
|
+
µ: 1e-6,
|
|
49
|
+
μ: 1e-6,
|
|
50
|
+
m: 1e-3,
|
|
51
|
+
k: 1e3,
|
|
52
|
+
M: 1e6,
|
|
53
|
+
G: 1e9,
|
|
54
|
+
T: 1e12,
|
|
55
|
+
P: 1e15,
|
|
56
|
+
E: 1e18,
|
|
57
|
+
Z: 1e21,
|
|
58
|
+
Y: 1e24,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Full-string parsing preserves scientific notation and rejects other
|
|
62
|
+
// dimensions. The generic Circuit JSON converters currently do neither.
|
|
63
|
+
function positiveQuantity(unit: string, label: string, example: string) {
|
|
64
|
+
const pattern = new RegExp(
|
|
65
|
+
`^(${quantity})\\s*(?:([yzafpnumkMGTPEZYµμ]?)(?:${unit}))?$`,
|
|
66
|
+
)
|
|
67
|
+
return z
|
|
68
|
+
.union([
|
|
69
|
+
z.number(),
|
|
70
|
+
z
|
|
71
|
+
.string()
|
|
72
|
+
.trim()
|
|
73
|
+
.regex(pattern, `Use ${label} or a unit string, e.g. ${example}`),
|
|
74
|
+
])
|
|
75
|
+
.transform((value) => {
|
|
76
|
+
if (typeof value === "number") return value
|
|
77
|
+
const match = pattern.exec(value)!
|
|
78
|
+
return Number(match[1]) * prefixScale[match[2] ?? ""]!
|
|
79
|
+
})
|
|
80
|
+
.pipe(z.number().finite().positive(`${label} must be greater than zero`))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const positiveCurrent = positiveQuantity("A", "peak amperes", "5mA")
|
|
84
|
+
const positiveResistance = positiveQuantity(
|
|
85
|
+
"ohms?|Ohms?|Ω",
|
|
86
|
+
"real ohms",
|
|
87
|
+
"100ohm",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
export const pcbReturnCurrentExcitationProps = z
|
|
91
|
+
.object({
|
|
92
|
+
name: z.string().trim().min(1).optional(),
|
|
93
|
+
source: selector,
|
|
94
|
+
load: selector,
|
|
95
|
+
ground: selector,
|
|
96
|
+
current: positiveCurrent,
|
|
97
|
+
returnSource: selector,
|
|
98
|
+
returnSink: selector,
|
|
99
|
+
sourceImpedance: positiveResistance,
|
|
100
|
+
loadImpedance: positiveResistance,
|
|
101
|
+
trace: selector.optional(),
|
|
102
|
+
returnSourceLayer: layer_ref.optional(),
|
|
103
|
+
returnSinkLayer: layer_ref.optional(),
|
|
104
|
+
})
|
|
105
|
+
.strict()
|
|
106
|
+
|
|
107
|
+
expectTypesMatch<
|
|
108
|
+
PcbReturnCurrentExcitationProps,
|
|
109
|
+
z.input<typeof pcbReturnCurrentExcitationProps>
|
|
110
|
+
>(true)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { expectTypesMatch } from "lib/typecheck"
|
|
2
|
+
import type { ReactNode } from "react"
|
|
3
|
+
import { z } from "zod"
|
|
4
|
+
|
|
5
|
+
/** Defines a pending PCB return-current experiment; rendering does not run it.
|
|
6
|
+
* Frequency, mesh and sampling settings are supplied to the simulation CLI.
|
|
7
|
+
*/
|
|
8
|
+
export interface PcbReturnCurrentSimulationProps {
|
|
9
|
+
/** Stable identity and readable name for the experiment. */
|
|
10
|
+
name?: string
|
|
11
|
+
/** One or more nested simulation.pcbreturncurrentexcitation elements. */
|
|
12
|
+
children?: ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const pcbReturnCurrentSimulationProps = z
|
|
16
|
+
.object({
|
|
17
|
+
name: z.string().trim().min(1).optional(),
|
|
18
|
+
children: z.custom<ReactNode>().optional(),
|
|
19
|
+
})
|
|
20
|
+
.strict()
|
|
21
|
+
|
|
22
|
+
expectTypesMatch<
|
|
23
|
+
PcbReturnCurrentSimulationProps,
|
|
24
|
+
z.input<typeof pcbReturnCurrentSimulationProps>
|
|
25
|
+
>(true)
|
package/lib/index.ts
CHANGED
|
@@ -81,6 +81,7 @@ export * from "./components/analogtransientsimulation"
|
|
|
81
81
|
export * from "./components/analogdcoperatingpointsimulation"
|
|
82
82
|
export * from "./components/analogdcsweepsimulation"
|
|
83
83
|
export * from "./components/analogacsweepsimulation"
|
|
84
|
+
export * from "./simulation"
|
|
84
85
|
export * from "./components/analogsweepparameter"
|
|
85
86
|
export * from "./components/autoroutingphase"
|
|
86
87
|
export * from "./components/spicemodel"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { pcbReturnCurrentExcitationProps } from "../components/pcb-return-current-excitation"
|
|
2
|
+
import { pcbReturnCurrentSimulationProps } from "../components/pcb-return-current-simulation"
|
|
3
|
+
|
|
4
|
+
export * from "../components/pcb-return-current-excitation"
|
|
5
|
+
export * from "../components/pcb-return-current-simulation"
|
|
6
|
+
|
|
7
|
+
export const simulationProps = {
|
|
8
|
+
pcbreturncurrentsimulation: pcbReturnCurrentSimulationProps,
|
|
9
|
+
pcbreturncurrentexcitation: pcbReturnCurrentExcitationProps,
|
|
10
|
+
} as const
|