app
Adom Library
Public Made by Adomby adom
adom-lbr — the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad ⇄ Altium ⇄ EAGLE/Fusion) through one canonical adom-lbr JSON — symbol + footprint + 3D + pin↔pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.
← Commit history
2.11.9: colon-name strip, title-verified evidence, rescan recovery, Demo in the Say card
7 files changed
+60−35
Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-lbr"-version = "2.11.8"+version = "2.11.9" dependencies = [ "altium-codec", "anyhow",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-lbr"-version = "2.11.8"+version = "2.11.9" edition = "2021" description = "adom-lbr generator / EDA translator: the canonical adom-lbr JSON is the hub, with native converters to Altium (.SchLib/.PcbLib/.IntLib), EAGLE/Fusion (.lbr), and KiCad. Altium output is written natively — no Altium needed at conversion time."
VERSION+1−1@@ -1 +1 @@-2.11.8+2.11.9
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-lbr",- "version": "2.11.8",+ "version": "2.11.9", "type": "app", "description": "adom-lbr \u2014 the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad \u21c4 Altium \u21c4 EAGLE/Fusion) through one canonical adom-lbr JSON \u2014 symbol + footprint + 3D + pin\u2194pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.", "dependencies": {},
page.json+9−12@@ -1,8 +1,8 @@ { "slug": "adom-lbr",- "version": "2.11.8",+ "version": "2.11.9", "type": "app",- "description": "adom-lbr — the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad ⇄ Altium ⇄ EAGLE/Fusion) through one canonical adom-lbr JSON — symbol + footprint + 3D + pin↔pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.",+ "description": "adom-lbr \u2014 the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad \u21c4 Altium \u21c4 EAGLE/Fusion) through one canonical adom-lbr JSON \u2014 symbol + footprint + 3D + pin\u2194pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.", "dependencies": {}, "tags": [ "eda",@@ -52,24 +52,21 @@ ], "files": [ "bin/**",- "assets/**", "SKILL.md",- "skills/**", "README.md", "install.sh", "uninstall.sh",- "screenshots/**",- "dockbar.json"+ "screenshots/**" ],- "discovery_pitch": "Convert electronic-component symbols + footprints between any two EDA tools — KiCad, Altium (written natively, no Altium needed), and EAGLE/Fusion — through one canonical adom-lbr JSON, silk/courtyard/fab layers preserved. Plus a Manager to browse and inspect any wiki part.",+ "discovery_pitch": "Convert electronic-component symbols + footprints between any two EDA tools \u2014 KiCad, Altium (written natively, no Altium needed), and EAGLE/Fusion \u2014 through one canonical adom-lbr JSON, silk/courtyard/fab layers preserved. Plus a Manager to browse and inspect any wiki part.", "sample_prompts": [ { "prompt": "Convert this KiCad symbol and footprint to an Altium SchLib and PcbLib",- "label": "KiCad → Altium"+ "label": "KiCad \u2192 Altium" }, { "prompt": "Import this Altium footprint into the adom-lbr JSON",- "label": "Altium → JSON"+ "label": "Altium \u2192 JSON" }, { "prompt": "Bundle this part into a single Altium IntLib",@@ -77,15 +74,15 @@ }, { "prompt": "Convert this Altium SchLib and PcbLib to a KiCad symbol and footprint",- "label": "Altium → KiCad"+ "label": "Altium \u2192 KiCad" }, { "prompt": "Export this adom-lbr JSON to a KiCad .kicad_sym and .kicad_mod",- "label": "JSON → KiCad"+ "label": "JSON \u2192 KiCad" }, { "prompt": "Open the adom-lbr Manager to browse wiki components and view the layer stackup", "label": "Browse parts" } ]-}+}
src/manage.html+36−18@@ -262,7 +262,6 @@ <div class="brand"><img src="favicon.svg" alt="">adom-lbr <small>Dashboard</small></div> <div class="leds" id="leds" title="live status from /api/status"></div> <span class="ver" id="ver"></span>- <button id="btnDemo" title="A guided tour: the dashboard drives itself and shows you everything it can do"><svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path fill="currentColor" d="M8 5v14l11-7z"/></svg>Demo</button> <button class="iconbtn" id="btnSettings" title="Settings" aria-label="Settings"><svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"><path fill="currentColor" d="M12 15.5A3.5 3.5 0 0 1 8.5 12A3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5a3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.64.07-.97s-.03-.66-.07-1l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65A.506.506 0 0 0 14 2h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 11c-.04.34-.07.67-.07 1s.03.65.07.97l-2.11 1.66c-.19.15-.25.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1.01c.52.4 1.06.74 1.69.99l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.26 1.17-.59 1.69-.99l2.49 1.01c.22.08.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64z"/></svg></button> </header> <nav id="sendbar" aria-label="Send the open part to your EDA tools">@@ -283,6 +282,7 @@ <section id="say" aria-labelledby="sayTitle" hidden> <button class="iconbtn say-close" id="sayClose" aria-label="Hide the Say it strip" title="Hide this strip. It stays hidden until you turn it back on in Settings (Say it strip)."><svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button> <div class="say-head"><h2 id="sayTitle">Say it to your AI</h2>+ <button id="btnDemo" title="A guided tour: the dashboard drives itself and shows you everything it can do"><svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path fill="currentColor" d="M8 5v14l11-7z"/></svg>Demo</button> <span class="say-sub">You do not drive this dashboard by hand. You ask your AI, using the word <b>lbr</b>. Click a phrase (or Try it) to copy it, then paste it into any Adom AI thread.</span></div> <div class="say-list" id="phrases"></div> </section>@@ -957,14 +957,22 @@ function findShots(o,out,depth){out=out||[];depth=depth||0; if(typeof (o.thumbnailDataUrl||o.image||o.base64||o.png)==='string'&&(o.thumbnailDataUrl||o.image||o.base64||o.png).length>200)out.push(o); else for(const v of Object.values(o))findShots(v,out,depth+1); return out;}-async function kicadEvidence(label){- // FINAL frames: ask KiCad for its window state with images after the chain lands- sp.step('capturing evidence…');- const r=await abVerb('kicad_state',{includeImages:true});- const shots=findShots(r.j);- if(!shots.length){sp.log(' (no evidence frames came back from kicad_state)');return;}- shots.slice(0,4).forEach(x=>sp.shot(x.thumbnailDataUrl||x.image||x.base64||x.png,x.title||label));- sp.log(' evidence: '+shots.length+' frame(s) rendered below');+// Evidence honesty (ab demo doctrine, John 2026-08-20): render ONLY the frames+// the verb returned FOR THIS REQUEST, and verify their titles actually name the+// part. Never screenshot "whatever KiCad is showing" - that is how a picture of+// the WRONG part ends up under a green tick.+function renderEvidence(frames,expectTokens,cmd){+ if(!frames.length){sp.log(' (no evidence frames came back from '+cmd+'; check the KiCad window itself)');return true;}+ let allVerified=true;+ frames.slice(0,4).forEach(x=>{+ const title=x.title||x.step||'frame';+ const hit=expectTokens.some(t=>t&&title.toLowerCase().includes(t.toLowerCase()));+ if(!hit)allVerified=false;+ sp.shot(x.thumbnailDataUrl||x.image||x.base64||x.png,(hit?'':'⚠ unverified: ')+title);+ });+ if(allVerified)sp.log(' evidence: '+frames.length+' frame(s), titles name the part');+ else sp.log(' WARNING: some evidence titles do not name this part; the editor may be showing something else');+ return allVerified; } // ---- KiCad: install once per part, then the show verbs ----@@ -1002,15 +1010,25 @@ async function sendKicad(kind){ const [cmd,args]=shows[kind]; sp.bar(80);sp.step('opening in KiCad…'); sp.log(cmd+' '+JSON.stringify(args));- const r=await abVerb(cmd,args);- if(!(r.s===200&&r.j&&r.j.success!==false&&!r.j.errorCode))throw new Error(cmd+': '+((r.j&&(r.j.error||r.j.errorCode))||r.s));- sp.log(' '+cmd+' ok');- // the show verb may carry the final frames itself; else ask kicad_state- const inline=findShots(r.j);- if(inline.length){inline.slice(0,4).forEach(x=>sp.shot(x.thumbnailDataUrl||x.image||x.base64||x.png,x.title||kind));sp.log(' evidence: '+inline.length+' frame(s) from '+cmd);}- else await kicadEvidence(kind.replace('kicad-',''));- sp.bar(100);sp.step('done — open in KiCad');- toast('Open in KiCad: '+kind.replace('kicad-',''),false,'success');+ let r=await abVerb(cmd,args);+ const failed=x=>!(x.s===200&&x.j&&x.j.success!==false&&!x.j.errorCode);+ if(failed(r)&&r.j&&(r.j.errorCode==='library_cache_stale'||String(r.j.error||'').includes('indexed its libraries'))){+ // The open editor indexed its libraries before our install. The bridge's+ // own remedy: rescan:true reopens the editor to re-index (~1 minute cold+ // library scan; it never closes a HUMAN-opened editor without this).+ sp.log(' editor library index is stale; reopening with rescan:true (the cold re-index takes about a minute)');+ sp.step('re-indexing KiCad’s libraries (about a minute)…');sp.bar(null);+ r=await abVerb(cmd,Object.assign({rescan:true},args));+ }+ if(failed(r)){+ if(r.j&&r.j._hint)sp.log(' hint: '+r.j._hint);+ throw new Error(cmd+': '+((r.j&&(r.j.error||r.j.errorCode))||r.s));+ }+ sp.log(' '+cmd+' ok'+((r.j&&r.j.autoRescanned)?' (editor re-indexed)':''));+ const bare=n=>String(n||'').split(':').pop();+ const verified=renderEvidence(findShots(r.j),[bare(k.symbol),k.footprint,k.lib+':'],cmd);+ sp.bar(100);sp.step(verified?'done — open in KiCad':'done, but verify the KiCad window yourself');+ toast('Open in KiCad: '+kind.replace('kicad-',''),false,verified?'success':undefined); } // ---- Fusion: one call builds + proves the library, then poll the verdict ---- async function sendFusion(){
src/manage.rs+11−1@@ -1161,10 +1161,20 @@ fn export_part(slug: &str) -> Value { return json!({"status":"error","error":format!("invalid slug: {slug:?}")}); } let mpn = up(slug);- let (part, src) = match load_part(slug, &mpn) {+ let (mut part, src) = match load_part(slug, &mpn) { Ok(v) => v, Err(e) => return json!({"status":"error","error":e}), };+ // Wiki sources often carry a lib-qualified symbol name ("MCU_ST_STM32H5:+ // STM32H563RGTx"). Shipping the colon into the installed library breaks+ // every downstream search (KiCad's editor reads it as lib:name and can+ // never match it, which presented as an unfixable library_cache_stale on+ // AdomLapper, 2026-08-23). Strip to the bare name.+ if let Some(tail) = part.symbol.name.rsplit(':').next().map(String::from) {+ if !tail.is_empty() && tail != part.symbol.name {+ part.symbol.name = tail;+ }+ } let sym_txt = export_kicad_symbol(&part.symbol); let fp_txt = export_kicad_footprint(&part.footprint); // pin->pad map from the hub's connect[] (only the disagreements matter)