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,480 @@
1
+ // SPDX-FileCopyrightText: 2026 André Fiedler
2
+ //
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ import {
6
+ AltiumParser,
7
+ BomTableRenderer,
8
+ PcbScene3dSummaryRenderer,
9
+ PcbSvgRenderer,
10
+ SchematicSvgRenderer
11
+ } from '../../src/index.mjs'
12
+ import { PcbThreeSceneRenderer } from './PcbThreeSceneRenderer.mjs'
13
+ import { SvgViewportController } from './SvgViewportController.mjs'
14
+
15
+ const SOURCE_PROJECT_URL =
16
+ 'https://github.com/Mehdi-KHALFALLAH/My-Arduino-UNO-Design'
17
+ const SOURCE_DOCUMENTS = [
18
+ {
19
+ key: 'schematic',
20
+ label: 'source schematic',
21
+ fileName: '[03] - 28PINS SHEMATIC.SchDoc',
22
+ defaultView: 'schematic',
23
+ url: 'https://raw.githubusercontent.com/Mehdi-KHALFALLAH/My-Arduino-UNO-Design/master/Design%20Files/28_Pin%20Project%20V1.1/%5B03%5D%20-%2028PINS%20SHEMATIC.SchDoc'
24
+ },
25
+ {
26
+ key: 'pcb',
27
+ label: 'source PCB',
28
+ fileName: '28Pins_Project_1.1_PCB.PcbDoc',
29
+ defaultView: 'pcb',
30
+ url: 'https://raw.githubusercontent.com/Mehdi-KHALFALLAH/My-Arduino-UNO-Design/master/Design%20Files/28_Pin%20Project%20V1.1/28Pins_Project_1.1_PCB.PcbDoc'
31
+ }
32
+ ]
33
+
34
+ /**
35
+ * Coordinates the local-file example page.
36
+ */
37
+ class ArduinoUnoExample {
38
+ #activeView = 'schematic'
39
+ #documentModel = null
40
+ #elements
41
+ #sourceDocumentModels = new Map()
42
+ #svgViewportController = null
43
+ #threeRenderer = null
44
+
45
+ /**
46
+ * Starts the browser example.
47
+ * @returns {void}
48
+ */
49
+ static boot() {
50
+ new ArduinoUnoExample().#bind()
51
+ }
52
+
53
+ /**
54
+ * Creates the page controller.
55
+ */
56
+ constructor() {
57
+ this.#elements = {
58
+ input: document.querySelector('#document-file'),
59
+ output: document.querySelector('#output'),
60
+ status: document.querySelector('#status'),
61
+ tabs: [...document.querySelectorAll('[data-view]')]
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Wires DOM events.
67
+ * @returns {void}
68
+ */
69
+ #bind() {
70
+ this.#elements.input?.addEventListener('change', (event) => {
71
+ this.#handleFileSelection(event)
72
+ })
73
+
74
+ for (const tab of this.#elements.tabs) {
75
+ tab.addEventListener('click', () => {
76
+ this.#setActiveView(tab.dataset.view)
77
+ })
78
+ }
79
+
80
+ this.#loadSourceDocuments()
81
+ }
82
+
83
+ /**
84
+ * Reads and parses the selected file.
85
+ * @param {Event} event
86
+ * @returns {Promise<void>}
87
+ */
88
+ async #handleFileSelection(event) {
89
+ const [file] = event.target.files || []
90
+ if (!file) return
91
+
92
+ this.#setStatus('Parsing ' + file.name + '...', 'busy')
93
+
94
+ try {
95
+ this.#disposeSvgViewportController()
96
+ this.#disposeThreeRenderer()
97
+ const arrayBuffer = await file.arrayBuffer()
98
+ this.#documentModel = AltiumParser.parseArrayBuffer(
99
+ file.name,
100
+ arrayBuffer
101
+ )
102
+ this.#activeView =
103
+ this.#documentModel.kind === 'pcb' ? 'pcb' : 'schematic'
104
+ this.#setStatus(
105
+ 'Loaded ' +
106
+ file.name +
107
+ ' as ' +
108
+ this.#documentModel.fileType +
109
+ '.',
110
+ 'ready'
111
+ )
112
+ this.#syncTabs()
113
+ this.#render()
114
+ } catch (error) {
115
+ this.#disposeSvgViewportController()
116
+ this.#disposeThreeRenderer()
117
+ this.#documentModel = null
118
+ this.#setStatus(this.#formatError(error), 'error')
119
+ this.#renderError(error)
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Fetches and parses every document from the credited source project.
125
+ * @returns {Promise<void>}
126
+ */
127
+ async #loadSourceDocuments() {
128
+ this.#disposeSvgViewportController()
129
+ this.#disposeThreeRenderer()
130
+ this.#documentModel = null
131
+ this.#activeView = 'schematic'
132
+ this.#syncTabs()
133
+ this.#setStatus(
134
+ 'Loading credited source documents from GitHub...',
135
+ 'busy'
136
+ )
137
+ this.#elements.output.innerHTML = this.#renderLoadingState()
138
+
139
+ try {
140
+ const loadedDocuments = await Promise.all(
141
+ SOURCE_DOCUMENTS.map((sourceDocument) =>
142
+ this.#fetchSourceDocument(sourceDocument)
143
+ )
144
+ )
145
+ this.#sourceDocumentModels = new Map(
146
+ loadedDocuments.map(({ sourceDocument, documentModel }) => [
147
+ sourceDocument.key,
148
+ documentModel
149
+ ])
150
+ )
151
+ this.#setStatus(
152
+ 'Loaded credited source schematic and source PCB from Mehdi KHALFALLAH via raw.githubusercontent.com.',
153
+ 'ready'
154
+ )
155
+ this.#syncTabs()
156
+ this.#render()
157
+ } catch (error) {
158
+ this.#sourceDocumentModels = new Map()
159
+ this.#setStatus(this.#formatError(error), 'error')
160
+ this.#renderError(error)
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Fetches and parses one credited source document.
166
+ * @param {{ key: string, label: string, fileName: string, defaultView: string, url: string }} sourceDocument
167
+ * @returns {Promise<{ sourceDocument: { key: string, label: string, fileName: string, defaultView: string, url: string }, documentModel: ReturnType<typeof AltiumParser.parseArrayBuffer> }>}
168
+ */
169
+ async #fetchSourceDocument(sourceDocument) {
170
+ const response = await fetch(sourceDocument.url)
171
+ if (!response.ok) {
172
+ throw new Error(
173
+ 'GitHub returned HTTP ' +
174
+ response.status +
175
+ ' for ' +
176
+ sourceDocument.fileName +
177
+ '.'
178
+ )
179
+ }
180
+
181
+ const arrayBuffer = await response.arrayBuffer()
182
+ return {
183
+ sourceDocument,
184
+ documentModel: AltiumParser.parseArrayBuffer(
185
+ sourceDocument.fileName,
186
+ arrayBuffer
187
+ )
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Changes the active renderer tab.
193
+ * @param {string | undefined} view
194
+ * @returns {void}
195
+ */
196
+ #setActiveView(view) {
197
+ if (!view) return
198
+ this.#activeView = view
199
+ this.#syncTabs()
200
+ this.#render()
201
+ }
202
+
203
+ /**
204
+ * Updates tab ARIA and selected state.
205
+ * @returns {void}
206
+ */
207
+ #syncTabs() {
208
+ for (const tab of this.#elements.tabs) {
209
+ const isActive = tab.dataset.view === this.#activeView
210
+ tab.classList.toggle('is-active', isActive)
211
+ tab.setAttribute('aria-selected', String(isActive))
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Selects the parsed document model for the current view.
217
+ * @returns {ReturnType<typeof AltiumParser.parseArrayBuffer> | null}
218
+ */
219
+ #getActiveDocumentModel() {
220
+ if (this.#documentModel) return this.#documentModel
221
+
222
+ const sourceKeyByView = {
223
+ '3d': 'pcb',
224
+ bom: 'schematic',
225
+ metadata: 'schematic',
226
+ pcb: 'pcb',
227
+ schematic: 'schematic',
228
+ summary: 'pcb'
229
+ }
230
+ const sourceKey = sourceKeyByView[this.#activeView]
231
+
232
+ return (
233
+ this.#sourceDocumentModels.get(sourceKey) ||
234
+ this.#sourceDocumentModels.values().next().value ||
235
+ null
236
+ )
237
+ }
238
+
239
+ /**
240
+ * Writes the current view to the output panel.
241
+ * @returns {void}
242
+ */
243
+ #render() {
244
+ this.#disposeSvgViewportController()
245
+ this.#disposeThreeRenderer()
246
+ const documentModel = this.#getActiveDocumentModel()
247
+ if (!documentModel) {
248
+ this.#elements.output.innerHTML = this.#renderEmptyState()
249
+ return
250
+ }
251
+
252
+ const renderers = {
253
+ schematic: () => SchematicSvgRenderer.render(documentModel),
254
+ pcb: () => PcbSvgRenderer.render(documentModel),
255
+ bom: () => BomTableRenderer.render(documentModel.bom || []),
256
+ '3d': () => this.#renderThreeScene(documentModel),
257
+ summary: () => PcbScene3dSummaryRenderer.render(documentModel),
258
+ metadata: () => this.#renderMetadata(documentModel)
259
+ }
260
+
261
+ this.#elements.output.innerHTML =
262
+ renderers[this.#activeView]?.() || this.#renderEmptyState()
263
+
264
+ if (this.#activeView === 'schematic') {
265
+ this.#mountSvgViewport('.schematic-svg')
266
+ } else if (this.#activeView === 'pcb') {
267
+ this.#mountSvgViewport('.pcb-svg')
268
+ } else if (this.#activeView === '3d') {
269
+ this.#mountThreeScene(documentModel)
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Mounts pan and zoom controls on the active SVG renderer output.
275
+ * @param {string} selector
276
+ * @returns {void}
277
+ */
278
+ #mountSvgViewport(selector) {
279
+ const svgElement = this.#elements.output.querySelector(selector)
280
+ if (!svgElement) return
281
+
282
+ this.#svgViewportController = new SvgViewportController(svgElement)
283
+ }
284
+
285
+ /**
286
+ * Releases the active SVG viewport controller when the view changes.
287
+ * @returns {void}
288
+ */
289
+ #disposeSvgViewportController() {
290
+ this.#svgViewportController?.dispose()
291
+ this.#svgViewportController = null
292
+ }
293
+
294
+ /**
295
+ * Mounts the browser-only Three.js PCB view.
296
+ * @param {ReturnType<typeof AltiumParser.parseArrayBuffer>} documentModel
297
+ * @returns {void}
298
+ */
299
+ #mountThreeScene(documentModel) {
300
+ const rootNode = this.#elements.output.querySelector(
301
+ '[data-three-scene-3d]'
302
+ )
303
+ if (!rootNode) return
304
+
305
+ try {
306
+ this.#threeRenderer = PcbThreeSceneRenderer.renderInto(
307
+ rootNode,
308
+ documentModel
309
+ )
310
+ } catch (error) {
311
+ this.#renderError(error)
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Releases the active Three.js renderer when the view changes.
317
+ * @returns {void}
318
+ */
319
+ #disposeThreeRenderer() {
320
+ this.#threeRenderer?.dispose()
321
+ this.#threeRenderer = null
322
+ }
323
+
324
+ /**
325
+ * Renders the interactive Three.js PCB shell.
326
+ * @param {ReturnType<typeof AltiumParser.parseArrayBuffer>} documentModel
327
+ * @returns {string}
328
+ */
329
+ #renderThreeScene(documentModel) {
330
+ const pcb = documentModel?.pcb
331
+ if (!pcb) {
332
+ return '<section class="empty-state"><h2>No PCB document loaded</h2><p>The interactive 3D view renders after the credited source PCB or another PCB document is loaded.</p></section>'
333
+ }
334
+
335
+ const widthMil = Math.round(pcb.boardOutline?.widthMil || 0)
336
+ const heightMil = Math.round(pcb.boardOutline?.heightMil || 0)
337
+ const componentCount = pcb.components?.length || 0
338
+ const padCount = pcb.pads?.length || 0
339
+
340
+ return (
341
+ '<section class="scene-3d" data-three-scene-3d><header class="scene-3d__header"><div><h2>Interactive 3D PCB</h2><p>' +
342
+ widthMil +
343
+ ' x ' +
344
+ heightMil +
345
+ ' mil board envelope</p></div><dl class="scene-3d__stats"><div><dt>Components</dt><dd>' +
346
+ componentCount +
347
+ '</dd></div><div><dt>Pads</dt><dd>' +
348
+ padCount +
349
+ '</dd></div></dl></header><div class="scene-3d__toolbar" aria-label="3D camera presets">' +
350
+ '<button class="scene-3d__preset is-active" type="button" data-three-scene-3d-preset="isometric" aria-pressed="true">Isometric</button>' +
351
+ '<button class="scene-3d__preset" type="button" data-three-scene-3d-preset="top" aria-pressed="false">Top</button>' +
352
+ '<button class="scene-3d__preset" type="button" data-three-scene-3d-preset="bottom" aria-pressed="false">Bottom</button>' +
353
+ '</div><div class="scene-3d__stage"><div class="scene-3d__viewport" aria-label="Interactive 3D PCB view">' +
354
+ '<div class="scene-3d__canvas-mount" data-three-scene-3d-viewport></div>' +
355
+ '<div class="scene-3d__loading" data-three-scene-3d-loading aria-live="polite"><p>Rendering PCB scene...</p></div></div>' +
356
+ '<aside class="scene-3d__controls" aria-label="3D visibility controls">' +
357
+ '<label class="scene-3d__toggle"><input type="checkbox" checked data-three-scene-3d-toggle="components" />Components</label>' +
358
+ '<label class="scene-3d__toggle"><input type="checkbox" checked data-three-scene-3d-toggle="copper" />Copper</label>' +
359
+ '</aside></div><p class="scene-3d__diagnostics" data-three-scene-3d-diagnostics aria-live="polite">Rendering PCB scene...</p></section>'
360
+ )
361
+ }
362
+
363
+ /**
364
+ * Renders parsed metadata and diagnostics.
365
+ * @param {ReturnType<typeof AltiumParser.parseArrayBuffer>} documentModel
366
+ * @returns {string}
367
+ */
368
+ #renderMetadata(documentModel) {
369
+ const summaryRows = Object.entries(documentModel.summary || {})
370
+ .map(
371
+ ([label, value]) =>
372
+ '<tr><th>' +
373
+ this.#escapeHtml(label) +
374
+ '</th><td>' +
375
+ this.#escapeHtml(String(value)) +
376
+ '</td></tr>'
377
+ )
378
+ .join('')
379
+ const diagnostics = (documentModel.diagnostics || [])
380
+ .map(
381
+ (diagnostic) =>
382
+ '<li><strong>' +
383
+ this.#escapeHtml(diagnostic.severity) +
384
+ '</strong> ' +
385
+ this.#escapeHtml(diagnostic.message) +
386
+ '</li>'
387
+ )
388
+ .join('')
389
+
390
+ return (
391
+ '<section class="metadata-panel"><header><h2>' +
392
+ this.#escapeHtml(documentModel.fileName) +
393
+ '</h2><p>' +
394
+ this.#escapeHtml(documentModel.fileType) +
395
+ ' parsed locally. Source inspiration: <a href="' +
396
+ SOURCE_PROJECT_URL +
397
+ '" target="_blank" rel="noreferrer">Mehdi KHALFALLAH&apos;s Arduino Uno Altium project</a>.</p></header>' +
398
+ '<table><tbody>' +
399
+ (summaryRows ||
400
+ '<tr><td colspan="2">No summary fields were recovered.</td></tr>') +
401
+ '</tbody></table><h3>Diagnostics</h3><ul>' +
402
+ (diagnostics || '<li>No diagnostics were reported.</li>') +
403
+ '</ul></section>'
404
+ )
405
+ }
406
+
407
+ /**
408
+ * Renders the initial empty state.
409
+ * @returns {string}
410
+ */
411
+ #renderEmptyState() {
412
+ return (
413
+ '<section class="empty-state"><h2>No document loaded</h2><p>' +
414
+ 'The example can fetch credited source documents from GitHub or load another local Altium design.</p></section>'
415
+ )
416
+ }
417
+
418
+ /**
419
+ * Renders the source project loading state.
420
+ * @returns {string}
421
+ */
422
+ #renderLoadingState() {
423
+ return (
424
+ '<section class="empty-state"><h2>Loading credited source documents</h2><p>' +
425
+ 'Fetching the source schematic and source PCB from Mehdi KHALFALLAH&apos;s public GitHub project. The files are parsed in this browser session and are not stored in this repository.</p></section>'
426
+ )
427
+ }
428
+
429
+ /**
430
+ * Renders a parse error.
431
+ * @param {unknown} error
432
+ * @returns {void}
433
+ */
434
+ #renderError(error) {
435
+ this.#elements.output.innerHTML =
436
+ '<section class="empty-state empty-state--error"><h2>Unable to render document</h2><p>' +
437
+ this.#escapeHtml(this.#formatError(error)) +
438
+ '</p></section>'
439
+ }
440
+
441
+ /**
442
+ * Updates the status text.
443
+ * @param {string} message
444
+ * @param {'busy' | 'error' | 'ready'} tone
445
+ * @returns {void}
446
+ */
447
+ #setStatus(message, tone = 'ready') {
448
+ this.#elements.status.textContent = message
449
+ this.#elements.status.dataset.tone = tone
450
+ }
451
+
452
+ /**
453
+ * Formats thrown values for display.
454
+ * @param {unknown} error
455
+ * @returns {string}
456
+ */
457
+ #formatError(error) {
458
+ return error instanceof Error ? error.message : String(error)
459
+ }
460
+
461
+ /**
462
+ * Escapes text for trusted example markup assembly.
463
+ * @param {string} value
464
+ * @returns {string}
465
+ */
466
+ #escapeHtml(value) {
467
+ return value.replace(/[&<>"']/g, (character) => {
468
+ const escapes = {
469
+ '&': '&amp;',
470
+ '<': '&lt;',
471
+ '>': '&gt;',
472
+ '"': '&quot;',
473
+ "'": '&#39;'
474
+ }
475
+ return escapes[character]
476
+ })
477
+ }
478
+ }
479
+
480
+ ArduinoUnoExample.boot()
@@ -0,0 +1,163 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 André Fiedler
3
+
4
+ SPDX-License-Identifier: GPL-3.0-or-later
5
+ -->
6
+
7
+ <!doctype html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="utf-8" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <title>Arduino Uno Altium Example | Altium Toolkit</title>
13
+ <link
14
+ rel="icon"
15
+ href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%231f7a68'/%3E%3Cpath d='M4 5h8v6H4z' fill='%23fffaf3'/%3E%3Cpath d='M5 8h6' stroke='%23c35f35' stroke-width='1.5'/%3E%3C/svg%3E"
16
+ />
17
+ <link rel="stylesheet" href="./styles.css" />
18
+ <script type="importmap">
19
+ {
20
+ "imports": {
21
+ "fflate": "../../node_modules/fflate/esm/browser.js",
22
+ "three": "../../node_modules/three/build/three.module.js",
23
+ "three/addons/": "../../node_modules/three/examples/jsm/"
24
+ }
25
+ }
26
+ </script>
27
+ <script type="module" src="./example.mjs"></script>
28
+ </head>
29
+ <body>
30
+ <main class="app-shell">
31
+ <header class="app-header">
32
+ <div>
33
+ <p class="eyebrow">Altium Toolkit example</p>
34
+ <h1>Arduino Uno design viewer</h1>
35
+ <p class="credit">
36
+ Based on the public My-Arduino-UNO-Design Altium project
37
+ by
38
+ <a
39
+ href="https://github.com/Mehdi-KHALFALLAH/My-Arduino-UNO-Design"
40
+ target="_blank"
41
+ rel="noreferrer"
42
+ >Mehdi KHALFALLAH</a
43
+ >. All credit for that Altium project belongs to him.
44
+ </p>
45
+ </div>
46
+ <a
47
+ class="source-link"
48
+ href="https://github.com/Mehdi-KHALFALLAH/My-Arduino-UNO-Design"
49
+ target="_blank"
50
+ rel="noreferrer"
51
+ >Source project</a
52
+ >
53
+ </header>
54
+
55
+ <section class="workspace" aria-label="Arduino Uno design example">
56
+ <aside class="control-panel">
57
+ <label class="file-picker" for="document-file">
58
+ <span>Load local Altium document</span>
59
+ <input
60
+ id="document-file"
61
+ type="file"
62
+ accept=".SchDoc,.PcbDoc,.schdoc,.pcbdoc"
63
+ />
64
+ </label>
65
+
66
+ <p id="status" class="status" role="status">
67
+ Loading credited source documents from GitHub...
68
+ </p>
69
+
70
+ <dl class="source-facts">
71
+ <div>
72
+ <dt>Reference</dt>
73
+ <dd>Arduino Uno recreated in Altium Designer</dd>
74
+ </div>
75
+ <div>
76
+ <dt>Creator</dt>
77
+ <dd>Mehdi KHALFALLAH</dd>
78
+ </div>
79
+ <div>
80
+ <dt>Data flow</dt>
81
+ <dd>
82
+ Auto-fetch source documents or load local files
83
+ </dd>
84
+ </div>
85
+ </dl>
86
+ </aside>
87
+
88
+ <section class="viewer-panel" aria-label="Rendered output">
89
+ <div class="viewer-toolbar">
90
+ <div class="tabs" role="tablist" aria-label="Views">
91
+ <button
92
+ class="tab is-active"
93
+ type="button"
94
+ data-view="schematic"
95
+ role="tab"
96
+ aria-selected="true"
97
+ >
98
+ Schematic
99
+ </button>
100
+ <button
101
+ class="tab"
102
+ type="button"
103
+ data-view="pcb"
104
+ role="tab"
105
+ aria-selected="false"
106
+ >
107
+ PCB
108
+ </button>
109
+ <button
110
+ class="tab"
111
+ type="button"
112
+ data-view="bom"
113
+ role="tab"
114
+ aria-selected="false"
115
+ >
116
+ BOM
117
+ </button>
118
+ <button
119
+ class="tab"
120
+ type="button"
121
+ data-view="3d"
122
+ role="tab"
123
+ aria-selected="false"
124
+ >
125
+ 3D
126
+ </button>
127
+ <button
128
+ class="tab"
129
+ type="button"
130
+ data-view="summary"
131
+ role="tab"
132
+ aria-selected="false"
133
+ >
134
+ Summary
135
+ </button>
136
+ <button
137
+ class="tab"
138
+ type="button"
139
+ data-view="metadata"
140
+ role="tab"
141
+ aria-selected="false"
142
+ >
143
+ Metadata
144
+ </button>
145
+ </div>
146
+ </div>
147
+
148
+ <div id="output" class="output" role="tabpanel">
149
+ <section class="empty-state">
150
+ <h2>Loading credited source documents</h2>
151
+ <p>
152
+ The example fetches Mehdi KHALFALLAH's public
153
+ Arduino Uno schematic and PCB documents from
154
+ GitHub at runtime. The Altium files are not
155
+ redistributed in this repository.
156
+ </p>
157
+ </section>
158
+ </div>
159
+ </section>
160
+ </section>
161
+ </main>
162
+ </body>
163
+ </html>