kicadts 0.0.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 (188) hide show
  1. package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
  2. package/.vscode/settings.json +16 -0
  3. package/AGENTS.md +30 -0
  4. package/README.md +206 -0
  5. package/biome.json +93 -0
  6. package/bun.lock +48 -0
  7. package/bunfig.toml +5 -0
  8. package/lib/index.ts +1 -0
  9. package/lib/sexpr/base-classes/SxClass.ts +164 -0
  10. package/lib/sexpr/base-classes/SxPrimitiveBoolean.ts +35 -0
  11. package/lib/sexpr/base-classes/SxPrimitiveNumber.ts +26 -0
  12. package/lib/sexpr/base-classes/SxPrimitiveString.ts +26 -0
  13. package/lib/sexpr/classes/At.ts +38 -0
  14. package/lib/sexpr/classes/Bus.ts +83 -0
  15. package/lib/sexpr/classes/BusEntry.ts +142 -0
  16. package/lib/sexpr/classes/Color.ts +29 -0
  17. package/lib/sexpr/classes/Dnp.ts +8 -0
  18. package/lib/sexpr/classes/EmbeddedFonts.ts +70 -0
  19. package/lib/sexpr/classes/ExcludeFromSim.ts +8 -0
  20. package/lib/sexpr/classes/FieldsAutoplaced.ts +8 -0
  21. package/lib/sexpr/classes/Footprint.ts +719 -0
  22. package/lib/sexpr/classes/FootprintAttr.ts +102 -0
  23. package/lib/sexpr/classes/FootprintAutoplaceCost180.ts +9 -0
  24. package/lib/sexpr/classes/FootprintAutoplaceCost90.ts +9 -0
  25. package/lib/sexpr/classes/FootprintClearance.ts +9 -0
  26. package/lib/sexpr/classes/FootprintDescr.ts +44 -0
  27. package/lib/sexpr/classes/FootprintLocked.ts +32 -0
  28. package/lib/sexpr/classes/FootprintModel.ts +145 -0
  29. package/lib/sexpr/classes/FootprintNetTiePadGroups.ts +50 -0
  30. package/lib/sexpr/classes/FootprintPad.ts +705 -0
  31. package/lib/sexpr/classes/FootprintPath.ts +44 -0
  32. package/lib/sexpr/classes/FootprintPlaced.ts +32 -0
  33. package/lib/sexpr/classes/FootprintPrivateLayers.ts +56 -0
  34. package/lib/sexpr/classes/FootprintSheetfile.ts +44 -0
  35. package/lib/sexpr/classes/FootprintSheetname.ts +44 -0
  36. package/lib/sexpr/classes/FootprintSolderMaskMargin.ts +9 -0
  37. package/lib/sexpr/classes/FootprintSolderPasteMargin.ts +9 -0
  38. package/lib/sexpr/classes/FootprintSolderPasteRatio.ts +9 -0
  39. package/lib/sexpr/classes/FootprintTags.ts +44 -0
  40. package/lib/sexpr/classes/FootprintTedit.ts +21 -0
  41. package/lib/sexpr/classes/FootprintThermalGap.ts +9 -0
  42. package/lib/sexpr/classes/FootprintThermalWidth.ts +9 -0
  43. package/lib/sexpr/classes/FootprintZoneConnect.ts +9 -0
  44. package/lib/sexpr/classes/FpArc.ts +289 -0
  45. package/lib/sexpr/classes/FpCircle.ts +293 -0
  46. package/lib/sexpr/classes/FpLine.ts +288 -0
  47. package/lib/sexpr/classes/FpPoly.ts +266 -0
  48. package/lib/sexpr/classes/FpPolyFill.ts +48 -0
  49. package/lib/sexpr/classes/FpPolyLocked.ts +40 -0
  50. package/lib/sexpr/classes/FpRect.ts +293 -0
  51. package/lib/sexpr/classes/FpText.ts +341 -0
  52. package/lib/sexpr/classes/FpTextBox.ts +412 -0
  53. package/lib/sexpr/classes/GrLine.ts +245 -0
  54. package/lib/sexpr/classes/GrLineAngle.ts +32 -0
  55. package/lib/sexpr/classes/GrLineEnd.ts +61 -0
  56. package/lib/sexpr/classes/GrLineLocked.ts +40 -0
  57. package/lib/sexpr/classes/GrLineStart.ts +61 -0
  58. package/lib/sexpr/classes/GrText.ts +202 -0
  59. package/lib/sexpr/classes/Image.ts +256 -0
  60. package/lib/sexpr/classes/InBom.ts +8 -0
  61. package/lib/sexpr/classes/Junction.ts +134 -0
  62. package/lib/sexpr/classes/KicadPcb.ts +313 -0
  63. package/lib/sexpr/classes/KicadSch.ts +303 -0
  64. package/lib/sexpr/classes/KicadSchGenerator.ts +32 -0
  65. package/lib/sexpr/classes/KicadSchGeneratorVersion.ts +30 -0
  66. package/lib/sexpr/classes/KicadSchVersion.ts +22 -0
  67. package/lib/sexpr/classes/Label.ts +136 -0
  68. package/lib/sexpr/classes/Layer.ts +51 -0
  69. package/lib/sexpr/classes/Layers.ts +47 -0
  70. package/lib/sexpr/classes/LibSymbols.ts +61 -0
  71. package/lib/sexpr/classes/NoConnect.ts +73 -0
  72. package/lib/sexpr/classes/OnBoard.ts +8 -0
  73. package/lib/sexpr/classes/PadChamfer.ts +50 -0
  74. package/lib/sexpr/classes/PadChamferRatio.ts +9 -0
  75. package/lib/sexpr/classes/PadClearance.ts +9 -0
  76. package/lib/sexpr/classes/PadDieLength.ts +9 -0
  77. package/lib/sexpr/classes/PadDrill.ts +145 -0
  78. package/lib/sexpr/classes/PadDrillOffset.ts +54 -0
  79. package/lib/sexpr/classes/PadLayers.ts +59 -0
  80. package/lib/sexpr/classes/PadNet.ts +56 -0
  81. package/lib/sexpr/classes/PadOptions.ts +182 -0
  82. package/lib/sexpr/classes/PadPinFunction.ts +9 -0
  83. package/lib/sexpr/classes/PadPinType.ts +9 -0
  84. package/lib/sexpr/classes/PadPrimitiveGrArc.ts +254 -0
  85. package/lib/sexpr/classes/PadPrimitiveGrCircle.ts +279 -0
  86. package/lib/sexpr/classes/PadPrimitiveGrLine.ts +126 -0
  87. package/lib/sexpr/classes/PadPrimitives.ts +289 -0
  88. package/lib/sexpr/classes/PadRectDelta.ts +57 -0
  89. package/lib/sexpr/classes/PadRoundrectRratio.ts +9 -0
  90. package/lib/sexpr/classes/PadSize.ts +54 -0
  91. package/lib/sexpr/classes/PadSolderMaskMargin.ts +9 -0
  92. package/lib/sexpr/classes/PadSolderPasteMargin.ts +9 -0
  93. package/lib/sexpr/classes/PadSolderPasteMarginRatio.ts +9 -0
  94. package/lib/sexpr/classes/PadTeardrops.ts +208 -0
  95. package/lib/sexpr/classes/PadThermalBridgeAngle.ts +9 -0
  96. package/lib/sexpr/classes/PadThermalGap.ts +9 -0
  97. package/lib/sexpr/classes/PadThermalWidth.ts +9 -0
  98. package/lib/sexpr/classes/PadZoneConnect.ts +9 -0
  99. package/lib/sexpr/classes/Paper.ts +119 -0
  100. package/lib/sexpr/classes/PcbGeneral.ts +75 -0
  101. package/lib/sexpr/classes/PcbGeneralLegacyTeardrops.ts +44 -0
  102. package/lib/sexpr/classes/PcbGeneralThickness.ts +9 -0
  103. package/lib/sexpr/classes/PcbGenerator.ts +16 -0
  104. package/lib/sexpr/classes/PcbGeneratorVersion.ts +16 -0
  105. package/lib/sexpr/classes/PcbLayerDefinition.ts +102 -0
  106. package/lib/sexpr/classes/PcbLayers.ts +34 -0
  107. package/lib/sexpr/classes/PcbNet.ts +56 -0
  108. package/lib/sexpr/classes/PcbVersion.ts +9 -0
  109. package/lib/sexpr/classes/Property.ts +246 -0
  110. package/lib/sexpr/classes/PropertyHide.ts +9 -0
  111. package/lib/sexpr/classes/PropertyUnlocked.ts +9 -0
  112. package/lib/sexpr/classes/Pts.ts +65 -0
  113. package/lib/sexpr/classes/RenderCache.ts +221 -0
  114. package/lib/sexpr/classes/SchematicText.ts +141 -0
  115. package/lib/sexpr/classes/Segment.ts +222 -0
  116. package/lib/sexpr/classes/SegmentEnd.ts +59 -0
  117. package/lib/sexpr/classes/SegmentLocked.ts +33 -0
  118. package/lib/sexpr/classes/SegmentNet.ts +62 -0
  119. package/lib/sexpr/classes/SegmentStart.ts +59 -0
  120. package/lib/sexpr/classes/Setup/PcbPlotParams.ts +729 -0
  121. package/lib/sexpr/classes/Setup/PcbPlotParamsBase.ts +9 -0
  122. package/lib/sexpr/classes/Setup/PcbPlotParamsNumericProperties.ts +105 -0
  123. package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesA.ts +104 -0
  124. package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesB.ts +105 -0
  125. package/lib/sexpr/classes/Setup/Setup.ts +573 -0
  126. package/lib/sexpr/classes/Setup/SetupPropertyTypes.ts +119 -0
  127. package/lib/sexpr/classes/Setup/Stackup.ts +140 -0
  128. package/lib/sexpr/classes/Setup/StackupLayer.ts +233 -0
  129. package/lib/sexpr/classes/Setup/StackupLayerProperties.ts +78 -0
  130. package/lib/sexpr/classes/Setup/StackupProperties.ts +41 -0
  131. package/lib/sexpr/classes/Setup/base.ts +167 -0
  132. package/lib/sexpr/classes/Setup/index.ts +14 -0
  133. package/lib/sexpr/classes/Setup/setupMultiValueProperties.ts +54 -0
  134. package/lib/sexpr/classes/Setup/setupNumericProperties.ts +151 -0
  135. package/lib/sexpr/classes/Setup/setupPropertyHandlers.ts +90 -0
  136. package/lib/sexpr/classes/Setup/setupStringProperties.ts +75 -0
  137. package/lib/sexpr/classes/Sheet.ts +205 -0
  138. package/lib/sexpr/classes/SheetFill.ts +44 -0
  139. package/lib/sexpr/classes/SheetInstances.ts +168 -0
  140. package/lib/sexpr/classes/SheetInstancesRoot.ts +165 -0
  141. package/lib/sexpr/classes/SheetPin.ts +122 -0
  142. package/lib/sexpr/classes/SheetProperty.ts +115 -0
  143. package/lib/sexpr/classes/SheetSize.ts +44 -0
  144. package/lib/sexpr/classes/Stroke.ts +58 -0
  145. package/lib/sexpr/classes/StrokeType.ts +34 -0
  146. package/lib/sexpr/classes/Symbol.ts +1541 -0
  147. package/lib/sexpr/classes/TextEffects.ts +444 -0
  148. package/lib/sexpr/classes/TitleBlock.ts +352 -0
  149. package/lib/sexpr/classes/Unit.ts +28 -0
  150. package/lib/sexpr/classes/Uuid.ts +8 -0
  151. package/lib/sexpr/classes/Via.ts +328 -0
  152. package/lib/sexpr/classes/ViaNet.ts +59 -0
  153. package/lib/sexpr/classes/Width.ts +8 -0
  154. package/lib/sexpr/classes/Wire.ts +91 -0
  155. package/lib/sexpr/classes/Xy.ts +35 -0
  156. package/lib/sexpr/classes/Zone.ts +41 -0
  157. package/lib/sexpr/index.ts +130 -0
  158. package/lib/sexpr/parseKicadSexpr.ts +5 -0
  159. package/lib/sexpr/parseToPrimitiveSExpr.ts +240 -0
  160. package/lib/sexpr/utils/indentLines.ts +3 -0
  161. package/lib/sexpr/utils/parseYesNo.ts +12 -0
  162. package/lib/sexpr/utils/quoteSExprString.ts +8 -0
  163. package/lib/sexpr/utils/strokeFromArgs.ts +19 -0
  164. package/lib/sexpr/utils/toNumberValue.ts +13 -0
  165. package/lib/sexpr/utils/toStringValue.ts +10 -0
  166. package/package.json +26 -0
  167. package/scripts/download-references.ts +66 -0
  168. package/tests/fixtures/expectEqualPrimitiveSExpr.ts +200 -0
  169. package/tests/sexpr/KicadPcbDemos.test.ts +48 -0
  170. package/tests/sexpr/KicadSchDemos.test.ts +49 -0
  171. package/tests/sexpr/classes/Footprint.test.ts +277 -0
  172. package/tests/sexpr/classes/FootprintPad.test.ts +71 -0
  173. package/tests/sexpr/classes/FpArc.test.ts +45 -0
  174. package/tests/sexpr/classes/FpCircle.test.ts +39 -0
  175. package/tests/sexpr/classes/FpPoly.test.ts +43 -0
  176. package/tests/sexpr/classes/FpRect.test.ts +40 -0
  177. package/tests/sexpr/classes/FpTextBox.test.ts +84 -0
  178. package/tests/sexpr/classes/Image.test.ts +50 -0
  179. package/tests/sexpr/classes/KicadSch.test.ts +97 -0
  180. package/tests/sexpr/classes/Paper.test.ts +30 -0
  181. package/tests/sexpr/classes/Property.test.ts +48 -0
  182. package/tests/sexpr/classes/Setup.test.ts +189 -0
  183. package/tests/sexpr/classes/Sheet.test.ts +107 -0
  184. package/tests/sexpr/classes/Stroke.test.ts +15 -0
  185. package/tests/sexpr/classes/Symbol.test.ts +96 -0
  186. package/tests/sexpr/classes/TextEffects.test.ts +56 -0
  187. package/tests/sexpr/classes/TitleBlock.test.ts +40 -0
  188. package/tsconfig.json +35 -0
