altium-toolkit 1.0.8 → 1.0.10

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 (102) hide show
  1. package/README.md +18 -6
  2. package/docs/api.md +78 -16
  3. package/docs/model-format.md +229 -8
  4. package/docs/schemas/altium_toolkit/ci_artifact_bundle_a1.schema.json +76 -0
  5. package/docs/schemas/altium_toolkit/draftsman_digest_a1.schema.json +35 -0
  6. package/docs/schemas/altium_toolkit/netlist_a1.schema.json +53 -0
  7. package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +1826 -110
  8. package/docs/schemas/altium_toolkit/parser_compatibility_fuzz_a1.schema.json +25 -0
  9. package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +86 -0
  10. package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +63 -0
  11. package/docs/schemas/altium_toolkit/project_document_graph_a1.schema.json +33 -0
  12. package/docs/schemas/altium_toolkit/schematic_svg_semantics_a1.schema.json +50 -0
  13. package/docs/schemas/altium_toolkit/svg_model_cross_link_a1.schema.json +39 -0
  14. package/docs/testing.md +9 -3
  15. package/package.json +1 -1
  16. package/spec/library-scope.md +7 -1
  17. package/src/core/altium/AltiumLayoutParser.mjs +104 -8
  18. package/src/core/altium/AltiumParser.mjs +196 -45
  19. package/src/core/altium/CiArtifactBundleBuilder.mjs +202 -0
  20. package/src/core/altium/DraftsmanDigestParser.mjs +689 -0
  21. package/src/core/altium/EmbeddedFileInventoryBuilder.mjs +255 -0
  22. package/src/core/altium/IntLibModelParser.mjs +240 -0
  23. package/src/core/altium/IntLibStreamExtractor.mjs +366 -0
  24. package/src/core/altium/LibraryRenderManifestBuilder.mjs +417 -0
  25. package/src/core/altium/LibrarySearchIndex.mjs +215 -0
  26. package/src/core/altium/NormalizedModelSchema.mjs +36 -0
  27. package/src/core/altium/ParserCompatibilityFuzzer.mjs +192 -0
  28. package/src/core/altium/PcbCustomPadShapeParser.mjs +244 -0
  29. package/src/core/altium/PcbDefaultsParser.mjs +171 -0
  30. package/src/core/altium/PcbDimensionParser.mjs +229 -0
  31. package/src/core/altium/PcbEmbeddedModelExtractor.mjs +232 -6
  32. package/src/core/altium/PcbExtendedPrimitiveInformationParser.mjs +256 -0
  33. package/src/core/altium/PcbLibModelParser.mjs +235 -14
  34. package/src/core/altium/PcbLibStreamExtractor.mjs +62 -4
  35. package/src/core/altium/PcbMaskPasteResolver.mjs +354 -0
  36. package/src/core/altium/PcbMechanicalLayerPairParser.mjs +204 -0
  37. package/src/core/altium/PcbModelParser.mjs +495 -32
  38. package/src/core/altium/PcbOwnershipGraphBuilder.mjs +245 -0
  39. package/src/core/altium/PcbPadPrimitiveParser.mjs +78 -65
  40. package/src/core/altium/PcbPadStackParser.mjs +229 -2
  41. package/src/core/altium/PcbPickPlacePositionResolver.mjs +224 -0
  42. package/src/core/altium/PcbPrimitiveParameterParser.mjs +3 -2
  43. package/src/core/altium/PcbRawRecordRegistry.mjs +121 -130
  44. package/src/core/altium/PcbRegionPrimitiveParser.mjs +76 -3
  45. package/src/core/altium/PcbRouteAnalysisBuilder.mjs +730 -0
  46. package/src/core/altium/PcbRuleParser.mjs +354 -33
  47. package/src/core/altium/PcbSidecarRecordParser.mjs +177 -0
  48. package/src/core/altium/PcbSpecialStringResolver.mjs +220 -0
  49. package/src/core/altium/PcbStatisticsBuilder.mjs +541 -0
  50. package/src/core/altium/PcbStreamExtractor.mjs +111 -4
  51. package/src/core/altium/PcbTextPrimitiveParser.mjs +60 -0
  52. package/src/core/altium/PcbUnionParser.mjs +307 -0
  53. package/src/core/altium/PcbViaStackParser.mjs +98 -10
  54. package/src/core/altium/PcbViaStructureParser.mjs +335 -0
  55. package/src/core/altium/PrintableTextDecoder.mjs +53 -3
  56. package/src/core/altium/PrjPcbModelParser.mjs +281 -7
  57. package/src/core/altium/ProjectAnnotationParser.mjs +205 -0
  58. package/src/core/altium/ProjectDesignBundleBuilder.mjs +492 -0
  59. package/src/core/altium/ProjectDocumentGraphBuilder.mjs +280 -0
  60. package/src/core/altium/ProjectNetlistExporter.mjs +503 -0
  61. package/src/core/altium/ProjectOutJobDigestBuilder.mjs +109 -0
  62. package/src/core/altium/ProjectVariantViewBuilder.mjs +334 -0
  63. package/src/core/altium/SchematicBindingProvenanceParser.mjs +223 -0
  64. package/src/core/altium/SchematicComponentOwnerTextResolver.mjs +312 -0
  65. package/src/core/altium/SchematicComponentTextResolver.mjs +72 -19
  66. package/src/core/altium/SchematicConnectivityQaBuilder.mjs +271 -0
  67. package/src/core/altium/SchematicCrossSheetConnectorParser.mjs +140 -0
  68. package/src/core/altium/SchematicDirectiveParser.mjs +312 -0
  69. package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +231 -0
  70. package/src/core/altium/SchematicHarnessParser.mjs +302 -0
  71. package/src/core/altium/SchematicImageParser.mjs +474 -3
  72. package/src/core/altium/SchematicImplementationParser.mjs +518 -0
  73. package/src/core/altium/SchematicNetlistBuilder.mjs +15 -2
  74. package/src/core/altium/SchematicOwnershipGraphParser.mjs +195 -0
  75. package/src/core/altium/SchematicPinParser.mjs +84 -1
  76. package/src/core/altium/SchematicPrimitiveParser.mjs +301 -0
  77. package/src/core/altium/SchematicProjectParameterResolver.mjs +361 -0
  78. package/src/core/altium/SchematicQaReportBuilder.mjs +284 -0
  79. package/src/core/altium/SchematicRecordTypeRegistry.mjs +137 -0
  80. package/src/core/altium/SchematicRepeatedChannelParser.mjs +229 -0
  81. package/src/core/altium/SchematicStreamExtractor.mjs +10 -1
  82. package/src/core/altium/SchematicTemplateParser.mjs +256 -0
  83. package/src/core/altium/SchematicTextParser.mjs +123 -0
  84. package/src/core/altium/SvgModelCrossLinkValidator.mjs +402 -0
  85. package/src/core/circuit-json/CircuitJsonModelAdapter.mjs +136 -96
  86. package/src/core/circuit-json/CircuitJsonModelAdapterPcbElements.mjs +244 -0
  87. package/src/core/circuit-json/CircuitJsonModelSchema.mjs +1 -1
  88. package/src/core/ole/OleCompoundDocument.mjs +20 -0
  89. package/src/parser.mjs +35 -0
  90. package/src/styles/altium-renderers.css +19 -0
  91. package/src/ui/PcbBarcodeTextRenderer.mjs +436 -0
  92. package/src/ui/PcbInteractionIndex.mjs +9 -4
  93. package/src/ui/PcbScene3dBuilder.mjs +137 -3
  94. package/src/ui/PcbScene3dModelRegistry.mjs +74 -0
  95. package/src/ui/PcbSvgRenderer.mjs +1252 -34
  96. package/src/ui/PcbTextPrimitiveRenderer.mjs +193 -7
  97. package/src/ui/SchematicNoteRenderer.mjs +9 -2
  98. package/src/ui/SchematicOwnerPinLabelLayout.mjs +206 -0
  99. package/src/ui/SchematicShapeRenderer.mjs +362 -0
  100. package/src/ui/SchematicSvgRenderer.mjs +1442 -92
  101. package/src/ui/SchematicTypography.mjs +48 -5
  102. package/src/ui/TextGeometrySidecarBuilder.mjs +147 -0
