kicadts 0.0.1 → 0.0.2

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 (78) hide show
  1. package/.github/workflows/bun-formatcheck.yml +26 -0
  2. package/.github/workflows/bun-pver-release.yml +70 -0
  3. package/.github/workflows/bun-test.yml +32 -0
  4. package/.github/workflows/bun-typecheck.yml +26 -0
  5. package/.vscode/settings.json +1 -1
  6. package/AGENTS.md +1 -0
  7. package/LICENSE +21 -0
  8. package/README.md +101 -91
  9. package/TODO.md +46 -0
  10. package/bunfig.toml +2 -2
  11. package/lib/sexpr/classes/At.ts +15 -0
  12. package/lib/sexpr/classes/Bus.ts +23 -3
  13. package/lib/sexpr/classes/BusEntry.ts +30 -3
  14. package/lib/sexpr/classes/EmbeddedFonts.ts +1 -3
  15. package/lib/sexpr/classes/Footprint.ts +157 -27
  16. package/lib/sexpr/classes/FootprintAttr.ts +3 -1
  17. package/lib/sexpr/classes/FootprintModel.ts +1 -4
  18. package/lib/sexpr/classes/FootprintNetTiePadGroups.ts +3 -1
  19. package/lib/sexpr/classes/FootprintPad.ts +206 -54
  20. package/lib/sexpr/classes/FpArc.ts +23 -0
  21. package/lib/sexpr/classes/FpCircle.ts +24 -3
  22. package/lib/sexpr/classes/FpLine.ts +31 -3
  23. package/lib/sexpr/classes/FpPoly.ts +24 -4
  24. package/lib/sexpr/classes/FpRect.ts +24 -3
  25. package/lib/sexpr/classes/FpText.ts +43 -9
  26. package/lib/sexpr/classes/FpTextBox.ts +43 -5
  27. package/lib/sexpr/classes/GrLine.ts +20 -1
  28. package/lib/sexpr/classes/GrText.ts +38 -12
  29. package/lib/sexpr/classes/Image.ts +38 -11
  30. package/lib/sexpr/classes/Junction.ts +36 -4
  31. package/lib/sexpr/classes/KicadPcb.ts +49 -1
  32. package/lib/sexpr/classes/KicadSch.ts +119 -25
  33. package/lib/sexpr/classes/Label.ts +45 -5
  34. package/lib/sexpr/classes/NoConnect.ts +20 -3
  35. package/lib/sexpr/classes/PadLayers.ts +13 -1
  36. package/lib/sexpr/classes/PadOptions.ts +4 -5
  37. package/lib/sexpr/classes/PadPrimitiveGrArc.ts +22 -4
  38. package/lib/sexpr/classes/PadPrimitiveGrCircle.ts +23 -4
  39. package/lib/sexpr/classes/PadPrimitives.ts +3 -1
  40. package/lib/sexpr/classes/PadSize.ts +15 -0
  41. package/lib/sexpr/classes/PadTeardrops.ts +3 -1
  42. package/lib/sexpr/classes/PcbGeneral.ts +14 -7
  43. package/lib/sexpr/classes/PcbLayerDefinition.ts +5 -1
  44. package/lib/sexpr/classes/Property.ts +64 -9
  45. package/lib/sexpr/classes/Pts.ts +7 -5
  46. package/lib/sexpr/classes/SchematicText.ts +39 -9
  47. package/lib/sexpr/classes/Segment.ts +21 -0
  48. package/lib/sexpr/classes/SegmentNet.ts +3 -1
  49. package/lib/sexpr/classes/Setup/PcbPlotParams.ts +10 -50
  50. package/lib/sexpr/classes/Setup/Setup.ts +12 -11
  51. package/lib/sexpr/classes/Setup/Stackup.ts +14 -19
  52. package/lib/sexpr/classes/Setup/StackupLayerProperties.ts +3 -1
  53. package/lib/sexpr/classes/Setup/StackupProperties.ts +0 -1
  54. package/lib/sexpr/classes/Setup/base.ts +1 -3
  55. package/lib/sexpr/classes/Setup/setupMultiValueProperties.ts +0 -1
  56. package/lib/sexpr/classes/Sheet.ts +85 -3
  57. package/lib/sexpr/classes/SheetPin.ts +4 -1
  58. package/lib/sexpr/classes/Symbol.ts +176 -51
  59. package/lib/sexpr/classes/TextEffects.ts +25 -8
  60. package/lib/sexpr/classes/TitleBlock.ts +21 -4
  61. package/lib/sexpr/classes/Via.ts +38 -3
  62. package/lib/sexpr/classes/ViaNet.ts +2 -1
  63. package/lib/sexpr/classes/Wire.ts +23 -3
  64. package/lib/sexpr/classes/Xy.ts +1 -3
  65. package/lib/sexpr/classes/Zone.ts +1 -3
  66. package/lib/sexpr/parseToPrimitiveSExpr.ts +6 -1
  67. package/lib/sexpr/utils/strokeFromArgs.ts +5 -6
  68. package/lib/sexpr/utils/toStringValue.ts +2 -1
  69. package/package.json +2 -1
  70. package/scripts/download-references.ts +24 -22
  71. package/tests/fixtures/expectEqualPrimitiveSExpr.ts +6 -7
  72. package/tests/fixtures/png-matcher.ts +109 -0
  73. package/tests/fixtures/preload.ts +1 -0
  74. package/tests/sexpr/classes/FootprintPad.test.ts +8 -1
  75. package/tests/sexpr/classes/Image.test.ts +9 -1
  76. package/tests/sexpr/classes/KicadSch.test.ts +1 -3
  77. package/tests/sexpr/classes/Setup.test.ts +0 -1
  78. package/bun.lock +0 -48