@@ -0,0 +1,61 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { toNumberValue } from "../utils/toNumberValue"
4
+
5
+ export class GrLineEnd extends SxClass {
6
+ static override token = "end"
7
+ static override parentToken = "gr_line"
8
+ override token = "end"
9
+
10
+ private _x: number
11
+ private _y: number
12
+
13
+ constructor(x: number, y: number) {
14
+ super()
15
+ this._x = x
16
+ this._y = y
17
+ }
18
+
19
+ static override fromSexprPrimitives(
20
+ primitiveSexprs: PrimitiveSExpr[],
21
+ ): GrLineEnd {
22
+ const [rawX, rawY] = primitiveSexprs
23
+ const x = toNumberValue(rawX)
24
+ const y = toNumberValue(rawY)
25
+
26
+ if (x === undefined || y === undefined) {
27
+ throw new Error("gr_line end expects two numeric arguments")
28
+ }
29
+
30
+ return new GrLineEnd(x, y)
31
+ }
32
+
33
+ get x(): number {
34
+ return this._x
35
+ }
36
+
37
+ set x(value: number) {
38
+ this._x = value
39
+ }
40
+
41
+ get y(): number {
42
+ return this._y
43
+ }
44
+
45
+ set y(value: number) {
46
+ this._y = value
47
+ }
48
+
49
+ toObject(): { x: number; y: number } {
50
+ return { x: this._x, y: this._y }
51
+ }
52
+
53
+ override getChildren(): SxClass[] {
54
+ return []
55
+ }
56
+
57
+ override getString(): string {
58
+ return `(end ${this._x} ${this._y})`
59
+ }
60
+ }
61
+ SxClass.register(GrLineEnd)
@@ -0,0 +1,40 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import { SxPrimitiveBoolean } from "../base-classes/SxPrimitiveBoolean"
3
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
4
+
5
+ const truthyStrings = new Set(["true", "yes", "1"])
6
+
7
+ export class GrLineLocked extends SxPrimitiveBoolean {
8
+ static override token = "locked"
9
+ static override parentToken = "gr_line"
10
+ override token = "locked"
11
+
12
+ constructor(value: boolean) {
13
+ super(value)
14
+ }
15
+
16
+ static override fromSexprPrimitives(
17
+ primitiveSexprs: PrimitiveSExpr[],
18
+ ): GrLineLocked {
19
+ const [rawValue] = primitiveSexprs
20
+
21
+ if (rawValue === undefined) {
22
+ return new GrLineLocked(true)
23
+ }
24
+
25
+ if (typeof rawValue === "boolean") {
26
+ return new GrLineLocked(rawValue)
27
+ }
28
+
29
+ if (typeof rawValue === "string") {
30
+ return new GrLineLocked(truthyStrings.has(rawValue.toLowerCase()))
31
+ }
32
+
33
+ return new GrLineLocked(false)
34
+ }
35
+
36
+ override getString(): string {
37
+ return `(locked ${this.value ? "yes" : "no"})`
38
+ }
39
+ }
40
+ SxClass.register(GrLineLocked)
@@ -0,0 +1,61 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { toNumberValue } from "../utils/toNumberValue"
4
+
5
+ export class GrLineStart extends SxClass {
6
+ static override token = "start"
7
+ static override parentToken = "gr_line"
8
+ override token = "start"
9
+
10
+ private _x: number
11
+ private _y: number
12
+
13
+ constructor(x: number, y: number) {
14
+ super()
15
+ this._x = x
16
+ this._y = y
17
+ }
18
+
19
+ static override fromSexprPrimitives(
20
+ primitiveSexprs: PrimitiveSExpr[],
21
+ ): GrLineStart {
22
+ const [rawX, rawY] = primitiveSexprs
23
+ const x = toNumberValue(rawX)
24
+ const y = toNumberValue(rawY)
25
+
26
+ if (x === undefined || y === undefined) {
27
+ throw new Error("gr_line start expects two numeric arguments")
28
+ }
29
+
30
+ return new GrLineStart(x, y)
31
+ }
32
+
33
+ get x(): number {
34
+ return this._x
35
+ }
36
+
37
+ set x(value: number) {
38
+ this._x = value
39
+ }
40
+
41
+ get y(): number {
42
+ return this._y
43
+ }
44
+
45
+ set y(value: number) {
46
+ this._y = value
47
+ }
48
+
49
+ toObject(): { x: number; y: number } {
50
+ return { x: this._x, y: this._y }
51
+ }
52
+
53
+ override getChildren(): SxClass[] {
54
+ return []
55
+ }
56
+
57
+ override getString(): string {
58
+ return `(start ${this._x} ${this._y})`
59
+ }
60
+ }
61
+ SxClass.register(GrLineStart)
@@ -0,0 +1,202 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { quoteSExprString } from "../utils/quoteSExprString"
4
+ import { toStringValue } from "../utils/toStringValue"
5
+ import { At } from "./At"
6
+ import { Xy } from "./Xy"
7
+ import { Layer } from "./Layer"
8
+ import { TextEffects } from "./TextEffects"
9
+ import { Uuid } from "./Uuid"
10
+
11
+ export interface GrTextPosition {
12
+ x: number
13
+ y: number
14
+ angle?: number
15
+ }
16
+
17
+ const SUPPORTED_SINGLE_TOKENS = new Set([
18
+ "at",
19
+ "xy",
20
+ "layer",
21
+ "uuid",
22
+ "effects",
23
+ ])
24
+
25
+ export class GrText extends SxClass {
26
+ static override token = "gr_text"
27
+ override token = "gr_text"
28
+
29
+ private _text = ""
30
+ private _sxPosition?: At | Xy
31
+ private _sxLayer?: Layer
32
+ private _sxUuid?: Uuid
33
+ private _sxEffects?: TextEffects
34
+
35
+ constructor(text = "") {
36
+ super()
37
+ this.text = text
38
+ }
39
+
40
+ static override fromSexprPrimitives(
41
+ primitiveSexprs: PrimitiveSExpr[],
42
+ ): GrText {
43
+ if (primitiveSexprs.length === 0) {
44
+ throw new Error("gr_text requires a text argument")
45
+ }
46
+
47
+ const [rawText, ...rest] = primitiveSexprs
48
+ const text = toStringValue(rawText)
49
+ if (text === undefined) {
50
+ throw new Error("gr_text text must be a string value")
51
+ }
52
+
53
+ const grText = new GrText(text)
54
+
55
+ const { propertyMap, arrayPropertyMap } =
56
+ SxClass.parsePrimitivesToClassProperties(rest, this.token)
57
+
58
+ const unexpectedTokens = new Set<string>()
59
+ for (const token of Object.keys(propertyMap)) {
60
+ if (!SUPPORTED_SINGLE_TOKENS.has(token)) {
61
+ unexpectedTokens.add(token)
62
+ }
63
+ }
64
+ for (const token of Object.keys(arrayPropertyMap)) {
65
+ if (!SUPPORTED_SINGLE_TOKENS.has(token)) {
66
+ unexpectedTokens.add(token)
67
+ continue
68
+ }
69
+ if (arrayPropertyMap[token]!.length > 1) {
70
+ throw new Error(
71
+ `gr_text does not support repeated child tokens: ${token}`,
72
+ )
73
+ }
74
+ }
75
+
76
+ if (unexpectedTokens.size > 0) {
77
+ throw new Error(
78
+ `Unsupported child tokens inside gr_text expression: ${[...unexpectedTokens].join(", ")}`,
79
+ )
80
+ }
81
+
82
+ for (const primitive of rest) {
83
+ if (Array.isArray(primitive)) continue
84
+ throw new Error(
85
+ `gr_text encountered unexpected primitive child: ${JSON.stringify(primitive)}`,
86
+ )
87
+ }
88
+
89
+ const atInstance = propertyMap.at as At | undefined
90
+ const xyInstance = propertyMap.xy as Xy | undefined
91
+ if (atInstance && xyInstance) {
92
+ throw new Error("gr_text cannot include both at and xy tokens")
93
+ }
94
+
95
+ grText._sxPosition = atInstance ?? xyInstance
96
+ grText._sxLayer = propertyMap.layer as Layer | undefined
97
+ grText._sxUuid = propertyMap.uuid as Uuid | undefined
98
+ grText._sxEffects = propertyMap.effects as TextEffects | undefined
99
+
100
+ if (!grText._sxPosition) {
101
+ throw new Error("gr_text requires a position child token")
102
+ }
103
+ if (!grText._sxLayer) {
104
+ throw new Error("gr_text requires a layer child token")
105
+ }
106
+ if (!grText._sxUuid) {
107
+ throw new Error("gr_text requires a uuid child token")
108
+ }
109
+ if (!grText._sxEffects) {
110
+ throw new Error("gr_text requires an effects child token")
111
+ }
112
+
113
+ return grText
114
+ }
115
+
116
+ get text(): string {
117
+ return this._text
118
+ }
119
+
120
+ set text(value: string) {
121
+ this._text = value
122
+ }
123
+
124
+ get position(): At | Xy | undefined {
125
+ return this._sxPosition
126
+ }
127
+
128
+ set position(
129
+ value: At | Xy | GrTextPosition | undefined,
130
+ ) {
131
+ if (value === undefined) {
132
+ this._sxPosition = undefined
133
+ return
134
+ }
135
+ if (value instanceof At || value instanceof Xy) {
136
+ this._sxPosition = value
137
+ return
138
+ }
139
+ const { x, y, angle } = value
140
+ if (angle !== undefined) {
141
+ this._sxPosition = new At([x, y, angle])
142
+ } else {
143
+ this._sxPosition = new Xy(x, y)
144
+ }
145
+ }
146
+
147
+ get layer(): Layer | undefined {
148
+ return this._sxLayer
149
+ }
150
+
151
+ set layer(value: Layer | string | Array<string | number> | undefined) {
152
+ if (value === undefined) {
153
+ this._sxLayer = undefined
154
+ return
155
+ }
156
+ if (value instanceof Layer) {
157
+ this._sxLayer = value
158
+ return
159
+ }
160
+ const names = Array.isArray(value) ? value : [value]
161
+ this._sxLayer = new Layer(names)
162
+ }
163
+
164
+ get uuid(): Uuid | undefined {
165
+ return this._sxUuid
166
+ }
167
+
168
+ set uuid(value: Uuid | string | undefined) {
169
+ if (value === undefined) {
170
+ this._sxUuid = undefined
171
+ return
172
+ }
173
+ this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
174
+ }
175
+
176
+ get effects(): TextEffects | undefined {
177
+ return this._sxEffects
178
+ }
179
+
180
+ set effects(value: TextEffects | undefined) {
181
+ this._sxEffects = value
182
+ }
183
+
184
+ override getChildren(): SxClass[] {
185
+ const children: SxClass[] = []
186
+ if (this._sxPosition) children.push(this._sxPosition)
187
+ if (this._sxLayer) children.push(this._sxLayer)
188
+ if (this._sxUuid) children.push(this._sxUuid)
189
+ if (this._sxEffects) children.push(this._sxEffects)
190
+ return children
191
+ }
192
+
193
+ override getString(): string {
194
+ const lines = ["(gr_text", ` ${quoteSExprString(this._text)}`]
195
+ for (const child of this.getChildren()) {
196
+ lines.push(child.getStringIndented())
197
+ }
198
+ lines.push(")")
199
+ return lines.join("\n")
200
+ }
201
+ }
202
+ SxClass.register(GrText)
@@ -0,0 +1,256 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import { printSExpr, type PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { quoteSExprString } from "../utils/quoteSExprString"
4
+ import { toNumberValue } from "../utils/toNumberValue"
5
+ import { toStringValue } from "../utils/toStringValue"
6
+ import { Layer } from "./Layer"
7
+ import { Uuid } from "./Uuid"
8
+ import { At } from "./At"
9
+ import { Xy } from "./Xy"
10
+
11
+ const SUPPORTED_SINGLE_TOKENS = new Set([
12
+ "at",
13
+ "xy",
14
+ "scale",
15
+ "layer",
16
+ "uuid",
17
+ "data",
18
+ ])
19
+
20
+ const SUPPORTED_MULTI_TOKENS = new Set<string>()
21
+
22
+ export class Image extends SxClass {
23
+ static override token = "image"
24
+ token = "image"
25
+
26
+ private _sxPosition?: At | Xy
27
+ private _sxScale?: ImageScale
28
+ private _sxLayer?: Layer
29
+ private _sxUuid?: Uuid
30
+ private _sxData?: ImageData
31
+
32
+ static override fromSexprPrimitives(
33
+ primitiveSexprs: PrimitiveSExpr[],
34
+ ): Image {
35
+ const image = new Image()
36
+
37
+ const structuredPrimitives: PrimitiveSExpr[] = []
38
+
39
+ for (const primitive of primitiveSexprs) {
40
+ if (!Array.isArray(primitive) || primitive.length === 0) {
41
+ throw new Error(
42
+ `image encountered invalid child expression: ${JSON.stringify(primitive)}`,
43
+ )
44
+ }
45
+
46
+ structuredPrimitives.push(primitive)
47
+ }
48
+
49
+ const { propertyMap, arrayPropertyMap } =
50
+ SxClass.parsePrimitivesToClassProperties(structuredPrimitives, this.token)
51
+
52
+ for (const token of Object.keys(propertyMap)) {
53
+ if (!SUPPORTED_SINGLE_TOKENS.has(token)) {
54
+ throw new Error(
55
+ `image encountered unsupported child token "${token}"`,
56
+ )
57
+ }
58
+ }
59
+
60
+ for (const [token, entries] of Object.entries(arrayPropertyMap)) {
61
+ if (!SUPPORTED_SINGLE_TOKENS.has(token)) {
62
+ throw new Error(
63
+ `image encountered unsupported child token "${token}"`,
64
+ )
65
+ }
66
+ if (!SUPPORTED_MULTI_TOKENS.has(token) && entries.length > 1) {
67
+ throw new Error(
68
+ `image does not support repeated child token "${token}"`,
69
+ )
70
+ }
71
+ }
72
+
73
+ const atInstance = propertyMap.at as At | undefined
74
+ const xyInstance = propertyMap.xy as Xy | undefined
75
+ if (atInstance && xyInstance) {
76
+ throw new Error("image cannot include both at and xy child tokens")
77
+ }
78
+
79
+ image._sxPosition = atInstance ?? xyInstance
80
+ image._sxScale = propertyMap.scale as ImageScale | undefined
81
+ image._sxLayer = propertyMap.layer as Layer | undefined
82
+ image._sxUuid = propertyMap.uuid as Uuid | undefined
83
+ image._sxData = propertyMap.data as ImageData | undefined
84
+
85
+ return image
86
+ }
87
+
88
+ get position(): At | Xy | undefined {
89
+ return this._sxPosition
90
+ }
91
+
92
+ set position(value: At | Xy | undefined) {
93
+ this._sxPosition = value
94
+ }
95
+
96
+ get scale(): ImageScale | undefined {
97
+ return this._sxScale
98
+ }
99
+
100
+ set scale(value: ImageScale | number | undefined) {
101
+ if (value === undefined) {
102
+ this._sxScale = undefined
103
+ return
104
+ }
105
+ this._sxScale = value instanceof ImageScale ? value : new ImageScale(value)
106
+ }
107
+
108
+ get layer(): Layer | undefined {
109
+ return this._sxLayer
110
+ }
111
+
112
+ set layer(value: Layer | string | string[] | undefined) {
113
+ if (value === undefined) {
114
+ this._sxLayer = undefined
115
+ return
116
+ }
117
+ if (value instanceof Layer) {
118
+ this._sxLayer = value
119
+ } else {
120
+ const names = Array.isArray(value) ? value : [value]
121
+ this._sxLayer = new Layer(names)
122
+ }
123
+ }
124
+
125
+ get uuid(): Uuid | undefined {
126
+ return this._sxUuid
127
+ }
128
+
129
+ set uuid(value: Uuid | string | undefined) {
130
+ if (value === undefined) {
131
+ this._sxUuid = undefined
132
+ return
133
+ }
134
+ this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
135
+ }
136
+
137
+ get data(): ImageData | undefined {
138
+ return this._sxData
139
+ }
140
+
141
+ set data(value: ImageData | string | string[] | undefined) {
142
+ if (value === undefined) {
143
+ this._sxData = undefined
144
+ return
145
+ }
146
+ if (value instanceof ImageData) {
147
+ this._sxData = value
148
+ } else if (Array.isArray(value)) {
149
+ this._sxData = ImageData.fromStrings(value)
150
+ } else {
151
+ this._sxData = ImageData.fromStrings([value])
152
+ }
153
+ }
154
+
155
+ override getChildren(): SxClass[] {
156
+ const children: SxClass[] = []
157
+ if (this._sxPosition) children.push(this._sxPosition)
158
+ if (this._sxScale) children.push(this._sxScale)
159
+ if (this._sxLayer) children.push(this._sxLayer)
160
+ if (this._sxUuid) children.push(this._sxUuid)
161
+ if (this._sxData) children.push(this._sxData)
162
+ return children
163
+ }
164
+ }
165
+ SxClass.register(Image)
166
+
167
+ export class ImageScale extends SxClass {
168
+ static override token = "scale"
169
+ static override parentToken = "image"
170
+ token = "scale"
171
+
172
+ value: number
173
+
174
+ constructor(value: number) {
175
+ super()
176
+ this.value = value
177
+ }
178
+
179
+ static override fromSexprPrimitives(
180
+ primitiveSexprs: PrimitiveSExpr[],
181
+ ): ImageScale {
182
+ const [raw] = primitiveSexprs
183
+ const numeric = toNumberValue(raw)
184
+ return new ImageScale(numeric ?? 1)
185
+ }
186
+
187
+ override getChildren(): SxClass[] {
188
+ return []
189
+ }
190
+
191
+ override getString(): string {
192
+ return `(scale ${this.value})`
193
+ }
194
+ }
195
+ SxClass.register(ImageScale)
196
+
197
+ export class ImageData extends SxClass {
198
+ static override token = "data"
199
+ static override parentToken = "image"
200
+ token = "data"
201
+
202
+ private _chunks: string[]
203
+
204
+ constructor(chunks: string[] = []) {
205
+ super()
206
+ this._chunks = chunks
207
+ }
208
+
209
+ static override fromSexprPrimitives(
210
+ primitiveSexprs: PrimitiveSExpr[],
211
+ ): ImageData {
212
+ const chunks = primitiveSexprs.map((primitive) => primitiveToChunk(primitive))
213
+ return new ImageData(chunks)
214
+ }
215
+
216
+ static fromStrings(values: string[]): ImageData {
217
+ return new ImageData(values)
218
+ }
219
+
220
+ get chunks(): string[] {
221
+ return [...this._chunks]
222
+ }
223
+
224
+ set chunks(values: string[]) {
225
+ this._chunks = [...values]
226
+ }
227
+
228
+ get value(): string {
229
+ return this._chunks.join("")
230
+ }
231
+
232
+ set value(data: string) {
233
+ this._chunks = [data]
234
+ }
235
+
236
+ override getChildren(): SxClass[] {
237
+ return []
238
+ }
239
+
240
+ override getString(): string {
241
+ if (this._chunks.length === 0) {
242
+ return "(data)"
243
+ }
244
+ const rendered = this._chunks.map((chunk) => quoteSExprString(chunk)).join(" ")
245
+ return `(data ${rendered})`
246
+ }
247
+ }
248
+ SxClass.register(ImageData)
249
+
250
+ function primitiveToChunk(value: PrimitiveSExpr): string {
251
+ const stringValue = toStringValue(value)
252
+ if (stringValue !== undefined) {
253
+ return stringValue
254
+ }
255
+ return printSExpr(value)
256
+ }
@@ -0,0 +1,8 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import { SxPrimitiveBoolean } from "../base-classes/SxPrimitiveBoolean"
3
+
4
+ export class InBom extends SxPrimitiveBoolean {
5
+ static override token = "in_bom"
6
+ token = "in_bom"
7
+ }
8
+ SxClass.register(InBom)