@@ -0,0 +1,220 @@
1
+ // SPDX-FileCopyrightText: 2026 André Fiedler
2
+ //
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ /**
6
+ * Resolves PCB text special strings against project parameters.
7
+ */
8
+ export class PcbSpecialStringResolver {
9
+ /**
10
+ * Resolves one authored PCB text expression.
11
+ * @param {string} text
12
+ * @param {Record<string, string | number | boolean>} [parameters]
13
+ * @returns {{ rawText: string, resolvedText: string, parameterNames: string[], expressionParts: object[] } | null}
14
+ */
15
+ static resolveText(text, parameters = {}) {
16
+ const rawText = String(text ?? '')
17
+ const expressionParts =
18
+ PcbSpecialStringResolver.#parseExpressionParts(rawText)
19
+
20
+ if (!expressionParts.length) {
21
+ return null
22
+ }
23
+
24
+ const normalizedParameters =
25
+ PcbSpecialStringResolver.#normalizeParameters(parameters)
26
+ const resolvedParts = []
27
+ const parameterNames = []
28
+
29
+ for (const part of expressionParts) {
30
+ if (part.type !== 'parameter') {
31
+ resolvedParts.push(part)
32
+ continue
33
+ }
34
+
35
+ const parameter = normalizedParameters.get(part.name.toLowerCase())
36
+
37
+ if (!parameter) {
38
+ resolvedParts.push({
39
+ ...part,
40
+ value: '.' + part.name,
41
+ unresolved: true
42
+ })
43
+ continue
44
+ }
45
+
46
+ parameterNames.push(parameter.name)
47
+ resolvedParts.push({
48
+ type: 'parameter',
49
+ name: parameter.name,
50
+ value: String(parameter.value)
51
+ })
52
+ }
53
+
54
+ if (
55
+ !parameterNames.length &&
56
+ !resolvedParts.some((part) => part.type === 'parameter')
57
+ ) {
58
+ return null
59
+ }
60
+
61
+ return {
62
+ rawText,
63
+ resolvedText: resolvedParts.map((part) => part.value).join(''),
64
+ parameterNames,
65
+ expressionParts: resolvedParts
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Adds special-string metadata to text primitives.
71
+ * @param {object[]} texts
72
+ * @param {Record<string, string | number | boolean>} [parameters]
73
+ * @param {{ replaceText?: boolean }} [options]
74
+ * @returns {object[]}
75
+ */
76
+ static annotateTexts(texts, parameters = {}, options = {}) {
77
+ return (texts || []).map((text) => {
78
+ const resolved = PcbSpecialStringResolver.resolveText(
79
+ text?.text,
80
+ parameters
81
+ )
82
+
83
+ if (!resolved) {
84
+ return text
85
+ }
86
+
87
+ return {
88
+ ...text,
89
+ ...(options.replaceText === true
90
+ ? { text: resolved.resolvedText }
91
+ : {}),
92
+ rawText: resolved.rawText,
93
+ resolvedText: resolved.resolvedText,
94
+ specialString: {
95
+ parameterNames: resolved.parameterNames,
96
+ expressionParts: resolved.expressionParts
97
+ }
98
+ }
99
+ })
100
+ }
101
+
102
+ /**
103
+ * Splits a supported special-string expression into literal and parameter
104
+ * parts.
105
+ * @param {string} text
106
+ * @returns {{ type: string, name?: string, value?: string }[]}
107
+ */
108
+ static #parseExpressionParts(text) {
109
+ const segments = PcbSpecialStringResolver.#splitConcatenation(text)
110
+ const parts = []
111
+
112
+ for (const segment of segments) {
113
+ const trimmed = segment.trim()
114
+ if (!trimmed) {
115
+ continue
116
+ }
117
+
118
+ if (PcbSpecialStringResolver.#isQuoted(trimmed)) {
119
+ parts.push({
120
+ type: 'literal',
121
+ value: PcbSpecialStringResolver.#unquote(trimmed)
122
+ })
123
+ continue
124
+ }
125
+
126
+ const parameterMatch = trimmed.match(/^\.([A-Za-z_][\w.-]*)$/u)
127
+ if (parameterMatch) {
128
+ parts.push({
129
+ type: 'parameter',
130
+ name: parameterMatch[1]
131
+ })
132
+ continue
133
+ }
134
+
135
+ if (trimmed.startsWith('.')) {
136
+ parts.push({
137
+ type: 'literal',
138
+ value: trimmed
139
+ })
140
+ }
141
+ }
142
+
143
+ return parts
144
+ }
145
+
146
+ /**
147
+ * Splits an expression on plus operators outside quoted strings.
148
+ * @param {string} text
149
+ * @returns {string[]}
150
+ */
151
+ static #splitConcatenation(text) {
152
+ const segments = []
153
+ let quote = ''
154
+ let current = ''
155
+
156
+ for (const character of String(text || '')) {
157
+ if ((character === '"' || character === "'") && !quote) {
158
+ quote = character
159
+ current += character
160
+ continue
161
+ }
162
+
163
+ if (character === quote) {
164
+ quote = ''
165
+ current += character
166
+ continue
167
+ }
168
+
169
+ if (character === '+' && !quote) {
170
+ segments.push(current)
171
+ current = ''
172
+ continue
173
+ }
174
+
175
+ current += character
176
+ }
177
+
178
+ segments.push(current)
179
+ return segments
180
+ }
181
+
182
+ /**
183
+ * Returns true when one segment is a quoted literal.
184
+ * @param {string} value
185
+ * @returns {boolean}
186
+ */
187
+ static #isQuoted(value) {
188
+ return (
189
+ (value.startsWith('"') && value.endsWith('"')) ||
190
+ (value.startsWith("'") && value.endsWith("'"))
191
+ )
192
+ }
193
+
194
+ /**
195
+ * Removes simple surrounding quotes from one literal.
196
+ * @param {string} value
197
+ * @returns {string}
198
+ */
199
+ static #unquote(value) {
200
+ return value.slice(1, -1).replace(/\\(["'\\])/gu, '$1')
201
+ }
202
+
203
+ /**
204
+ * Builds a case-insensitive project parameter map.
205
+ * @param {Record<string, string | number | boolean>} parameters
206
+ * @returns {Map<string, { name: string, value: string | number | boolean }>}
207
+ */
208
+ static #normalizeParameters(parameters) {
209
+ const normalized = new Map()
210
+
211
+ for (const [name, value] of Object.entries(parameters || {})) {
212
+ normalized.set(String(name).toLowerCase(), {
213
+ name,
214
+ value
215
+ })
216
+ }
217
+
218
+ return normalized
219
+ }
220
+ }