@@ -0,0 +1,26 @@
1
+ # Created using @tscircuit/plop (npm install -g @tscircuit/plop)
2
+ name: Format Check
3
+
4
+ on:
5
+ push:
6
+ branches: [main]
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ jobs:
11
+ format-check:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Setup bun
18
+ uses: oven-sh/setup-bun@v2
19
+ with:
20
+ bun-version: latest
21
+
22
+ - name: Install dependencies
23
+ run: bun install
24
+
25
+ - name: Run format check
26
+ run: bun run format:check
@@ -0,0 +1,70 @@
1
+ # Created using @tscircuit/plop (npm install -g @tscircuit/plop)
2
+ name: Publish to npm
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+
9
+ env:
10
+ UPSTREAM_REPOS: "" # comma-separated list, e.g. "eval,tscircuit,docs"
11
+ UPSTREAM_PACKAGES_TO_UPDATE: "" # comma-separated list, e.g. "@tscircuit/core,@tscircuit/protos"
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ token: ${{ secrets.TSCIRCUIT_BOT_GITHUB_TOKEN }}
20
+ - name: Setup bun
21
+ uses: oven-sh/setup-bun@v2
22
+ with:
23
+ bun-version: latest
24
+ - uses: actions/setup-node@v3
25
+ with:
26
+ node-version: 20
27
+ registry-url: https://registry.npmjs.org/
28
+ - run: npm install -g pver
29
+ - run: bun install --frozen-lockfile
30
+ - run: bun run build
31
+ - run: pver release
32
+ env:
33
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
34
+ GITHUB_TOKEN: ${{ secrets.TSCIRCUIT_BOT_GITHUB_TOKEN }}
35
+
36
+ # - name: Create Pull Request
37
+ # id: create-pr
38
+ # uses: peter-evans/create-pull-request@v5
39
+ # with:
40
+ # commit-message: "chore: bump version"
41
+ # title: "chore: bump version"
42
+ # body: "Automated package update"
43
+ # branch: bump-version-${{ github.run_number }}
44
+ # base: main
45
+ # token: ${{ secrets.TSCIRCUIT_BOT_GITHUB_TOKEN }}
46
+ # committer: tscircuitbot <githubbot@tscircuit.com>
47
+ # author: tscircuitbot <githubbot@tscircuit.com>
48
+
49
+ # - name: Enable auto-merge
50
+ # if: steps.create-pr.outputs.pull-request-number != ''
51
+ # run: |
52
+ # gh pr merge ${{ steps.create-pr.outputs.pull-request-number }} --rebase --delete-branch
53
+ # env:
54
+ # GH_TOKEN: ${{ secrets.TSCIRCUIT_BOT_GITHUB_TOKEN }}
55
+
56
+ # - name: Trigger upstream repo updates
57
+ # if: env.UPSTREAM_REPOS && env.UPSTREAM_PACKAGES_TO_UPDATE
58
+ # run: |
59
+ # IFS=',' read -ra REPOS <<< "${{ env.UPSTREAM_REPOS }}"
60
+ # for repo in "${REPOS[@]}"; do
61
+ # if [[ -n "$repo" ]]; then
62
+ # echo "Triggering update for repo: $repo"
63
+ # curl -X POST \
64
+ # -H "Accept: application/vnd.github.v3+json" \
65
+ # -H "Authorization: token ${{ secrets.TSCIRCUIT_BOT_GITHUB_TOKEN }}" \
66
+ # -H "Content-Type: application/json" \
67
+ # "https://api.github.com/repos/tscircuit/$repo/actions/workflows/update-package.yml/dispatches" \
68
+ # -d "{\"ref\":\"main\",\"inputs\":{\"package_names\":\"${{ env.UPSTREAM_PACKAGES_TO_UPDATE }}\"}}"
69
+ # fi
70
+ # done
@@ -0,0 +1,32 @@
1
+ # Created using @tscircuit/plop (npm install -g @tscircuit/plop)
2
+ name: Bun Test
3
+
4
+ on:
5
+ pull_request:
6
+ push:
7
+ branches:
8
+ - main
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ timeout-minutes: 5
14
+
15
+ # Skip test for PRs that not chore: bump version
16
+ if: "${{ github.event_name != 'pull_request' || github.event.pull_request.title != 'chore: bump version' }}"
17
+
18
+ steps:
19
+ - name: Checkout code
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Setup bun
23
+ uses: oven-sh/setup-bun@v2
24
+ with:
25
+ bun-version: latest
26
+
27
+ - name: Install dependencies
28
+ run: bun install
29
+ - run: bun run download-demos
30
+
31
+ - name: Run tests
32
+ run: bun test
@@ -0,0 +1,26 @@
1
+ # Created using @tscircuit/plop (npm install -g @tscircuit/plop)
2
+ name: Type Check
3
+
4
+ on:
5
+ push:
6
+ branches: [main]
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ jobs:
11
+ type-check:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Setup bun
18
+ uses: oven-sh/setup-bun@v2
19
+ with:
20
+ bun-version: latest
21
+
22
+ - name: Install dependencies
23
+ run: bun i
24
+
25
+ - name: Run type check
26
+ run: bunx tsc --noEmit
@@ -13,4 +13,4 @@
13
13
  "titleBar.inactiveForeground": "#15202b99"
