← Commit history
SKILL.md+2
@@ -199,3 +199,5 @@ Registered wiki widgets receive server-inlined manifest JSON. This does not auth For private wiki LODs, generate one lazy `*.frame.html` transport per level in the component repo. The transport contains that level's GLB bytes and posts an ArrayBuffer to its requesting parent. Record `transport.kind: wiki-sandbox-frame@1`, its path/hash/bytes and `sourceLevelSha256` in the level. It is navigated only after explicit enable and on demand for a selected level. The server's normal page authorization applies to that iframe navigation. The parent verifies the exact iframe window, random request nonce, expected GLB hash and byte count; times out after 12 seconds, aborts on disposal, removes the transport frame, and passes verified Uint8Array bytes directly to Babylon. Do not use a blob URL fetch where CSP disallows it. Never put an authentication ticket or session token in the transport.  Opaque widgets also deny Web Storage. If access throws, use ephemeral document-local storage for canonical viewer preferences; never overwrite working user storage. Test against the real wiki CSP and private-file authorization separately from an unrestricted local harness. Preserve optional 3D behavior and small cached previews by default.++When enabling an inline preview, reveal the expanded viewer inside its existing work surface before starting offscreen observation or model loading. Test with the enable button near the bottom of a short viewport. Otherwise an otherwise-correct offscreen cleanup policy can immediately cancel the user's requested preview. Preserve the editor pane and OS foreground.
files/chip-thumbnail-widget.js+2
@@ -158,6 +158,8 @@ async function mountLodPreview({host,manifest,resolveAsset,onState,signal}) {       host.hidden=false;button.textContent='Turn off 3D preview';status.textContent='Loading super-low detail…';this._height();       const update=state=>{if(this._previewAbort!==abort)return;this._state.preview=state;this._state.glbRequests=state.glbRequests||0;this._previewActive=state.active;button.textContent=state.active?'Turn off 3D preview':'Turn on 3D preview';host.hidden=!state.active;status.textContent=state.active?`${state.level||'Loading'}${state.loading?' · loading '+state.wanted:''} · zoom to change detail`:'3D preview off'+(state.stopReason?' · '+state.stopReason:'');this._height();};       try{+        if(document.hidden)throw new Error('Activate this page before enabling 3D');+        await new Promise(resolve=>requestAnimationFrame(()=>{host.scrollIntoView({block:'center',behavior:'instant'});requestAnimationFrame(resolve);}));         const module={mountLodPreview};         if(abort.signal.aborted){update({active:false,stopReason:'turned off'});return;}         const controller=await module.mountLodPreview({host,manifest:d,resolveAsset:p=>this._url(p,src),onState:update,signal:abort.signal});if(this._previewAbort===abort)this._previewController=controller;else controller.dispose();
package.json+1−1
@@ -2,7 +2,7 @@   "slug": "component-widgets",   "title": "Component Widgets",   "type": "app",-  "version": "1.1.11",+  "version": "1.1.12",   "description": "Reusable component-page widgets for pinouts, thermal vias, molecule relationships, reviewed chip outlines and image-first LOD caches with an optional zoom-driven 3D preview.",   "license": "MIT",   "dependencies": {},
page.json+6−2
@@ -4,7 +4,7 @@   "type": "app",   "title": "Component Widgets",   "brief": "Reusable component widgets for pinouts, thermal vias, molecule relationships, reviewed chip outlines and lightweight four-level LOD cache listings.",-  "version": "1.1.11",+  "version": "1.1.12",   "visibility": {     "public": true   },@@ -60,6 +60,10 @@     "drop a pinout into a page",     "reusable component widget",     "cross-highlight pins on a page",+    "cached chip thumbnails",+    "reuse chip outline",+    "reviewed MPN placement",+    "chip artwork cache",     "component LOD widget",     "LOD listing",     "LOD manifest",@@ -90,4 +94,4 @@     "hero",     "readme"   ]-}+}\ No newline at end of file
skills/component-widgets/SKILL.md+2
@@ -199,3 +199,5 @@ Registered wiki widgets receive server-inlined manifest JSON. This does not auth For private wiki LODs, generate one lazy `*.frame.html` transport per level in the component repo. The transport contains that level's GLB bytes and posts an ArrayBuffer to its requesting parent. Record `transport.kind: wiki-sandbox-frame@1`, its path/hash/bytes and `sourceLevelSha256` in the level. It is navigated only after explicit enable and on demand for a selected level. The server's normal page authorization applies to that iframe navigation. The parent verifies the exact iframe window, random request nonce, expected GLB hash and byte count; times out after 12 seconds, aborts on disposal, removes the transport frame, and passes verified Uint8Array bytes directly to Babylon. Do not use a blob URL fetch where CSP disallows it. Never put an authentication ticket or session token in the transport.  Opaque widgets also deny Web Storage. If access throws, use ephemeral document-local storage for canonical viewer preferences; never overwrite working user storage. Test against the real wiki CSP and private-file authorization separately from an unrestricted local harness. Preserve optional 3D behavior and small cached previews by default.++When enabling an inline preview, reveal the expanded viewer inside its existing work surface before starting offscreen observation or model loading. Test with the enable button near the bottom of a short viewport. Otherwise an otherwise-correct offscreen cleanup policy can immediately cancel the user's requested preview. Preserve the editor pane and OS foreground.
skills/component-widgets/widgets/chip-thumbnail-widget.js+2
@@ -158,6 +158,8 @@ async function mountLodPreview({host,manifest,resolveAsset,onState,signal}) {       host.hidden=false;button.textContent='Turn off 3D preview';status.textContent='Loading super-low detail…';this._height();       const update=state=>{if(this._previewAbort!==abort)return;this._state.preview=state;this._state.glbRequests=state.glbRequests||0;this._previewActive=state.active;button.textContent=state.active?'Turn off 3D preview':'Turn on 3D preview';host.hidden=!state.active;status.textContent=state.active?`${state.level||'Loading'}${state.loading?' · loading '+state.wanted:''} · zoom to change detail`:'3D preview off'+(state.stopReason?' · '+state.stopReason:'');this._height();};       try{+        if(document.hidden)throw new Error('Activate this page before enabling 3D');+        await new Promise(resolve=>requestAnimationFrame(()=>{host.scrollIntoView({block:'center',behavior:'instant'});requestAnimationFrame(resolve);}));         const module={mountLodPreview};         if(abort.signal.aborted){update({active:false,stopReason:'turned off'});return;}         const controller=await module.mountLodPreview({host,manifest:d,resolveAsset:p=>this._url(p,src),onState:update,signal:abort.signal});if(this._previewAbort===abort)this._previewController=controller;else controller.dispose();
widgets/chip-thumbnail-widget.js+2
@@ -158,6 +158,8 @@ async function mountLodPreview({host,manifest,resolveAsset,onState,signal}) {       host.hidden=false;button.textContent='Turn off 3D preview';status.textContent='Loading super-low detail…';this._height();       const update=state=>{if(this._previewAbort!==abort)return;this._state.preview=state;this._state.glbRequests=state.glbRequests||0;this._previewActive=state.active;button.textContent=state.active?'Turn off 3D preview':'Turn on 3D preview';host.hidden=!state.active;status.textContent=state.active?`${state.level||'Loading'}${state.loading?' · loading '+state.wanted:''} · zoom to change detail`:'3D preview off'+(state.stopReason?' · '+state.stopReason:'');this._height();};       try{+        if(document.hidden)throw new Error('Activate this page before enabling 3D');+        await new Promise(resolve=>requestAnimationFrame(()=>{host.scrollIntoView({block:'center',behavior:'instant'});requestAnimationFrame(resolve);}));         const module={mountLodPreview};         if(abort.signal.aborted){update({active:false,stopReason:'turned off'});return;}         const controller=await module.mountLodPreview({host,manifest:d,resolveAsset:p=>this._url(p,src),onState:update,signal:abort.signal});if(this._previewAbort===abort)this._previewController=controller;else controller.dispose();