altium-toolkit 0.1.0

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 (82) hide show
  1. package/AGENTS.md +67 -0
  2. package/COMMERCIAL-LICENSE.md +20 -0
  3. package/CONTRIBUTING.md +19 -0
  4. package/LICENSE +22 -0
  5. package/LICENSES/CC-BY-SA-4.0.txt +170 -0
  6. package/LICENSES/GPL-3.0-or-later.txt +232 -0
  7. package/NOTICE.md +32 -0
  8. package/README.md +116 -0
  9. package/docs/api.md +73 -0
  10. package/docs/model-format.md +36 -0
  11. package/docs/testing.md +25 -0
  12. package/examples/README.md +47 -0
  13. package/examples/arduino-uno/PcbThreeSceneRenderer.mjs +635 -0
  14. package/examples/arduino-uno/SvgViewportController.mjs +306 -0
  15. package/examples/arduino-uno/example.mjs +480 -0
  16. package/examples/arduino-uno/index.html +163 -0
  17. package/examples/arduino-uno/styles.css +552 -0
  18. package/examples/server.mjs +212 -0
  19. package/package.json +53 -0
  20. package/spec/library-scope.md +32 -0
  21. package/src/core/BinaryReader.mjs +127 -0
  22. package/src/core/altium/AltiumLayoutParser.mjs +485 -0
  23. package/src/core/altium/AltiumParser.mjs +1007 -0
  24. package/src/core/altium/AsciiRecordParser.mjs +151 -0
  25. package/src/core/altium/ParserUtils.mjs +173 -0
  26. package/src/core/altium/PcbBinaryPrimitiveParser.mjs +424 -0
  27. package/src/core/altium/PcbEmbeddedModelExtractor.mjs +505 -0
  28. package/src/core/altium/PcbModelParser.mjs +336 -0
  29. package/src/core/altium/PcbOutlineRasterizer.mjs +852 -0
  30. package/src/core/altium/PcbOutlineRecovery.mjs +957 -0
  31. package/src/core/altium/PcbStreamExtractor.mjs +210 -0
  32. package/src/core/altium/PrintableTextDecoder.mjs +156 -0
  33. package/src/core/altium/SchematicAnnotationParser.mjs +220 -0
  34. package/src/core/altium/SchematicBusEntryParser.mjs +48 -0
  35. package/src/core/altium/SchematicDirectiveParser.mjs +47 -0
  36. package/src/core/altium/SchematicImageParser.mjs +173 -0
  37. package/src/core/altium/SchematicJunctionParser.mjs +43 -0
  38. package/src/core/altium/SchematicMultipartOwnerMatcher.mjs +564 -0
  39. package/src/core/altium/SchematicNetlistBuilder.mjs +351 -0
  40. package/src/core/altium/SchematicPinParser.mjs +767 -0
  41. package/src/core/altium/SchematicPrimitiveParser.mjs +716 -0
  42. package/src/core/altium/SchematicSheetParser.mjs +241 -0
  43. package/src/core/altium/SchematicSheetStyleResolver.mjs +46 -0
  44. package/src/core/altium/SchematicStandaloneCalloutNormalizer.mjs +592 -0
  45. package/src/core/altium/SchematicTextParser.mjs +708 -0
  46. package/src/core/altium/SchematicTextPostProcessor.mjs +801 -0
  47. package/src/core/ole/OleCompoundDocument.mjs +439 -0
  48. package/src/core/ole/OleConstants.mjs +64 -0
  49. package/src/core/ole/OleDirectoryEntry.mjs +95 -0
  50. package/src/index.mjs +7 -0
  51. package/src/parser.mjs +21 -0
  52. package/src/renderers.mjs +15 -0
  53. package/src/scene3d.mjs +9 -0
  54. package/src/styles/altium-renderers.css +358 -0
  55. package/src/ui/BomTableRenderer.mjs +46 -0
  56. package/src/ui/PcbArcUtils.mjs +189 -0
  57. package/src/ui/PcbEdgeFacingGlyphNormalizer.mjs +808 -0
  58. package/src/ui/PcbFootprintPrimitiveSelector.mjs +128 -0
  59. package/src/ui/PcbScene3dBuilder.mjs +742 -0
  60. package/src/ui/PcbScene3dModelRegistry.mjs +309 -0
  61. package/src/ui/PcbScene3dPackages.mjs +137 -0
  62. package/src/ui/PcbScene3dScenePreparator.mjs +36 -0
  63. package/src/ui/PcbScene3dSummaryRenderer.mjs +65 -0
  64. package/src/ui/PcbSvgRenderer.mjs +906 -0
  65. package/src/ui/SchematicColorResolver.mjs +132 -0
  66. package/src/ui/SchematicContentLayout.mjs +661 -0
  67. package/src/ui/SchematicDirectiveRenderer.mjs +184 -0
  68. package/src/ui/SchematicImageRenderer.mjs +135 -0
  69. package/src/ui/SchematicJunctionRenderer.mjs +381 -0
  70. package/src/ui/SchematicNoteRenderer.mjs +427 -0
  71. package/src/ui/SchematicOwnerPinLabelLayout.mjs +173 -0
  72. package/src/ui/SchematicPinSvgRenderer.mjs +495 -0
  73. package/src/ui/SchematicPortRenderer.mjs +558 -0
  74. package/src/ui/SchematicPowerPortRenderer.mjs +574 -0
  75. package/src/ui/SchematicRegionRenderer.mjs +94 -0
  76. package/src/ui/SchematicShapeRenderer.mjs +398 -0
  77. package/src/ui/SchematicSheetChromeRenderer.mjs +1025 -0
  78. package/src/ui/SchematicSheetSymbolRenderer.mjs +228 -0
  79. package/src/ui/SchematicSvgRenderer.mjs +756 -0
  80. package/src/ui/SchematicSvgUtils.mjs +182 -0
  81. package/src/ui/SchematicTypography.mjs +204 -0
  82. package/src/workers/altium-parser.worker.mjs +29 -0
