molecule
Evan — Adom Molecule LED Nameplate
Public Made by Adomby adom
Evan's name in 66 green 0603 LEDs on a 5x7 dot-matrix grid, USB-C powered off the east edge. A real Adom molecule: four corner machine pins, 96x44 mm absolute, every part from the Adom Basic Parts catalog.
← Commit history
Viewer: reassemble split bundle as an inline script; CSP blocks blob: URLs
1 file changed
+37−18
viewer/index.html+37−18@@ -1296,37 +1296,56 @@ function loadAlphabet() { } function loadAdom3D() {- // The wiki refuses to render any file over ~4 MB ("File too large to- // render", HTTP 413) and this bundle is 13 MB, so the static export also- // ships it as 3.5 MB parts and reassembles them here. Falls back to the- // whole file when the parts are absent (local dev / `adom-tsci start`).+ // Two wiki constraints shape this.+ //+ // 1. The render endpoint 413s ("File too large to render") on files over+ // ~4 MB and this bundle is 13 MB, so it also ships as 4 byte-exact+ // parts. Reassemble as BYTES, never as text: a multi-byte character can+ // straddle a split boundary and decoding per part corrupts it.+ //+ // 2. The page CSP is `script-src 'unsafe-inline' https:`. That permits an+ // INLINE script but blocks `blob:`, so URL.createObjectURL + script.src+ // fails with "Loading the script 'blob:null/...' violates ... Content+ // Security Policy". Setting textContent runs it under 'unsafe-inline'+ // instead, and an inline script executes synchronously on append, so+ // there is nothing to await once it is in the DOM.+ //+ // Falls back to the single 13 MB file when the parts are absent, which is+ // the local-dev case (`adom-tsci start`) where no CSP or size cap applies. return (async () => {- const PARTS = 4;- let bytes = null;+ const N = 4;+ let all = null; try { const bufs = [];- for (let i = 0; i < PARTS; i++) {+ for (let i = 0; i < N; i++) { const r = await fetch('js/adom-3d-viewer.min.js.part' + i); if (!r.ok) throw new Error('viewer bundle part ' + i + ' -> HTTP ' + r.status); bufs.push(new Uint8Array(await r.arrayBuffer())); } let total = 0; for (const b of bufs) total += b.length;- bytes = new Uint8Array(total);- let off = 0;- for (const b of bufs) { bytes.set(b, off); off += b.length; }+ all = new Uint8Array(total);+ let o = 0;+ for (const b of bufs) { all.set(b, o); o += b.length; } } catch (e) {- bytes = null; // parts not present -> single-file path below+ all = null; }- await new Promise((resolve, reject) => {++ if (all) { const s = document.createElement('script');- s.src = bytes- ? URL.createObjectURL(new Blob([bytes], { type: 'text/javascript' }))- : 'js/adom-3d-viewer.min.js';- s.onload = () => resolve();- s.onerror = () => reject(new Error('Failed to load adom-3d-viewer.min.js'));+ s.textContent = new TextDecoder('utf-8').decode(all); document.head.appendChild(s);- });+ } else {+ await new Promise((resolve, reject) => {+ const s = document.createElement('script');+ s.src = 'js/adom-3d-viewer.min.js';+ s.onload = () => resolve();+ s.onerror = () => reject(new Error('Failed to load adom-3d-viewer.min.js'));+ document.head.appendChild(s);+ });+ }++ if (!window.Adom3DViewer) throw new Error('viewer bundle ran but exposed no Adom3DViewer'); })(); }