← Commit history
README.md+6−1
@@ -81,7 +81,7 @@ manifest from the component repository: ::adom-chip-thumbnail{src="/OWNER/COMPONENT/render/artwork/MPN-thumbs.json"} ``` -It shows cached shaded renders, vector outlines, editable source files, exact MPN+Its cache mode focuses on vector chip outlines, editable source files, exact MPN placement, canonical Z-up orientation, source hashes and review state. Legacy PNG/image `src` usage still works. The element's `selfCheck()` returns status, cache key, review state, selected asset and AI reuse hints.@@ -100,3 +100,8 @@ custom HTML README or per-component JavaScript fork is needed. Never broaden page visibility to make a private manifest load. Test the actual widget frame with the intended reader's permissions. Manifest asset URLs must remain in the component's repository on the manifest origin.++The cache widget focuses on chip outlines and their reuse. Do not repeat shaded+chip renders here: the component page already has its full 3D viewer. Existing+shaded thumbnail files can stay cached for library/card consumers without being+displayed in this widget. Legacy image-only widget usage is unchanged.
SKILL.md+6−1
@@ -109,7 +109,7 @@ manifest from the component repository: ::adom-chip-thumbnail{src="/OWNER/COMPONENT/render/artwork/MPN-thumbs.json"} ``` -It shows cached shaded renders, vector outlines, editable source files, exact MPN+Its cache mode focuses on vector chip outlines, editable source files, exact MPN placement, canonical Z-up orientation, source hashes and review state. Legacy PNG/image `src` usage still works. The element's `selfCheck()` returns status, cache key, review state, selected asset and AI reuse hints.@@ -128,3 +128,8 @@ custom HTML README or per-component JavaScript fork is needed. Never broaden page visibility to make a private manifest load. Test the actual widget frame with the intended reader's permissions. Manifest asset URLs must remain in the component's repository on the manifest origin.++The cache widget focuses on chip outlines and their reuse. Do not repeat shaded+chip renders here: the component page already has its full 3D viewer. Existing+shaded thumbnail files can stay cached for library/card consumers without being+displayed in this widget. Legacy image-only widget usage is unchanged.
files/chip-thumbnail-widget.js+4−4
@@ -39,14 +39,14 @@     }     _cache(d,src){       if(!Array.isArray(d.assets)||d.assets.length>100)throw new Error('Invalid asset list');-      const assets=d.assets.map(a=>({...a,url:this._url(a.path,src)}));-      const previews=assets.filter(a=>['png','jpg','jpeg','svg'].includes(a.format)&&a.kind!=='review-evidence');+      const assets=d.assets.filter(a=>a.kind!=='chip-shaded').map(a=>({...a,url:this._url(a.path,src)}));+      const previews=assets.filter(a=>a.kind==='chip-outline'&&a.format==='svg');       if(!previews.length)throw new Error('No cached previews');       const verified=d.verification?.status==='matched-at-publication',review=d.review||{};       this._state={ok:true,mode:'cache',mpn:d.mpn,cacheKey:d.cacheKey,assets:assets.length,sourceVerification:verified?'matched-at-publication':'not-verified',review,hints:d.hints||[],manifest:src};-      this.shadowRoot.innerHTML=`<style>${CSS}</style><section class="cache"><h3>Chip-thumbnailer cache</h3><div class="muted">${esc(d.mpn)} · Reuse the prepared chip artwork and its reviewed placement.</div><div class="badges"><span class="badge">${esc(d.recipe?.upAxis||'Unknown')}-up display</span><span class="badge">${assets.length} cached files</span><span class="badge">${verified?'Hashes matched when published':'Source check not recorded'}</span></div><div class="row"><div><div class="preview"><img alt="${esc(d.mpn)} cached chip artwork"></div><div class="controls variants"></div><div class="controls"><button class="zoom">Inspect closer</button><button class="reset">Fit</button></div></div><div><dl><dt>Virtual MPN placement</dt><dd>${esc(d.marking?.placement)}</dd><dt>Artwork review</dt><dd>${esc(review.projectedArtwork||'Not recorded')}</dd><dt>Display orientation</dt><dd>${esc(review.orientation)}</dd><dt>Source freshness</dt><dd class="muted">${verified?'Source and asset hashes matched '+esc(d.verification.checkedAtIso)+'.':'Source hashes have not been checked.'} This records a point-in-time check; compare the manifest hashes before reuse.</dd></dl><details><summary>Reuse files and generation recipe</summary><div class="files"></div><pre>${esc((d.hints||[]).join('\n'))}</pre><code>Cache key: ${esc(d.cacheKey)}</code></details></div></div></section>`;+      this.shadowRoot.innerHTML=`<style>${CSS}</style><section class="cache"><h3>Chip outline cache</h3><div class="muted">${esc(d.mpn)} · Reuse reviewed chip outlines in symbols and libraries.</div><div class="badges"><span class="badge">${esc(d.recipe?.upAxis||'Unknown')}-up display</span><span class="badge">${assets.length} cached files</span><span class="badge">${verified?'Hashes matched when published':'Source check not recorded'}</span></div><div class="row"><div><div class="preview"><img alt="${esc(d.mpn)} cached chip artwork"></div><div class="controls variants"></div><div class="controls"><button class="zoom">Inspect closer</button><button class="reset">Fit</button></div></div><div><dl><dt>Virtual MPN placement</dt><dd>${esc(d.marking?.placement)}</dd><dt>Artwork review</dt><dd>${esc(review.projectedArtwork||'Not recorded')}</dd><dt>Display orientation</dt><dd>${esc(review.orientation)}</dd><dt>Source freshness</dt><dd class="muted">${verified?'Source and asset hashes matched '+esc(d.verification.checkedAtIso)+'.':'Source hashes have not been checked.'} This records a point-in-time check; compare the manifest hashes before reuse.</dd></dl><details><summary>Reuse files and generation recipe</summary><div class="files"></div><pre>${esc((d.hints||[]).join('\n'))}</pre><code>Cache key: ${esc(d.cacheKey)}</code></details></div></div></section>`;       const img=this.shadowRoot.querySelector('img'),controls=this.shadowRoot.querySelector('.variants');-      previews.forEach((a,i)=>{const b=document.createElement('button');b.textContent=a.label||a.style||a.kind;b.setAttribute('aria-pressed',String(i===0));b.onclick=()=>{img.src=a.url;img.style.transform='';controls.querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));this._state.selected=a.path;};controls.append(b);});+      previews.forEach((a,i)=>{const b=document.createElement('button');b.textContent=({'symbol':'Schematic ink','teal':'Teal','dark':'Dark ink'})[a.style]||a.label||a.style||a.kind;b.setAttribute('aria-pressed',String(i===0));b.onclick=()=>{img.src=a.url;img.style.transform='';controls.querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));this._state.selected=a.path;};controls.append(b);});       img.src=previews[0].url;img.onload=()=>this._height();img.onerror=()=>{this._state.ok=false;this._state.error='Preview failed to load';img.alt='Cached preview unavailable';this._height();};       this.shadowRoot.querySelector('.zoom').onclick=()=>{img.style.transform='scale(1.55)';};this.shadowRoot.querySelector('.reset').onclick=()=>{img.style.transform='';};       const files=this.shadowRoot.querySelector('.files');for(const a of assets){const link=document.createElement('a');link.href=a.url.replace(/https:\/\/wiki\.adom\.inc\/([^/]+)\/([^/]+)\/render\//,'https://wiki.adom.inc/api/pages/$1/$2/files/');link.download=a.path.split('/').pop();link.textContent=`${a.path.split('/').pop()} · ${(a.sizeBytes/1024).toFixed(1)} KB`;files.append(link);}
package.json+1−1
@@ -2,7 +2,7 @@   "slug": "component-widgets",   "title": "Component Widgets",   "type": "app",-  "version": "1.1.6",+  "version": "1.1.7",   "description": "Reusable, dependency-free web-component widgets to drop into any component or molecule page: <adom-pinout> (datasheet-style pin diagram + synced table), <adom-heatsink-via> (exposed-pad thermal via array, only when the part has an EP), <adom-molecule> (ganged part set + nets), and <adom-chip-thumbnail> (rendered part image). Each is one custom element, self-contained (Shadow DOM, no deps), theme-aware (light + dark), fed by inline JSON or el.data, and all on one lbr-hover cross-highlight bus so a pin hovered in one lights the same pin everywhere. Symbol, footprint, and 3D stay with adom-lbr; the datasheet renders via the native viewer. Installs the widget JS, iframe-embeddable viewers, example data, and an AI skill to ~/.claude/skills/component-widgets. Live reference page renders them all as a readme.html.",   "license": "MIT",   "dependencies": {},
page.json+1−1
@@ -4,7 +4,7 @@   "type": "app",   "title": "Component Widgets",   "brief": "One home for the reusable, readme-instantiable component/molecule widgets (pinout, heat-sink via, chip thumbnail, molecule) \u2014 self-contained web components on the shared adom-lbr hover bus.",-  "version": "1.1.6",+  "version": "1.1.7",   "visibility": {     "public": true   },
readme.html+1−1
@@ -1181,5 +1181,5 @@ h2 .anchor:hover{opacity:1;color:var(--teal)}   if (!customElements.get('adom-moleculize')) customElements.define('adom-moleculize', AdomMoleculize); })(); </script>-<section style="padding:20px;border:1px solid #30363d;border-radius:12px;margin:24px 0"><h2>Reuse reviewed chip artwork</h2><p>The chip-thumbnail widget now accepts a cached-artwork manifest as well as an image. It shows shaded previews, vector outlines, download links, exact virtual MPN placement, Z-up orientation and review state. Existing image-only pages continue to work.</p><p>Read the component’s <code>artwork/MPN-thumbs.json</code> before generating anything. Compare source hashes and reuse its reviewed placement. Hash verification is recorded at publication time; native pin-1 and model-seating qualification remain separate.</p><pre>::adom-chip-thumbnail{src="/OWNER/COMPONENT/render/artwork/MPN-thumbs.json"}</pre></section></body>+<section style="padding:20px;border:1px solid #30363d;border-radius:12px;margin:24px 0"><h2>Reuse reviewed chip artwork</h2><p>The chip-thumbnail widget now accepts a cached-artwork manifest as well as an image. Its cache mode shows vector chip outlines, download links, exact virtual MPN placement, Z-up orientation and review state. The page’s existing 3D viewer shows the full chip. Existing image-only pages continue to work.</p><p>Read the component’s <code>artwork/MPN-thumbs.json</code> before generating anything. Compare source hashes and reuse its reviewed placement. Hash verification is recorded at publication time; native pin-1 and model-seating qualification remain separate.</p><pre>::adom-chip-thumbnail{src="/OWNER/COMPONENT/render/artwork/MPN-thumbs.json"}</pre></section></body> </html>
skills/component-widgets/SKILL.md+6−1
@@ -124,7 +124,7 @@ manifest from the component repository: ::adom-chip-thumbnail{src="/OWNER/COMPONENT/render/artwork/MPN-thumbs.json"} ``` -It shows cached shaded renders, vector outlines, editable source files, exact MPN+Its cache mode focuses on vector chip outlines, editable source files, exact MPN placement, canonical Z-up orientation, source hashes and review state. Legacy PNG/image `src` usage still works. The element's `selfCheck()` returns status, cache key, review state, selected asset and AI reuse hints.@@ -143,3 +143,8 @@ custom HTML README or per-component JavaScript fork is needed. Never broaden page visibility to make a private manifest load. Test the actual widget frame with the intended reader's permissions. Manifest asset URLs must remain in the component's repository on the manifest origin.++The cache widget focuses on chip outlines and their reuse. Do not repeat shaded+chip renders here: the component page already has its full 3D viewer. Existing+shaded thumbnail files can stay cached for library/card consumers without being+displayed in this widget. Legacy image-only widget usage is unchanged.
skills/component-widgets/widgets/chip-thumbnail-widget.js+4−4
@@ -39,14 +39,14 @@     }     _cache(d,src){       if(!Array.isArray(d.assets)||d.assets.length>100)throw new Error('Invalid asset list');-      const assets=d.assets.map(a=>({...a,url:this._url(a.path,src)}));-      const previews=assets.filter(a=>['png','jpg','jpeg','svg'].includes(a.format)&&a.kind!=='review-evidence');+      const assets=d.assets.filter(a=>a.kind!=='chip-shaded').map(a=>({...a,url:this._url(a.path,src)}));+      const previews=assets.filter(a=>a.kind==='chip-outline'&&a.format==='svg');       if(!previews.length)throw new Error('No cached previews');       const verified=d.verification?.status==='matched-at-publication',review=d.review||{};       this._state={ok:true,mode:'cache',mpn:d.mpn,cacheKey:d.cacheKey,assets:assets.length,sourceVerification:verified?'matched-at-publication':'not-verified',review,hints:d.hints||[],manifest:src};-      this.shadowRoot.innerHTML=`<style>${CSS}</style><section class="cache"><h3>Chip-thumbnailer cache</h3><div class="muted">${esc(d.mpn)} · Reuse the prepared chip artwork and its reviewed placement.</div><div class="badges"><span class="badge">${esc(d.recipe?.upAxis||'Unknown')}-up display</span><span class="badge">${assets.length} cached files</span><span class="badge">${verified?'Hashes matched when published':'Source check not recorded'}</span></div><div class="row"><div><div class="preview"><img alt="${esc(d.mpn)} cached chip artwork"></div><div class="controls variants"></div><div class="controls"><button class="zoom">Inspect closer</button><button class="reset">Fit</button></div></div><div><dl><dt>Virtual MPN placement</dt><dd>${esc(d.marking?.placement)}</dd><dt>Artwork review</dt><dd>${esc(review.projectedArtwork||'Not recorded')}</dd><dt>Display orientation</dt><dd>${esc(review.orientation)}</dd><dt>Source freshness</dt><dd class="muted">${verified?'Source and asset hashes matched '+esc(d.verification.checkedAtIso)+'.':'Source hashes have not been checked.'} This records a point-in-time check; compare the manifest hashes before reuse.</dd></dl><details><summary>Reuse files and generation recipe</summary><div class="files"></div><pre>${esc((d.hints||[]).join('\n'))}</pre><code>Cache key: ${esc(d.cacheKey)}</code></details></div></div></section>`;+      this.shadowRoot.innerHTML=`<style>${CSS}</style><section class="cache"><h3>Chip outline cache</h3><div class="muted">${esc(d.mpn)} · Reuse reviewed chip outlines in symbols and libraries.</div><div class="badges"><span class="badge">${esc(d.recipe?.upAxis||'Unknown')}-up display</span><span class="badge">${assets.length} cached files</span><span class="badge">${verified?'Hashes matched when published':'Source check not recorded'}</span></div><div class="row"><div><div class="preview"><img alt="${esc(d.mpn)} cached chip artwork"></div><div class="controls variants"></div><div class="controls"><button class="zoom">Inspect closer</button><button class="reset">Fit</button></div></div><div><dl><dt>Virtual MPN placement</dt><dd>${esc(d.marking?.placement)}</dd><dt>Artwork review</dt><dd>${esc(review.projectedArtwork||'Not recorded')}</dd><dt>Display orientation</dt><dd>${esc(review.orientation)}</dd><dt>Source freshness</dt><dd class="muted">${verified?'Source and asset hashes matched '+esc(d.verification.checkedAtIso)+'.':'Source hashes have not been checked.'} This records a point-in-time check; compare the manifest hashes before reuse.</dd></dl><details><summary>Reuse files and generation recipe</summary><div class="files"></div><pre>${esc((d.hints||[]).join('\n'))}</pre><code>Cache key: ${esc(d.cacheKey)}</code></details></div></div></section>`;       const img=this.shadowRoot.querySelector('img'),controls=this.shadowRoot.querySelector('.variants');-      previews.forEach((a,i)=>{const b=document.createElement('button');b.textContent=a.label||a.style||a.kind;b.setAttribute('aria-pressed',String(i===0));b.onclick=()=>{img.src=a.url;img.style.transform='';controls.querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));this._state.selected=a.path;};controls.append(b);});+      previews.forEach((a,i)=>{const b=document.createElement('button');b.textContent=({'symbol':'Schematic ink','teal':'Teal','dark':'Dark ink'})[a.style]||a.label||a.style||a.kind;b.setAttribute('aria-pressed',String(i===0));b.onclick=()=>{img.src=a.url;img.style.transform='';controls.querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));this._state.selected=a.path;};controls.append(b);});       img.src=previews[0].url;img.onload=()=>this._height();img.onerror=()=>{this._state.ok=false;this._state.error='Preview failed to load';img.alt='Cached preview unavailable';this._height();};       this.shadowRoot.querySelector('.zoom').onclick=()=>{img.style.transform='scale(1.55)';};this.shadowRoot.querySelector('.reset').onclick=()=>{img.style.transform='';};       const files=this.shadowRoot.querySelector('.files');for(const a of assets){const link=document.createElement('a');link.href=a.url.replace(/https:\/\/wiki\.adom\.inc\/([^/]+)\/([^/]+)\/render\//,'https://wiki.adom.inc/api/pages/$1/$2/files/');link.download=a.path.split('/').pop();link.textContent=`${a.path.split('/').pop()} · ${(a.sizeBytes/1024).toFixed(1)} KB`;files.append(link);}
widgets/chip-thumbnail-widget.js+4−4
@@ -39,14 +39,14 @@     }     _cache(d,src){       if(!Array.isArray(d.assets)||d.assets.length>100)throw new Error('Invalid asset list');-      const assets=d.assets.map(a=>({...a,url:this._url(a.path,src)}));-      const previews=assets.filter(a=>['png','jpg','jpeg','svg'].includes(a.format)&&a.kind!=='review-evidence');+      const assets=d.assets.filter(a=>a.kind!=='chip-shaded').map(a=>({...a,url:this._url(a.path,src)}));+      const previews=assets.filter(a=>a.kind==='chip-outline'&&a.format==='svg');       if(!previews.length)throw new Error('No cached previews');       const verified=d.verification?.status==='matched-at-publication',review=d.review||{};       this._state={ok:true,mode:'cache',mpn:d.mpn,cacheKey:d.cacheKey,assets:assets.length,sourceVerification:verified?'matched-at-publication':'not-verified',review,hints:d.hints||[],manifest:src};-      this.shadowRoot.innerHTML=`<style>${CSS}</style><section class="cache"><h3>Chip-thumbnailer cache</h3><div class="muted">${esc(d.mpn)} · Reuse the prepared chip artwork and its reviewed placement.</div><div class="badges"><span class="badge">${esc(d.recipe?.upAxis||'Unknown')}-up display</span><span class="badge">${assets.length} cached files</span><span class="badge">${verified?'Hashes matched when published':'Source check not recorded'}</span></div><div class="row"><div><div class="preview"><img alt="${esc(d.mpn)} cached chip artwork"></div><div class="controls variants"></div><div class="controls"><button class="zoom">Inspect closer</button><button class="reset">Fit</button></div></div><div><dl><dt>Virtual MPN placement</dt><dd>${esc(d.marking?.placement)}</dd><dt>Artwork review</dt><dd>${esc(review.projectedArtwork||'Not recorded')}</dd><dt>Display orientation</dt><dd>${esc(review.orientation)}</dd><dt>Source freshness</dt><dd class="muted">${verified?'Source and asset hashes matched '+esc(d.verification.checkedAtIso)+'.':'Source hashes have not been checked.'} This records a point-in-time check; compare the manifest hashes before reuse.</dd></dl><details><summary>Reuse files and generation recipe</summary><div class="files"></div><pre>${esc((d.hints||[]).join('\n'))}</pre><code>Cache key: ${esc(d.cacheKey)}</code></details></div></div></section>`;+      this.shadowRoot.innerHTML=`<style>${CSS}</style><section class="cache"><h3>Chip outline cache</h3><div class="muted">${esc(d.mpn)} · Reuse reviewed chip outlines in symbols and libraries.</div><div class="badges"><span class="badge">${esc(d.recipe?.upAxis||'Unknown')}-up display</span><span class="badge">${assets.length} cached files</span><span class="badge">${verified?'Hashes matched when published':'Source check not recorded'}</span></div><div class="row"><div><div class="preview"><img alt="${esc(d.mpn)} cached chip artwork"></div><div class="controls variants"></div><div class="controls"><button class="zoom">Inspect closer</button><button class="reset">Fit</button></div></div><div><dl><dt>Virtual MPN placement</dt><dd>${esc(d.marking?.placement)}</dd><dt>Artwork review</dt><dd>${esc(review.projectedArtwork||'Not recorded')}</dd><dt>Display orientation</dt><dd>${esc(review.orientation)}</dd><dt>Source freshness</dt><dd class="muted">${verified?'Source and asset hashes matched '+esc(d.verification.checkedAtIso)+'.':'Source hashes have not been checked.'} This records a point-in-time check; compare the manifest hashes before reuse.</dd></dl><details><summary>Reuse files and generation recipe</summary><div class="files"></div><pre>${esc((d.hints||[]).join('\n'))}</pre><code>Cache key: ${esc(d.cacheKey)}</code></details></div></div></section>`;       const img=this.shadowRoot.querySelector('img'),controls=this.shadowRoot.querySelector('.variants');-      previews.forEach((a,i)=>{const b=document.createElement('button');b.textContent=a.label||a.style||a.kind;b.setAttribute('aria-pressed',String(i===0));b.onclick=()=>{img.src=a.url;img.style.transform='';controls.querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));this._state.selected=a.path;};controls.append(b);});+      previews.forEach((a,i)=>{const b=document.createElement('button');b.textContent=({'symbol':'Schematic ink','teal':'Teal','dark':'Dark ink'})[a.style]||a.label||a.style||a.kind;b.setAttribute('aria-pressed',String(i===0));b.onclick=()=>{img.src=a.url;img.style.transform='';controls.querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));this._state.selected=a.path;};controls.append(b);});       img.src=previews[0].url;img.onload=()=>this._height();img.onerror=()=>{this._state.ok=false;this._state.error='Preview failed to load';img.alt='Cached preview unavailable';this._height();};       this.shadowRoot.querySelector('.zoom').onclick=()=>{img.style.transform='scale(1.55)';};this.shadowRoot.querySelector('.reset').onclick=()=>{img.style.transform='';};       const files=this.shadowRoot.querySelector('.files');for(const a of assets){const link=document.createElement('a');link.href=a.url.replace(/https:\/\/wiki\.adom\.inc\/([^/]+)\/([^/]+)\/render\//,'https://wiki.adom.inc/api/pages/$1/$2/files/');link.download=a.path.split('/').pop();link.textContent=`${a.path.split('/').pop()} · ${(a.sizeBytes/1024).toFixed(1)} KB`;files.append(link);}