14
14
  },
15
15
  "peacock.color": "#07d5c7"
16
- }
16
+ }
package/AGENTS.md CHANGED
@@ -28,3 +28,4 @@ most extensive one is `references/SEXPR_MAIN.adoc` (use `bun run scripts/downloa
28
28
  - NEW PATTERN: Never track the order of children unless absolutely necessary, use the `_sx*` properties and `getChildren` to return the children in a predefined order
29
29
  - NEW PATTERN: Never track unknown children, throw an error/allow an error to be thrown if you encounter a child PrimitiveSExpr that can't be parsed. You can introduce new `_sx*` properties and a new class to prevent the error
30
30
  - NEW PATTERN: One class per file
31
+ - NEW PATTERN: Ergonomic constructors for classes
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 tscircuit Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -28,33 +28,33 @@ import {
28
28
  Xy,
29
29
  } from "kicadts"
30
30
 
31
- const schematic = new KicadSch()
32
- schematic.version = 20240101
33
- schematic.generator = "kicadts-demo"
34
-
35
- const title = new TitleBlock()
36
- title.title = "Demo Schematic"
37
- title.company = "Example Labs"
38
- schematic.titleBlock = title
39
-
40
- const paper = new Paper()
41
- paper.size = "A4"
42
- schematic.paper = paper
43
- schematic.properties = [new Property("Sheetfile", "demo.kicad_sch")]
44
-
45
- const sheet = new Sheet()
46
- sheet.position = new At([0, 0, 0])
47
- sheet.size = { width: 100, height: 80 }
48
- schematic.sheets = [sheet]
49
-
50
- const symbol = new SchematicSymbol()
51
- symbol.libraryId = "Device:R"
52
- symbol.at = new At([25.4, 12.7])
53
- schematic.symbols = [symbol]
54
-
55
- const wire = new Wire()
56
- wire.points = new Pts([new Xy(0, 0), new Xy(25.4, 12.7)])
57
- schematic.wires = [wire]
31
+ const schematic = new KicadSch({
32
+ version: 20240101,
33
+ generator: "kicadts-demo",
34
+ titleBlock: new TitleBlock({
35
+ title: "Demo Schematic",
36
+ company: "Example Labs",
37
+ }),
38
+ paper: new Paper({ size: "A4" }),
39
+ properties: [new Property({ key: "Sheetfile", value: "demo.kicad_sch" })],
40
+ sheets: [
41
+ new Sheet({
42
+ position: [0, 0, 0], // Can use array instead of new At([0, 0, 0])
43
+ size: { width: 100, height: 80 },
44
+ }),
45
+ ],
46
+ symbols: [
47
+ new SchematicSymbol({
48
+ libraryId: "Device:R",
49
+ at: { x: 25.4, y: 12.7 }, // Can use object instead of new At([25.4, 12.7])
50
+ }),
51
+ ],
52
+ wires: [
53
+ new Wire({
54
+ points: new Pts([new Xy(0, 0), new Xy(25.4, 12.7)]),
55
+ }),
56
+ ],
57
+ })
58
58
 
59
59
  await fs.writeFile("demo.kicad_sch", schematic.getString())
60
60
  ```
@@ -78,51 +78,57 @@ import {
78
78
  TextEffects,
79
79
  } from "kicadts"
80
80
 
81
- const pcb = new KicadPcb()
82
- pcb.version = 20240101
83
- pcb.generator = "kicadts-demo"
84
-
85
81
  const netGnd = new PcbNet(1, "GND")
86
82
  const netSignal = new PcbNet(2, "Net-(R1-Pad2)")
87
- pcb.nets = [netGnd, netSignal]
88
-
89
- const footprint = new Footprint()
90
- footprint.libraryLink = "Resistor_SMD:R_0603"
91
- footprint.layer = "F.Cu"
92
- footprint.position = new At([10, 5, 90])
93
-
94
- const makeText = (type: string, text: string, x: number, y: number, layer: string) => {
95
- const fpText = new FpText()
96
- fpText.type = type
97
- fpText.text = text
98
- fpText.position = new At([x, y])
99
- fpText.layer = layer
100
- const effects = new TextEffects()
101
- effects.font.size = { height: 1, width: 1 }
102
- effects.font.thickness = 0.15
103
- fpText.effects = effects
104
- return fpText
105
- }
106
-
107
- footprint.fpTexts = [
108
- makeText("reference", "R1", 0, -1.5, "F.SilkS"),
109
- makeText("value", "10k", 0, 1.5, "F.Fab"),
110
- ]
111
-
112
- const pad = (number: string, x: number, net: PcbNet) => {
113
- const fpPad = new FootprintPad(number, "smd", "roundrect")
114
- fpPad.at = new At([x, 0])
115
- fpPad.size = new PadSize(1.05, 0.95)
116
- fpPad.layers = new PadLayers(["F.Cu", "F.Paste", "F.Mask"])
117
- fpPad.roundrectRatio = 0.25
118
- fpPad.net = new PadNet(net.id, net.name)
119
- fpPad.pinfunction = number
120
- fpPad.pintype = "passive"
121
- return fpPad
122
- }
123
83
 
124
- footprint.fpPads = [pad("1", -0.8, netGnd), pad("2", 0.8, netSignal)]
125
- pcb.footprints = [footprint]
84
+ const makeText = (
85
+ type: string,
86
+ text: string,
87
+ x: number,
88
+ y: number,
89
+ layer: string
90
+ ) =>
91
+ new FpText({
92
+ type,
93
+ text,
94
+ position: { x, y },
95
+ layer,
96
+ effects: new TextEffects({
97
+ font: { size: { height: 1, width: 1 }, thickness: 0.15 },
98
+ }),
99
+ })
100
+
101
+ const pad = (number: string, x: number, net: PcbNet) =>
102
+ new FootprintPad({
103
+ number,
104
+ padType: "smd",
105
+ shape: "roundrect",
106
+ at: { x, y: 0 },
107
+ size: { width: 1.05, height: 0.95 },
108
+ layers: ["F.Cu", "F.Paste", "F.Mask"],
109
+ roundrectRatio: 0.25,
110
+ net: new PadNet(net.id, net.name),
111
+ pinfunction: number,
112
+ pintype: "passive",
113
+ })
114
+
115
+ const pcb = new KicadPcb({
116
+ version: 20240101,
117
+ generator: "kicadts-demo",
118
+ nets: [netGnd, netSignal],
119
+ footprints: [
120
+ new Footprint({
121
+ libraryLink: "Resistor_SMD:R_0603",
122
+ layer: "F.Cu",
123
+ position: { x: 10, y: 5, angle: 90 },
124
+ fpTexts: [
125
+ makeText("reference", "R1", 0, -1.5, "F.SilkS"),
126
+ makeText("value", "10k", 0, 1.5, "F.Fab"),
127
+ ],
128
+ fpPads: [pad("1", -0.8, netGnd), pad("2", 0.8, netSignal)],
129
+ }),
130
+ ],
131
+ })
126
132
 
127
133
  await fs.writeFile("demo.kicad_pcb", pcb.getString())
128
134
  ```
@@ -143,28 +149,32 @@ import {
143
149
  TextEffects,
144
150
  } from "kicadts"
145
151
 
146
- const footprint = new Footprint()
147
- footprint.libraryLink = "Demo:TestPad"
148
- footprint.layer = "F.Cu"
149
- footprint.position = new At([0, 0])
150
-
151
- const label = new FpText()
152
- label.type = "reference"
153
- label.text = "REF**"
154
- label.position = new At([0, -1.5])
155
- label.layer = "F.SilkS"
156
- const effects = new TextEffects()
157
- effects.font.size = { height: 1, width: 1 }
158
- effects.font.thickness = 0.15
159
- label.effects = effects
160
-
161
- footprint.fpTexts = [label]
162
-
163
- const pad = new FootprintPad("1", "smd", "rect")
164
- pad.at = new At([0, 0])
165
- pad.size = new PadSize(1.5, 1.5)
166
- pad.layers = new PadLayers(["F.Cu", "F.Paste", "F.Mask"])
167
- footprint.fpPads = [pad]
152
+ const footprint = new Footprint({
153
+ libraryLink: "Demo:TestPad",
154
+ layer: "F.Cu",
155
+ position: [0, 0], // Array format
156
+ fpTexts: [
157
+ new FpText({
158
+ type: "reference",
159
+ text: "REF**",
160
+ position: { x: 0, y: -1.5 },
161
+ layer: "F.SilkS",
162
+ effects: new TextEffects({
163
+ font: { size: { height: 1, width: 1 }, thickness: 0.15 },
164
+ }),
165
+ }),
166
+ ],
167
+ fpPads: [
168
+ new FootprintPad({
169
+ number: "1",
170
+ padType: "smd",
171
+ shape: "rect",
172
+ at: [0, 0], // You can also use { x, y } form
173
+ size: { width: 1.5, height: 1.5 },
174
+ layers: ["F.Cu", "F.Paste", "F.Mask"],
175
+ }),
176
+ ],
177
+ })
168
178
 
169
179
  await fs.writeFile("Demo_TestPad.kicad_mod", footprint.getString())
170
180
  ```
package/TODO.md ADDED
@@ -0,0 +1,46 @@
1
+ # TODO: Ergonomic Constructors
2
+
3
+ The following classes need ergonomic constructors created (similar to the pattern in `KicadSch.ts`):
4
+
5
+ ## Main Container Classes
6
+
7
+ - [x] **KicadSch** - `lib/sexpr/classes/KicadSch.ts`
8
+ - [ ] **Bus** - `lib/sexpr/classes/Bus.ts`
9
+ - [ ] **BusEntry** - `lib/sexpr/classes/BusEntry.ts`
10
+ - [ ] **Footprint** - `lib/sexpr/classes/Footprint.ts`
11
+ - [ ] **FootprintPad** - `lib/sexpr/classes/FootprintPad.ts`
12
+ - [ ] **FpArc** - `lib/sexpr/classes/FpArc.ts`
13
+ - [ ] **FpCircle** - `lib/sexpr/classes/FpCircle.ts`
14
+ - [ ] **FpLine** - `lib/sexpr/classes/FpLine.ts`
15
+ - [ ] **FpPoly** - `lib/sexpr/classes/FpPoly.ts`
16
+ - [ ] **FpRect** - `lib/sexpr/classes/FpRect.ts`
17
+ - [ ] **FpText** - `lib/sexpr/classes/FpText.ts`
18
+ - [ ] **FpTextBox** - `lib/sexpr/classes/FpTextBox.ts`
19
+ - [ ] **GrLine** - `lib/sexpr/classes/GrLine.ts`
20
+ - [ ] **GrText** - `lib/sexpr/classes/GrText.ts`
21
+ - [ ] **Image** - `lib/sexpr/classes/Image.ts`
22
+ - [ ] **Junction** - `lib/sexpr/classes/Junction.ts`
23
+ - [ ] **KicadPcb** - `lib/sexpr/classes/KicadPcb.ts`
24
+ - [ ] **Label** - `lib/sexpr/classes/Label.ts`
25
+ - [ ] **NoConnect** - `lib/sexpr/classes/NoConnect.ts`
26
+ - [ ] **PadPrimitiveGrArc** - `lib/sexpr/classes/PadPrimitiveGrArc.ts`
27
+ - [ ] **PadPrimitiveGrCircle** - `lib/sexpr/classes/PadPrimitiveGrCircle.ts`
28
+ - [ ] **Property** - `lib/sexpr/classes/Property.ts`
29
+ - [ ] **SchematicText** - `lib/sexpr/classes/SchematicText.ts`
30
+ - [ ] **Segment** - `lib/sexpr/classes/Segment.ts`
31
+ - [ ] **Sheet** - `lib/sexpr/classes/Sheet.ts`
32
+ - [ ] **Symbol** (SchematicSymbol) - `lib/sexpr/classes/Symbol.ts`
33
+ - [ ] **TextEffects** - `lib/sexpr/classes/TextEffects.ts`
34
+ - [ ] **TitleBlock** - `lib/sexpr/classes/TitleBlock.ts`
35
+ - [ ] **Via** - `lib/sexpr/classes/Via.ts`
36
+ - [ ] **Wire** - `lib/sexpr/classes/Wire.ts`
37
+
38
+ ## Pattern Requirements
39
+
40
+ Each class needs:
41
+ 1. A TypeScript interface `{ClassName}ConstructorParams` with optional properties
42
+ 2. Properties should accept either primitive values (strings, numbers) OR SxClass wrapper objects
43
+ 3. Constructor that accepts the params interface and handles both cases
44
+ 4. Getter/setter pairs working with primitive values while storing SxClass objects internally
45
+
46
+ See `lib/sexpr/classes/KicadSch.ts` for reference implementation.
package/bunfig.toml CHANGED
@@ -1,5 +1,5 @@
1
- # [test]
2
- # preload = ["./tests/fixtures/preload.ts"]
1
+ [test]
2
+ preload = ["./tests/fixtures/preload.ts"]
3
3
 
4
4
  [install.lockfile]
5
5
  save = false
@@ -1,6 +1,11 @@
1
1
  import { SxClass } from "../base-classes/SxClass"
2
2
  import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
3
 
4
+ export type AtInput =
5
+ | At
6
+ | [x: number, y: number, angle?: number]
7
+ | { x: number; y: number; angle?: number }
8
+
4
9
  export class At extends SxClass {
5
10
  static override token = "at"
6
11
  token = "at"
@@ -22,6 +27,16 @@ export class At extends SxClass {
22
27
  y: number
23
28
  angle?: number
24
29
 
30
+ static from(input: AtInput, opts: { isTextSymbol?: boolean } = {}): At {
31
+ if (input instanceof At) {
32
+ return input
33
+ }
34
+ if (Array.isArray(input)) {
35
+ return new At(input, opts)
36
+ }
37
+ return new At([input.x, input.y, input.angle], opts)
38
+ }
39
+
25
40
  static override fromSexprPrimitives(primitiveSexprs: PrimitiveSExpr[]): At {
26
41
  const [x, y, angle] = primitiveSexprs
27
42
  return new At([x as number, y as number, angle as number])
@@ -6,6 +6,12 @@ import { Uuid } from "./Uuid"
6
6
 
7
7
  const SUPPORTED_TOKENS = new Set(["pts", "stroke", "uuid"])
8
8
 
9
+ export interface BusConstructorParams {
10
+ points?: Pts
11
+ stroke?: Stroke
12
+ uuid?: string | Uuid
13
+ }
14
+
9
15
  export class Bus extends SxClass {
10
16
  static override token = "bus"
11
17
  static override parentToken = "kicad_sch"
@@ -15,9 +21,23 @@ export class Bus extends SxClass {
15
21
  private _sxStroke?: Stroke
16
22
  private _sxUuid?: Uuid
17
23
 
18
- static override fromSexprPrimitives(
19
- primitiveSexprs: PrimitiveSExpr[],
20
- ): Bus {
24
+ constructor(params: BusConstructorParams = {}) {
25
+ super()
26
+
27
+ if (params.points !== undefined) {
28
+ this.points = params.points
29
+ }
30
+
31
+ if (params.stroke !== undefined) {
32
+ this.stroke = params.stroke
33
+ }
34
+
35
+ if (params.uuid !== undefined) {
36
+ this.uuid = params.uuid
37
+ }
38
+ }
39
+
40
+ static override fromSexprPrimitives(primitiveSexprs: PrimitiveSExpr[]): Bus {
21
41
  const bus = new Bus()
22
42
 
23
43
  const { propertyMap, arrayPropertyMap } =
@@ -1,11 +1,18 @@
1
1
  import { SxClass } from "../base-classes/SxClass"
2
2
  import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
- import { At } from "./At"
3
+ import { At, type AtInput } from "./At"
4
4
  import { Stroke } from "./Stroke"
5
5
  import { Uuid } from "./Uuid"
6
6
 
7
7
  const SUPPORTED_TOKENS = new Set(["at", "size", "stroke", "uuid"])
8
8
 
9
+ export interface BusEntryConstructorParams {
10
+ at?: AtInput
11
+ size?: BusEntrySize | { x: number; y: number }
12
+ stroke?: Stroke
13
+ uuid?: string | Uuid
14
+ }
15
+
9
16
  export class BusEntry extends SxClass {
10
17
  static override token = "bus_entry"
11
18
  static override parentToken = "kicad_sch"
@@ -16,6 +23,26 @@ export class BusEntry extends SxClass {
16
23
  private _sxStroke?: Stroke
17
24
  private _sxUuid?: Uuid
18
25
 
26
+ constructor(params: BusEntryConstructorParams = {}) {
27
+ super()
28
+
29
+ if (params.at !== undefined) {
30
+ this.at = params.at
31
+ }
32
+
33
+ if (params.size !== undefined) {
34
+ this.size = params.size
35
+ }
36
+
37
+ if (params.stroke !== undefined) {
38
+ this.stroke = params.stroke
39
+ }
40
+
41
+ if (params.uuid !== undefined) {
42
+ this.uuid = params.uuid
43
+ }
44
+ }
45
+
19
46
  static override fromSexprPrimitives(
20
47
  primitiveSexprs: PrimitiveSExpr[],
21
48
  ): BusEntry {
@@ -52,8 +79,8 @@ export class BusEntry extends SxClass {
52
79
  return this._sxAt
53
80
  }
54
81
 
55
- set at(value: At | undefined) {
56
- this._sxAt = value
82
+ set at(value: AtInput | undefined) {
83
+ this._sxAt = value !== undefined ? At.from(value) : undefined
57
84
  }
58
85
 
59
86
  get size(): { x: number; y: number } | undefined {
@@ -41,9 +41,7 @@ export class EmbeddedFonts extends SxClass {
41
41
  if (normalized === "no") {
42
42
  return new EmbeddedFonts(false)
43
43
  }
44
- throw new Error(
45
- `embedded_fonts expects "yes" or "no", received "${raw}"`,
46
- )
44
+ throw new Error(`embedded_fonts expects "yes" or "no", received "${raw}"`)
47
45
  }
48
46
 
49
47
  throw new Error(