@@ -0,0 +1,552 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2026 André Fiedler
3
+ *
4
+ * SPDX-License-Identifier: GPL-3.0-or-later
5
+ */
6
+
7
+ @import url('../../src/styles/altium-renderers.css');
8
+
9
+ :root {
10
+ color-scheme: light;
11
+ --page: #f6f1e8;
12
+ --panel: #fffaf3;
13
+ --panel-strong: #ffffff;
14
+ --ink: #172228;
15
+ --muted: #52636c;
16
+ --border: rgba(57, 73, 82, 0.16);
17
+ --copper: #c35f35;
18
+ --copper-dark: #824127;
19
+ --board: #1f7a68;
20
+ --board-dark: #15594f;
21
+ --blue: #2a6fbb;
22
+ --danger: #b4233d;
23
+ --shadow: 0 18px 40px rgba(45, 55, 62, 0.14);
24
+ font-family:
25
+ Inter,
26
+ ui-sans-serif,
27
+ system-ui,
28
+ -apple-system,
29
+ BlinkMacSystemFont,
30
+ 'Segoe UI',
31
+ sans-serif;
32
+ }
33
+
34
+ * {
35
+ box-sizing: border-box;
36
+ }
37
+
38
+ body {
39
+ min-width: 320px;
40
+ margin: 0;
41
+ color: var(--ink);
42
+ background: var(--page);
43
+ }
44
+
45
+ html.is-svg-panning,
46
+ html.is-svg-panning body {
47
+ overflow: hidden;
48
+ }
49
+
50
+ a {
51
+ color: var(--blue);
52
+ text-decoration-thickness: 0.08em;
53
+ text-underline-offset: 0.18em;
54
+ }
55
+
56
+ .app-shell {
57
+ min-height: 100vh;
58
+ display: grid;
59
+ grid-template-rows: auto 1fr;
60
+ }
61
+
62
+ .app-header {
63
+ min-height: 8.5rem;
64
+ display: flex;
65
+ justify-content: space-between;
66
+ gap: 1.5rem;
67
+ align-items: center;
68
+ padding: 1.5rem clamp(1rem, 4vw, 3rem);
69
+ border-bottom: 1px solid var(--border);
70
+ background: var(--panel);
71
+ }
72
+
73
+ .eyebrow,
74
+ .credit,
75
+ .status,
76
+ .source-facts,
77
+ .metadata-panel p,
78
+ .empty-state p {
79
+ color: var(--muted);
80
+ }
81
+
82
+ .eyebrow {
83
+ margin: 0 0 0.4rem;
84
+ font-size: 0.76rem;
85
+ font-weight: 700;
86
+ text-transform: uppercase;
87
+ }
88
+
89
+ h1 {
90
+ margin: 0;
91
+ font-size: clamp(1.65rem, 3vw, 2.35rem);
92
+ line-height: 1.08;
93
+ }
94
+
95
+ .credit {
96
+ max-width: 54rem;
97
+ margin: 0.55rem 0 0;
98
+ line-height: 1.55;
99
+ }
100
+
101
+ .source-link {
102
+ flex: 0 0 auto;
103
+ display: inline-flex;
104
+ justify-content: center;
105
+ align-items: center;
106
+ min-height: 2.5rem;
107
+ padding: 0.65rem 0.95rem;
108
+ border: 1px solid var(--border);
109
+ border-radius: 6px;
110
+ color: var(--ink);
111
+ background: var(--panel-strong);
112
+ font-weight: 700;
113
+ text-decoration: none;
114
+ }
115
+
116
+ .workspace {
117
+ display: grid;
118
+ grid-template-columns: minmax(18rem, 24rem) minmax(0, 1fr);
119
+ min-height: 0;
120
+ }
121
+
122
+ .control-panel {
123
+ display: grid;
124
+ align-content: start;
125
+ gap: 1.25rem;
126
+ padding: 1.25rem;
127
+ border-right: 1px solid var(--border);
128
+ background: rgba(255, 250, 243, 0.72);
129
+ }
130
+
131
+ .file-picker {
132
+ display: grid;
133
+ gap: 0.7rem;
134
+ font-weight: 700;
135
+ }
136
+
137
+ .file-picker input {
138
+ width: 100%;
139
+ padding: 0.8rem;
140
+ border: 1px dashed rgba(57, 73, 82, 0.28);
141
+ border-radius: 8px;
142
+ background: var(--panel-strong);
143
+ }
144
+
145
+ .status {
146
+ min-height: 3rem;
147
+ margin: 0;
148
+ padding: 0.8rem 0.9rem;
149
+ border-left: 4px solid var(--blue);
150
+ background: rgba(255, 255, 255, 0.62);
151
+ line-height: 1.45;
152
+ }
153
+
154
+ .status[data-tone='busy'] {
155
+ border-left-color: var(--copper);
156
+ }
157
+
158
+ .status[data-tone='error'] {
159
+ border-left-color: var(--danger);
160
+ }
161
+
162
+ .source-facts {
163
+ display: grid;
164
+ gap: 0.85rem;
165
+ margin: 0;
166
+ }
167
+
168
+ .source-facts div {
169
+ display: grid;
170
+ gap: 0.2rem;
171
+ padding-top: 0.85rem;
172
+ border-top: 1px solid var(--border);
173
+ }
174
+
175
+ .source-facts dt {
176
+ color: var(--ink);
177
+ font-weight: 800;
178
+ }
179
+
180
+ .source-facts dd {
181
+ margin: 0;
182
+ line-height: 1.45;
183
+ }
184
+
185
+ .viewer-panel {
186
+ min-width: 0;
187
+ overflow: hidden;
188
+ display: grid;
189
+ grid-template-rows: auto minmax(0, 1fr);
190
+ }
191
+
192
+ .viewer-toolbar {
193
+ width: 100%;
194
+ min-width: 0;
195
+ max-width: 100%;
196
+ padding: 1rem clamp(1rem, 3vw, 1.5rem);
197
+ border-bottom: 1px solid var(--border);
198
+ background: rgba(255, 255, 255, 0.54);
199
+ }
200
+
201
+ .tabs {
202
+ display: flex;
203
+ gap: 0.35rem;
204
+ overflow-x: auto;
205
+ }
206
+
207
+ .tab {
208
+ flex: 0 0 auto;
209
+ min-height: 2.35rem;
210
+ padding: 0.55rem 0.78rem;
211
+ border: 1px solid transparent;
212
+ border-radius: 6px;
213
+ color: var(--muted);
214
+ background: transparent;
215
+ font: inherit;
216
+ font-weight: 750;
217
+ cursor: pointer;
218
+ }
219
+
220
+ .tab:hover,
221
+ .tab:focus-visible {
222
+ color: var(--ink);
223
+ border-color: var(--border);
224
+ background: var(--panel-strong);
225
+ }
226
+
227
+ .tab.is-active {
228
+ color: var(--ink);
229
+ border-color: rgba(31, 122, 104, 0.36);
230
+ background: #e8f1eb;
231
+ }
232
+
233
+ .output {
234
+ width: 100%;
235
+ min-width: 0;
236
+ max-width: 100%;
237
+ overflow: auto;
238
+ padding: clamp(1rem, 3vw, 1.5rem);
239
+ }
240
+
241
+ .schematic-svg,
242
+ .pcb-svg {
243
+ cursor: grab;
244
+ user-select: none;
245
+ touch-action: none;
246
+ }
247
+
248
+ .schematic-svg.is-panning,
249
+ .pcb-svg.is-panning {
250
+ cursor: grabbing;
251
+ }
252
+
253
+ .empty-state,
254
+ .metadata-panel {
255
+ max-width: 56rem;
256
+ padding: 1.25rem;
257
+ border: 1px solid var(--border);
258
+ border-radius: 8px;
259
+ background: var(--panel-strong);
260
+ }
261
+
262
+ .empty-state h2,
263
+ .metadata-panel h2,
264
+ .metadata-panel h3 {
265
+ margin: 0;
266
+ }
267
+
268
+ .empty-state p,
269
+ .metadata-panel p {
270
+ margin: 0.6rem 0 0;
271
+ line-height: 1.55;
272
+ }
273
+
274
+ .empty-state--error {
275
+ border-color: rgba(180, 35, 61, 0.35);
276
+ }
277
+
278
+ .metadata-panel {
279
+ display: grid;
280
+ gap: 1rem;
281
+ }
282
+
283
+ .metadata-panel table {
284
+ width: 100%;
285
+ border-collapse: collapse;
286
+ overflow-wrap: anywhere;
287
+ }
288
+
289
+ .metadata-panel th,
290
+ .metadata-panel td {
291
+ padding: 0.72rem;
292
+ border-bottom: 1px solid var(--border);
293
+ text-align: left;
294
+ vertical-align: top;
295
+ }
296
+
297
+ .metadata-panel th {
298
+ width: 14rem;
299
+ color: var(--copper-dark);
300
+ }
301
+
302
+ .metadata-panel ul {
303
+ margin: 0;
304
+ padding-left: 1.2rem;
305
+ line-height: 1.6;
306
+ }
307
+
308
+ .bom-table {
309
+ width: 100%;
310
+ border-collapse: collapse;
311
+ background: var(--panel-strong);
312
+ }
313
+
314
+ .bom-table th,
315
+ .bom-table td {
316
+ padding: 0.7rem;
317
+ border-bottom: 1px solid var(--border);
318
+ text-align: left;
319
+ }
320
+
321
+ .scene-3d {
322
+ display: grid;
323
+ gap: 1rem;
324
+ width: 100%;
325
+ min-width: 0;
326
+ max-width: 100%;
327
+ min-height: min(42rem, calc(100vh - 13rem));
328
+ }
329
+
330
+ .scene-3d__header {
331
+ display: flex;
332
+ justify-content: space-between;
333
+ gap: 1rem;
334
+ align-items: end;
335
+ min-width: 0;
336
+ }
337
+
338
+ .scene-3d__header h2,
339
+ .scene-3d__header p,
340
+ .scene-3d__diagnostics {
341
+ margin: 0;
342
+ }
343
+
344
+ .scene-3d__header p,
345
+ .scene-3d__diagnostics {
346
+ color: var(--muted);
347
+ line-height: 1.45;
348
+ }
349
+
350
+ .scene-3d__toolbar {
351
+ display: flex;
352
+ flex-wrap: wrap;
353
+ gap: 0.5rem;
354
+ }
355
+
356
+ .scene-3d__preset {
357
+ min-height: 2.25rem;
358
+ padding: 0.5rem 0.76rem;
359
+ border: 1px solid var(--border);
360
+ border-radius: 6px;
361
+ color: var(--ink);
362
+ background: var(--panel-strong);
363
+ font: inherit;
364
+ font-weight: 750;
365
+ cursor: pointer;
366
+ }
367
+
368
+ .scene-3d__preset:hover,
369
+ .scene-3d__preset:focus-visible {
370
+ border-color: rgba(31, 122, 104, 0.36);
371
+ background: #eef6f2;
372
+ }
373
+
374
+ .scene-3d__preset.is-active,
375
+ .scene-3d__preset[aria-pressed='true'] {
376
+ border-color: rgba(31, 122, 104, 0.46);
377
+ color: #0f3f37;
378
+ background: #dcece6;
379
+ }
380
+
381
+ .scene-3d__stage {
382
+ width: 100%;
383
+ min-width: 0;
384
+ max-width: 100%;
385
+ min-height: min(38rem, calc(100vh - 18rem));
386
+ display: grid;
387
+ grid-template-columns: minmax(0, 1fr) 13rem;
388
+ overflow: hidden;
389
+ border: 1px solid var(--border);
390
+ border-radius: 8px;
391
+ background: var(--panel-strong);
392
+ box-shadow: var(--shadow);
393
+ }
394
+
395
+ .scene-3d__viewport {
396
+ position: relative;
397
+ min-width: 0;
398
+ max-width: 100%;
399
+ min-height: 28rem;
400
+ background:
401
+ radial-gradient(
402
+ circle at 50% 0%,
403
+ rgba(255, 255, 255, 0.82),
404
+ rgba(255, 255, 255, 0) 46%
405
+ ),
406
+ linear-gradient(180deg, #f8f3ea 0%, #e7f0ed 100%);
407
+ }
408
+
409
+ .scene-3d__canvas-mount {
410
+ position: absolute;
411
+ inset: 0;
412
+ min-width: 0;
413
+ }
414
+
415
+ .scene-3d__canvas {
416
+ display: block;
417
+ width: 100%;
418
+ max-width: 100%;
419
+ height: 100%;
420
+ cursor: grab;
421
+ touch-action: none;
422
+ }
423
+
424
+ .scene-3d__canvas:active {
425
+ cursor: grabbing;
426
+ }
427
+
428
+ .scene-3d__loading {
429
+ position: absolute;
430
+ inset: 0;
431
+ display: grid;
432
+ place-items: center;
433
+ padding: 1rem;
434
+ color: var(--muted);
435
+ background: rgba(255, 250, 243, 0.86);
436
+ }
437
+
438
+ .scene-3d__loading[hidden] {
439
+ display: none;
440
+ }
441
+
442
+ .scene-3d__loading p {
443
+ margin: 0;
444
+ }
445
+
446
+ .scene-3d__controls {
447
+ display: grid;
448
+ align-content: start;
449
+ gap: 0.75rem;
450
+ padding: 1rem;
451
+ border-left: 1px solid var(--border);
452
+ background: rgba(255, 250, 243, 0.76);
453
+ }
454
+
455
+ .scene-3d__toggle {
456
+ display: flex;
457
+ align-items: center;
458
+ gap: 0.55rem;
459
+ line-height: 1.35;
460
+ font-weight: 700;
461
+ }
462
+
463
+ .scene-3d__toggle input {
464
+ width: 1rem;
465
+ height: 1rem;
466
+ accent-color: var(--board);
467
+ }
468
+
469
+ .scene-3d__stats {
470
+ display: grid;
471
+ grid-template-columns: repeat(2, minmax(5.5rem, 1fr));
472
+ gap: 0.65rem;
473
+ min-width: 0;
474
+ margin: 0;
475
+ }
476
+
477
+ .scene-3d__stats div {
478
+ padding: 0.65rem 0.75rem;
479
+ border: 1px solid var(--border);
480
+ border-radius: 8px;
481
+ background: rgba(255, 255, 255, 0.72);
482
+ }
483
+
484
+ .scene-3d__stats dt {
485
+ color: var(--muted);
486
+ font-size: 0.72rem;
487
+ font-weight: 800;
488
+ text-transform: uppercase;
489
+ }
490
+
491
+ .scene-3d__stats dd {
492
+ margin: 0.22rem 0 0;
493
+ font-weight: 800;
494
+ }
495
+
496
+ @media (max-width: 860px) {
497
+ .app-header {
498
+ align-items: flex-start;
499
+ flex-direction: column;
500
+ }
501
+
502
+ .workspace {
503
+ grid-template-columns: 1fr;
504
+ }
505
+
506
+ .control-panel {
507
+ border-right: 0;
508
+ border-bottom: 1px solid var(--border);
509
+ }
510
+
511
+ .scene-3d__header {
512
+ align-items: start;
513
+ flex-direction: column;
514
+ }
515
+
516
+ .scene-3d__stage {
517
+ grid-template-columns: 1fr;
518
+ }
519
+
520
+ .scene-3d__controls {
521
+ border-top: 1px solid var(--border);
522
+ border-left: 0;
523
+ }
524
+ }
525
+
526
+ @media (max-width: 520px) {
527
+ .app-header,
528
+ .control-panel,
529
+ .viewer-toolbar,
530
+ .output {
531
+ padding-inline: 0.85rem;
532
+ }
533
+
534
+ .source-link {
535
+ width: 100%;
536
+ }
537
+
538
+ .metadata-panel th,
539
+ .metadata-panel td {
540
+ display: block;
541
+ width: 100%;
542
+ }
543
+
544
+ .scene-3d__viewport {
545
+ min-height: 20rem;
546
+ }
547
+
548
+ .scene-3d__stats {
549
+ width: 100%;
550
+ grid-template-columns: 1fr;
551
+ }
552
+ }