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 + footp
name: adom-lbr
description: >
adom-lbr — the EDA library translator. Bring a component in from ANY supported
EDA tool and convert it to ANY other; the canonical adom-lbr JSON (symbol +
footprint + 3D + pin↔pad map) is the hub. Import from KiCad OR Altium; export to
NATIVE Altium (.SchLib/.PcbLib/.IntLib — real OLE2 binary, no Altium needed),
KiCad (.kicad_sym + .kicad_mod), or EAGLE/Fusion .lbr. Merge a symbol into an
existing .SchLib. adom-lbr manage opens the Manager: browse wiki components,
view symbol/footprint/3D + the per-EDA (KiCad vs Altium) footprint layer stackup.
Trigger words: adom-lbr, altium schlib pcblib intlib, convert to altium, kicad to
altium, altium to kicad, any eda to any eda, eda translator, lbr, eagle library,
fusion library, adom-lbr json, kicad convert, adom-lbr manager, layer stackup.
adom-lbr
adom-lbr generator / EDA translator. The adom-lbr JSON — Adom's own canonical part format (symbol + footprint + 3D + pin↔pad map) — is the hub; every EDA format is a converter around it.
- Import a KiCad
.kicad_sym+.kicad_modinto the adom-lbr JSON. - Export the adom-lbr JSON to native Altium
.SchLib/.PcbLib/.IntLib(real OLE2/CFBF binary, no Altium at conversion time), an EAGLE/Fusion.lbr, or a KiCad.kicad_sym. - Merge a part's symbol into an existing
.SchLib(install into a user's Altium library).
("lbr" here = Adom Library / the adom-lbr JSON, not EAGLE's .lbr — that's just one output.)
⚡ Altium user? The reliable workflow — READ THIS FIRST
Helping someone add parts to their real Altium library? The native generators (export-altium,
export-intlib, encode-sym) have burned entire sessions producing symbols/footprints that render
catastrophically wrong in Altium with NO error — pins dropped or flung thousands of mils away,
footprints with copper pads and nothing else. Prefer Altium-authored bytes over generation. The
golden path — quick AND correct:
- Get genuine bytes; don't generate.
- From a vendor
.IntLibthe user provides — the reliable source. It's an OLE2 file whoseSchLib/0.schlib+PCBLib/0.pcblibstreams are zlib-compressed at offset 1 (zlib.decompress(data[1:])) → real Altium.SchLib/.PcbLibthat render perfectly. Extract and use them directly. (adom-lbr import-altium foo.IntLib --mpn Xalso ingests one.) This needs only Pythonolefile+zlib— no special binary — so it ALWAYS works. - Many similar parts? Have the user perfect ONE in Altium and save it, then byte-clone the rest
(copy the component's
Datastream verbatim, edit only the name/value/footprint text records). - Footprints with real silk / fab / courtyard / 3D: use genuine footprints from IntLibs. The pad generator writes copper ONLY (see the footprint reality-check below).
- From a vendor
- Standard package → standard KiCad footprint (SOT-223, 0402/0603/0805), never a ds2sf datasheet extraction (it mis-lays pads).
- Back up the user's file BEFORE overwriting it (
pull_file+ keep a copy). Originals have been lost this way with no recovery. - Deploy with
adom-desktop send_files,dest= the user'sC:\Users\<user>\Documents\Altium\Libraries\{Symbols,Footprints}\folder. Verify by pulling it back and comparing sha256. - Tell the user to RESTART Altium. It caches file-based libraries and freezes already-placed parts;
most "it's still broken/old" reports are stale caches. If the lib is open with unsaved edits (title-bar
*), close WITHOUT saving — a Save clobbers your deployed file. - Verify in Altium's Properties panel, not your own parser. Reader and writer can agree with each other and both be wrong (a 79-mil pin read as 66,630 mil). Get this ground truth EARLY.
Everything below is detail. The two must-read deep sections: "⚠️ Altium .SchLib symbols — READ FIRST" and the footprint reality-check callout.
Native Altium commands (both directions)
# INTO the hub
adom-lbr import-kicad --sym part.kicad_sym --fp part.kicad_mod --mpn MPN # KiCad → MPN.adom-lbr.json
adom-lbr import-altium part.SchLib --pcblib part.PcbLib --mpn MPN # Altium → MPN.adom-lbr.json
adom-lbr import-altium part.IntLib --mpn MPN # or one bundled .IntLib
# OUT of the hub → native Altium (real OLE2/CFBF, no Altium needed)
adom-lbr export-altium part.adom-lbr.json --out-dir . # → MPN.SchLib + MPN.PcbLib
adom-lbr export-intlib part.adom-lbr.json # → MPN.IntLib (bundled)
adom-lbr export-kicad part.adom-lbr.json --out-dir . # → MPN.kicad_sym + MPN.kicad_mod
adom-lbr add-to-schlib existing.SchLib part.adom-lbr.json --out lib.SchLib # merge a symbol into a library
What carries across (both ways): symbol pins + body graphics; footprint pads; footprint silk / courtyard / fab graphics with their layers; the pin↔pad map; the footprint link. Value/Manufacturer/MPN/Package become Altium component property fields (Value drawn on the symbol; the rest hidden), and are read back out on import.
⚠️ Reality check on the CLI (
altium-codecbinary) — verified 2026-07-13. The neutralFootprintmodel is{name, pads}with no graphics fields, soencode-fp/encode-fp-many(andimport-kicad, which dropsF.SilkS/F.Fab/F.CrtYd) emit copper pads ONLY — no silk, fab, courtyard, keepout, or 3D. The "graphics carry across" claim above is the intended full pipeline, NOT what the CLI does today. If the user needs real footprint layers, do not generate — extract genuine full footprints from vendor.IntLibs (streams zlib-compressed at offset 1) and combine them withaltium-codec merge-fp-libs <out.PcbLib> <spec.json>(spec =[{pcblib,name}]): it byte-copies each footprint's primitives (pads + tracks on Mech13 fab / Mech15 courtyard + text) and its embedded STEP model verbatim, renaming only the footprint so existing symbol links still resolve. Same clone-don't-generate rule as symbols (see the READ-FIRST symbol section below).
Footprint layer mapping (Altium)
Graphics carry a neutral layer role (silk / courtyard / fab), mapped on
export to the house convention (from the C0402 reference footprint):
| Role | Altium layer | Named |
|---|---|---|
| fab + silk (outline) | Mechanical 13 | Assembly Top |
| courtyard | Mechanical 15 | Courtyard Top |
| pads | Top Layer | — |
Altium stores a track/arc layer in TWO places — the layer byte and a tail
class-pair [41]/[43] (Track) — and it renders from the tail pair, so the
encoder patches both. Decode reads the layers back into the neutral roles.
3D STEP models attach to the footprint via Altium's model factory (bridge), not
the native writer (the model checksum is a custom Altium algorithm).
KiCad round-trip fidelity
export-kicad writes a KiCad-valid .kicad_sym + .kicad_mod. Import captures
what a naive converter drops: rectangle fill (fill outline vs none — a
filled cap plate stays filled), and per-symbol pin-number / pin-name
visibility ((pin_numbers (hide yes))), so a KiCad→JSON→KiCad round trip keeps
solid vs hollow art and shown vs hidden pin labels. Footprint silk/courtyard/fab
graphics keep their layers → F.SilkS / F.CrtYd / F.Fab.
adom-lbr Manager (adom-lbr manage)
A web app (tiny_http → webview) that browses wiki components and inspects any
part. adom-lbr manage --port 8785 → open http://localhost:8785.
- List ▸ click ▸ viewer. Lists every component on the wiki (wiki API); click a row to open its viewer.
- On-the-fly render. Fetches the part FRESH each click — prefers the page's
canonical adom-lbr JSON, else generates it from the page's
.kicad_sym+.kicad_mod— and renders symbol + footprint + 3D on the spot. - Layer stackup. A table of the footprint's layers showing what each becomes
in KiCad and Altium (e.g. Courtyard →
F.CrtYd/ Mechanical 15). Plus a per-layer show/hide filter on the footprint. - Interaction. Drag-to-pan, scroll-to-zoom, double-click-reset on the symbol & footprint; hover a pin or pad to highlight it and read its number + name.
- Served relative-path so it works behind the code-server
/proxy/<port>/proxy; open it as a webview viaadom-cli hydrogen webview open-or-refresh.
EAGLE / Fusion path
Fusion 360 Electronics shows "No Components" when an .lbr has an empty <packages> or <connects/>. adom-lbr's generate builds a COMPLETE deviceset: symbol + package + full pin-to-pad connects. KiCad users can round-trip any .lbr back to .kicad_sym with adom-lbr import.
Commands
adom-lbr generate --sym <path.kicad_sym> [-o output.lbr] [--fp <path.kicad_mod>] [--name <override>]
adom-lbr import <path.lbr> [-o output.kicad_sym]
adom-lbr lint <path.lbr>
adom-lbr validate <path.lbr>
adom-lbr check <path.kicad_sym>
adom-lbr manage [--port 8785] # the adom-lbr Manager web app
adom-lbr health
adom-lbr install
adom-lbr completions <bash|zsh|fish>
generate
Parses a KiCad .kicad_sym file and produces a valid EAGLE .lbr with:
- Symbol (body rectangle + pins with correct direction/rotation/length)
- Package (placeholder SMD pads for each pin number, courtyard outline)
- Deviceset with gate, device, and complete
<connect>mappings
Auto-lints the output before writing. Exits 2 if lint fails.
import
Parses an EAGLE .lbr and converts to KiCad .kicad_sym format. Handles:
- Pin direction mapping (EAGLE pwr/in/out/io/pas/nc → KiCad power_in/input/output/bidirectional/passive/unconnected)
- Pin rotation, length, position
- Body rectangle from layer 94 wires
- Description, reference prefix
- Pin numbers from connect mappings
- Strips EAGLE @N dedup suffixes back to clean names
lint
Checks an existing .lbr for Fusion 360 compatibility. Outputs JSON with passed, errors[], warnings[].
Checks:
empty-packages— packages section empty (fatal: Fusion shows "No Components")empty-connects— device has no connect mappings (fatal)device-no-package— device element missing package attribute (fatal)duplicate-pin-name— EAGLE requires unique pin names; use @suffix for power/NC duplicatesconnect-pad-missing— connect references a pad that doesn't exist in the packagemalformed-xml— unbalanced angle bracketsno-symbol-layer— nothing on layer 94 (symbol body invisible)
validate
Same as lint but human-readable output. Exits 0 = valid, 2 = errors found.
check
Validates a .kicad_sym file by local Rust validation — checks structure, pins, properties, duplicate names. Use after import to verify the output before sending to a desktop KiCad instance.
Duplicate pin handling
EAGLE requires unique pin names. When KiCad has multiple pins with the same name (e.g., 6 IOVDD pins), the generator appends @N suffixes: IOVDD, IOVDD@2, IOVDD@3, etc. The import command strips these back.
Workflows
Fusion 360 user (has .kicad_sym, needs .lbr)
adom-lbr generate --sym RP2040.kicad_sym -o RP2040.lbr
# Send to Fusion via adom-desktop send_files + fusion_open_lbr
KiCad user (has .lbr, needs .kicad_sym)
adom-lbr import RP2040.lbr -o RP2040.kicad_sym
adom-lbr check RP2040.kicad_sym
# Send to KiCad via adom-desktop send_files + kicad_install_library
Pipeline (adom-symbol → both targets)
adom-symbol create <mpn> → .kicad_sym
adom-lbr generate --sym <mpn>.kicad_sym → .lbr (for Fusion)
# .kicad_sym already native for KiCad
Lint any .lbr before use
adom-lbr lint suspicious.lbr
# JSON output with error codes for programmatic handling
⚠️ Altium .SchLib symbols — READ FIRST (hard-won 2026-07-10..13)
The native Altium symbol encoder (export-altium / encode-sym / add-to-schlib)
has repeatedly produced symbols that render catastrophically wrong in real Altium with
NO error — pins dropped, or one pin shot thousands of mils off to the side — while the
bytes looked fine to our own parser. Over one session it needed four separate byte-level
fixes and was still wrong. So:
RULE 0 — DON'T generate the symbol; CLONE a known-good one.
Whenever a symbol must render in the user's real Altium, do not trust the codec's pin encoder. Instead reuse Altium-authored bytes:
- From a vendor
.IntLibthe user provides: it's an OLE2 file whoseSchLib/0.schlibandPCBLib/0.pcblibstreams are zlib-compressed at offset 1 (zlib.decompress(data[1:])) → the real.SchLib/.PcbLib. Use those directly (Altium's own format = renders perfectly). - From one symbol the user fixes in Altium + saves: then byte-clone it to the rest with
altium-codec replace-clone <in.SchLib> <src_comp> <out.SchLib> <specs.json>(specs =[{storage,libref,value,package,footprint,description}, …]). It copies the source component's Data stream verbatim (pins + graphics untouched) and edits only the metadata text records. This is the reliable fix and mirrors the machine-pin flow (user fixes one, we clone). Only generate from scratch when nothing Altium-authored exists — and then VERIFY in Altium (RULE 1) before trusting it.
RULE 1 — Verify in ALTIUM, never in your own parser.
Reader and writer can agree with each other and both be wrong. Ground truth = the Properties panel (Pin Length / Location) on the actual pin. A "Pin Length" of tens of thousands of mils = a byte bug. Get this ground truth EARLY instead of theorising for an hour.
RULE 2 — Altium CACHES file-based libraries and FREEZES placed instances.
After you overwrite a .SchLib/.PcbLib, the user must restart Altium (or the library
panel serves the stale copy) and delete any already-placed part (placed instances don't
update). Tell them this up front — most "it still looks broken/old" reports this session were
stale caches, and they cost hours. Also: a title-bar * = unsaved edits; if you overwrite the
file while it's open and the user then Saves, they clobber your fix — tell them to close
WITHOUT saving, or restart.
RULE 3 — Never call altium-bridge verbs with empty/partial args.
altium_clone_symbol / altium_build_footprint with {} time out and pop
"value cannot be null" / "save file error" dialogs inside the user's live Altium. Only
call bridge verbs with complete, correct payloads and an appropriate active doc.
RULE 4 — Back up the user's original before overwriting it.
Pull + keep a copy first. This session the user's original SnapEDA symbol got overwritten with a broken codec version and couldn't be recovered.
Byte-level reference (only if you MUST generate — and still verify in Altium)
Binary pin record, calibrated vs genuine pins (STM32F072/ADS1220/caps). NOTE: even after all
of these the encoder was STILL wrong on negative X (Altium's own save writes byte [19]=0xFF
sign-extension; the codec writes 0x00 → giant pin), so treat this as incomplete → clone.
[14]conglom (0x04).[15:17]length field — length in bits 6-15 (unit 1/25.6 mil), orient in bits 0-1, and bit 5 (0x20) is a REQUIRED flag. Genuine low bytes are only ever0x20/0x22/0x38/0x3a, never 0x00. A round length + orient 0 (100 mil → 0x0A00, low byte 0x00) → GIANT pin. Encode:field = (len & 0xFFC0) | 0x20 | orient.- X and Y must be snapped to a 10-mil grid (multiple of 256 internal units) or the low bytes bleed into neighbouring fields (off-grid X = the 66,630-mil pin). Exact X/Y widths are context-dependent and NOT fully nailed — clone instead.
- Pins before graphics; arcs → tessellated RECORD=6 polylines (a hand-rolled RECORD=12 arc dropped the pin). µ → ASCII ("uF" not "µF" — codec writes UTF-8 → shows as "µ"). Zero-width KiCad polylines are dropped by import-kicad (give leads a real width).
- The
altium-schlibREADER decodes Y at the wrong offset — don't trust it either.
Non-symbol notes
Which binary?
replace-cloneandmerge-fp-libscurrently live in the lower-levelaltium-codecdev crate (/home/adom/project/altium-writer/altium-codec/), not yet folded into the shippedadom-lbrbinary —cargo build --releasethere, or fall back to the manualolefile+zlibextraction (golden-path step 1), which needs no special binary and always works.adom-lbritself shipsimport/export-altium,export-intlib,add-to-schlib,import-altium.
encode-fp-many <[Footprint]> <out.PcbLib>builds a multi-footprint PcbLib — but writes copper pads ONLY (no silk/fab/courtyard/keepout/3D; the model is{name,pads}). Pad placement is reliable; the footprint is just bare. For real layers use genuine footprints +merge-fp-libs(see the ⚠️ reality-check note up top).merge-fp-libs <out.PcbLib> <spec.json>(spec=[{pcblib,name}]) — merge N genuine single-footprint PcbLibs into one, renaming each. Copies every primitive + embedded STEP verbatim; source[0] supplies the sharedLibrary/*boilerplate (all sources must be from the same converter). The way to get full-layer footprints into a user's library without generating them.- Symbol model has a
descriptionfield → RECORD=1 ComponentDescription. - For a STANDARD package (SOT-223, 0402/0603/0805), use the KiCad standard footprint, not a ds2sf datasheet extraction — ds2sf mis-laid the SOT-223 pads.
---
name: adom-lbr
description: >
adom-lbr — the EDA library translator. Bring a component in from ANY supported
EDA tool and convert it to ANY other; the canonical adom-lbr JSON (symbol +
footprint + 3D + pin↔pad map) is the hub. Import from KiCad OR Altium; export to
NATIVE Altium (.SchLib/.PcbLib/.IntLib — real OLE2 binary, no Altium needed),
KiCad (.kicad_sym + .kicad_mod), or EAGLE/Fusion .lbr. Merge a symbol into an
existing .SchLib. `adom-lbr manage` opens the Manager: browse wiki components,
view symbol/footprint/3D + the per-EDA (KiCad vs Altium) footprint layer stackup.
Trigger words: adom-lbr, altium schlib pcblib intlib, convert to altium, kicad to
altium, altium to kicad, any eda to any eda, eda translator, lbr, eagle library,
fusion library, adom-lbr json, kicad convert, adom-lbr manager, layer stackup.
---
# adom-lbr
adom-lbr generator / EDA translator. The **adom-lbr JSON** — Adom's own canonical part format (symbol + footprint + 3D + pin↔pad map) — is the hub; every EDA format is a converter around it.
- **Import** a KiCad `.kicad_sym` + `.kicad_mod` into the adom-lbr JSON.
- **Export** the adom-lbr JSON to **native Altium** `.SchLib` / `.PcbLib` / `.IntLib` (real OLE2/CFBF binary, no Altium at conversion time), an **EAGLE/Fusion** `.lbr`, or a **KiCad** `.kicad_sym`.
- **Merge** a part's symbol into an existing `.SchLib` (install into a user's Altium library).
("lbr" here = Adom Library / the adom-lbr JSON, not EAGLE's `.lbr` — that's just one output.)
## ⚡ Altium user? The reliable workflow — READ THIS FIRST
Helping someone add parts to their **real Altium** library? The native generators (`export-altium`,
`export-intlib`, `encode-sym`) have burned entire sessions producing symbols/footprints that render
**catastrophically wrong in Altium with NO error** — pins dropped or flung thousands of mils away,
footprints with copper pads and nothing else. **Prefer Altium-authored bytes over generation.** The
golden path — quick AND correct:
1. **Get genuine bytes; don't generate.**
- **From a vendor `.IntLib` the user provides** — the reliable source. It's an OLE2 file whose
`SchLib/0.schlib` + `PCBLib/0.pcblib` streams are **zlib-compressed at offset 1**
(`zlib.decompress(data[1:])`) → real Altium `.SchLib`/`.PcbLib` that render perfectly. Extract and
use them directly. (`adom-lbr import-altium foo.IntLib --mpn X` also ingests one.) This needs only
Python `olefile`+`zlib` — no special binary — so it ALWAYS works.
- **Many similar parts?** Have the user perfect ONE in Altium and save it, then byte-clone the rest
(copy the component's `Data` stream verbatim, edit only the name/value/footprint text records).
- **Footprints with real silk / fab / courtyard / 3D:** use genuine footprints from IntLibs. The
pad generator writes copper ONLY (see the footprint reality-check below).
2. **Standard package → standard KiCad footprint** (SOT-223, 0402/0603/0805), never a ds2sf datasheet
extraction (it mis-lays pads).
3. **Back up the user's file BEFORE overwriting it** (`pull_file` + keep a copy). Originals have been
lost this way with no recovery.
4. **Deploy** with `adom-desktop send_files`, `dest` = the user's
`C:\Users\<user>\Documents\Altium\Libraries\{Symbols,Footprints}\` folder. Verify by pulling it back
and comparing sha256.
5. **Tell the user to RESTART Altium.** It caches file-based libraries and freezes already-placed parts;
most "it's still broken/old" reports are stale caches. If the lib is open with unsaved edits (title-bar
`*`), close WITHOUT saving — a Save clobbers your deployed file.
6. **Verify in Altium's Properties panel, not your own parser.** Reader and writer can agree with each
other and both be wrong (a 79-mil pin read as 66,630 mil). Get this ground truth EARLY.
Everything below is detail. The two must-read deep sections: **"⚠️ Altium .SchLib symbols — READ FIRST"**
and the **footprint reality-check** callout.
## Native Altium commands (both directions)
```bash
# INTO the hub
adom-lbr import-kicad --sym part.kicad_sym --fp part.kicad_mod --mpn MPN # KiCad → MPN.adom-lbr.json
adom-lbr import-altium part.SchLib --pcblib part.PcbLib --mpn MPN # Altium → MPN.adom-lbr.json
adom-lbr import-altium part.IntLib --mpn MPN # or one bundled .IntLib
# OUT of the hub → native Altium (real OLE2/CFBF, no Altium needed)
adom-lbr export-altium part.adom-lbr.json --out-dir . # → MPN.SchLib + MPN.PcbLib
adom-lbr export-intlib part.adom-lbr.json # → MPN.IntLib (bundled)
adom-lbr export-kicad part.adom-lbr.json --out-dir . # → MPN.kicad_sym + MPN.kicad_mod
adom-lbr add-to-schlib existing.SchLib part.adom-lbr.json --out lib.SchLib # merge a symbol into a library
```
**What carries across (both ways):** symbol pins + body graphics; footprint pads;
**footprint silk / courtyard / fab graphics with their layers**; the pin↔pad map;
the footprint link. Value/Manufacturer/MPN/Package become Altium component
property fields (Value drawn on the symbol; the rest hidden), and are read back
out on import.
> ⚠️ **Reality check on the CLI (`altium-codec` binary) — verified 2026-07-13.** The neutral
> `Footprint` model is `{name, pads}` with **no graphics fields**, so `encode-fp` / `encode-fp-many`
> (and `import-kicad`, which drops `F.SilkS`/`F.Fab`/`F.CrtYd`) emit **copper pads ONLY** — no silk,
> fab, courtyard, keepout, or 3D. The "graphics carry across" claim above is the intended full
> pipeline, NOT what the CLI does today. If the user needs real footprint layers, **do not generate** —
> extract genuine full footprints from vendor `.IntLib`s (streams zlib-compressed at offset 1) and
> combine them with **`altium-codec merge-fp-libs <out.PcbLib> <spec.json>`** (spec = `[{pcblib,name}]`):
> it byte-copies each footprint's primitives (pads + tracks on Mech13 fab / Mech15 courtyard + text)
> **and** its embedded STEP model verbatim, renaming only the footprint so existing symbol links still
> resolve. Same clone-don't-generate rule as symbols (see the READ-FIRST symbol section below).
### Footprint layer mapping (Altium)
Graphics carry a neutral layer role (`silk` / `courtyard` / `fab`), mapped on
export to the house convention (from the C0402 reference footprint):
| Role | Altium layer | Named |
|---|---|---|
| fab + silk (outline) | Mechanical 13 | Assembly Top |
| courtyard | Mechanical 15 | Courtyard Top |
| pads | Top Layer | — |
Altium stores a track/arc layer in TWO places — the layer byte **and** a tail
class-pair `[41]/[43]` (Track) — and it *renders* from the tail pair, so the
encoder patches both. Decode reads the layers back into the neutral roles.
3D STEP models attach to the footprint via Altium's model factory (bridge), not
the native writer (the model checksum is a custom Altium algorithm).
### KiCad round-trip fidelity
`export-kicad` writes a KiCad-valid `.kicad_sym` + `.kicad_mod`. Import captures
what a naive converter drops: rectangle **fill** (`fill outline` vs `none` — a
filled cap plate stays filled), and per-symbol **pin-number / pin-name
visibility** (`(pin_numbers (hide yes))`), so a KiCad→JSON→KiCad round trip keeps
solid vs hollow art and shown vs hidden pin labels. Footprint silk/courtyard/fab
graphics keep their layers → `F.SilkS` / `F.CrtYd` / `F.Fab`.
## adom-lbr Manager (`adom-lbr manage`)
A web app (tiny_http → webview) that browses wiki components and inspects any
part. `adom-lbr manage --port 8785` → open `http://localhost:8785`.
- **List ▸ click ▸ viewer.** Lists every component on the wiki (wiki API); click a
row to open its viewer.
- **On-the-fly render.** Fetches the part FRESH each click — prefers the page's
canonical adom-lbr JSON, else generates it from the page's `.kicad_sym` +
`.kicad_mod` — and renders **symbol + footprint + 3D** on the spot.
- **Layer stackup.** A table of the footprint's layers showing what each becomes
in **KiCad and Altium** (e.g. Courtyard → `F.CrtYd` / Mechanical 15). Plus a
per-layer show/hide **filter** on the footprint.
- **Interaction.** Drag-to-pan, scroll-to-zoom, double-click-reset on the symbol
& footprint; **hover a pin or pad** to highlight it and read its number + name.
- Served relative-path so it works behind the code-server `/proxy/<port>/` proxy;
open it as a webview via `adom-cli hydrogen webview open-or-refresh`.
## EAGLE / Fusion path
Fusion 360 Electronics shows "No Components" when an .lbr has an empty `<packages>` or `<connects/>`. adom-lbr's `generate` builds a COMPLETE deviceset: symbol + package + full pin-to-pad connects. KiCad users can round-trip any `.lbr` back to `.kicad_sym` with `adom-lbr import`.
## Commands
```
adom-lbr generate --sym <path.kicad_sym> [-o output.lbr] [--fp <path.kicad_mod>] [--name <override>]
adom-lbr import <path.lbr> [-o output.kicad_sym]
adom-lbr lint <path.lbr>
adom-lbr validate <path.lbr>
adom-lbr check <path.kicad_sym>
adom-lbr manage [--port 8785] # the adom-lbr Manager web app
adom-lbr health
adom-lbr install
adom-lbr completions <bash|zsh|fish>
```
## generate
Parses a KiCad .kicad_sym file and produces a valid EAGLE .lbr with:
- Symbol (body rectangle + pins with correct direction/rotation/length)
- Package (placeholder SMD pads for each pin number, courtyard outline)
- Deviceset with gate, device, and complete `<connect>` mappings
Auto-lints the output before writing. Exits 2 if lint fails.
## import
Parses an EAGLE .lbr and converts to KiCad .kicad_sym format. Handles:
- Pin direction mapping (EAGLE pwr/in/out/io/pas/nc → KiCad power_in/input/output/bidirectional/passive/unconnected)
- Pin rotation, length, position
- Body rectangle from layer 94 wires
- Description, reference prefix
- Pin numbers from connect mappings
- Strips EAGLE @N dedup suffixes back to clean names
## lint
Checks an existing .lbr for Fusion 360 compatibility. Outputs JSON with `passed`, `errors[]`, `warnings[]`.
Checks:
- `empty-packages` — packages section empty (fatal: Fusion shows "No Components")
- `empty-connects` — device has no connect mappings (fatal)
- `device-no-package` — device element missing package attribute (fatal)
- `duplicate-pin-name` — EAGLE requires unique pin names; use @suffix for power/NC duplicates
- `connect-pad-missing` — connect references a pad that doesn't exist in the package
- `malformed-xml` — unbalanced angle brackets
- `no-symbol-layer` — nothing on layer 94 (symbol body invisible)
## validate
Same as lint but human-readable output. Exits 0 = valid, 2 = errors found.
## check
Validates a .kicad_sym file by local Rust validation — checks structure, pins, properties, duplicate names. Use after `import` to verify the output before sending to a desktop KiCad instance.
## Duplicate pin handling
EAGLE requires unique pin names. When KiCad has multiple pins with the same name (e.g., 6 IOVDD pins), the generator appends `@N` suffixes: `IOVDD`, `IOVDD@2`, `IOVDD@3`, etc. The `import` command strips these back.
## Workflows
### Fusion 360 user (has .kicad_sym, needs .lbr)
```
adom-lbr generate --sym RP2040.kicad_sym -o RP2040.lbr
# Send to Fusion via adom-desktop send_files + fusion_open_lbr
```
### KiCad user (has .lbr, needs .kicad_sym)
```
adom-lbr import RP2040.lbr -o RP2040.kicad_sym
adom-lbr check RP2040.kicad_sym
# Send to KiCad via adom-desktop send_files + kicad_install_library
```
### Pipeline (adom-symbol → both targets)
```
adom-symbol create <mpn> → .kicad_sym
adom-lbr generate --sym <mpn>.kicad_sym → .lbr (for Fusion)
# .kicad_sym already native for KiCad
```
### Lint any .lbr before use
```
adom-lbr lint suspicious.lbr
# JSON output with error codes for programmatic handling
```
## ⚠️ Altium .SchLib symbols — READ FIRST (hard-won 2026-07-10..13)
The native Altium **symbol** encoder (`export-altium` / `encode-sym` / `add-to-schlib`)
has repeatedly produced symbols that render **catastrophically wrong in real Altium with
NO error** — pins dropped, or one pin shot thousands of mils off to the side — while the
bytes looked fine to our own parser. Over one session it needed *four* separate byte-level
fixes and was **still** wrong. So:
### RULE 0 — DON'T generate the symbol; CLONE a known-good one.
Whenever a symbol must render in the user's real Altium, do **not** trust the codec's pin
encoder. Instead reuse Altium-authored bytes:
- **From a vendor `.IntLib`** the user provides: it's an OLE2 file whose `SchLib/0.schlib`
and `PCBLib/0.pcblib` streams are **zlib-compressed at offset 1** (`zlib.decompress(data[1:])`)
→ the real `.SchLib`/`.PcbLib`. Use those directly (Altium's own format = renders perfectly).
- **From one symbol the user fixes in Altium + saves:** then byte-clone it to the rest with
**`altium-codec replace-clone <in.SchLib> <src_comp> <out.SchLib> <specs.json>`**
(specs = `[{storage,libref,value,package,footprint,description}, …]`). It copies the source
component's Data stream **verbatim** (pins + graphics untouched) and edits only the metadata
text records. This is the reliable fix and mirrors the machine-pin flow (user fixes one, we
clone). Only *generate* from scratch when nothing Altium-authored exists — and then VERIFY
in Altium (RULE 1) before trusting it.
### RULE 1 — Verify in ALTIUM, never in your own parser.
Reader and writer can agree with each other and **both be wrong**. Ground truth = the
**Properties panel** (Pin Length / Location) on the actual pin. A "Pin Length" of tens of
thousands of mils = a byte bug. Get this ground truth EARLY instead of theorising for an hour.
### RULE 2 — Altium CACHES file-based libraries and FREEZES placed instances.
After you overwrite a `.SchLib`/`.PcbLib`, the user must **restart Altium** (or the library
panel serves the stale copy) and **delete any already-placed part** (placed instances don't
update). Tell them this up front — most "it still looks broken/old" reports this session were
stale caches, and they cost hours. Also: a title-bar `*` = unsaved edits; if you overwrite the
file while it's open and the user then Saves, they clobber your fix — tell them to close
WITHOUT saving, or restart.
### RULE 3 — Never call altium-bridge verbs with empty/partial args.
`altium_clone_symbol` / `altium_build_footprint` with `{}` **time out** and pop
`"value cannot be null"` / `"save file error"` dialogs inside the user's live Altium. Only
call bridge verbs with complete, correct payloads and an appropriate active doc.
### RULE 4 — Back up the user's original before overwriting it.
Pull + keep a copy first. This session the user's original SnapEDA symbol got overwritten with
a broken codec version and couldn't be recovered.
### Byte-level reference (only if you MUST generate — and still verify in Altium)
Binary pin record, calibrated vs genuine pins (STM32F072/ADS1220/caps). NOTE: even after all
of these the encoder was STILL wrong on negative X (Altium's own save writes byte `[19]=0xFF`
sign-extension; the codec writes `0x00` → giant pin), so **treat this as incomplete → clone**.
- `[14]` conglom (0x04).
- `[15:17]` **length field** — length in **bits 6-15** (unit 1/25.6 mil), orient in bits 0-1,
and **bit 5 (0x20) is a REQUIRED flag**. Genuine low bytes are only ever `0x20/0x22/0x38/0x3a`,
**never 0x00**. A round length + orient 0 (100 mil → 0x0A00, low byte 0x00) → GIANT pin.
Encode: `field = (len & 0xFFC0) | 0x20 | orient`.
- **X and Y must be snapped to a 10-mil grid** (multiple of 256 internal units) or the low
bytes bleed into neighbouring fields (off-grid X = the 66,630-mil pin). Exact X/Y widths are
context-dependent and NOT fully nailed — clone instead.
- **Pins before graphics**; **arcs → tessellated RECORD=6 polylines** (a hand-rolled RECORD=12
arc dropped the pin). **µ → ASCII** ("uF" not "µF" — codec writes UTF-8 → shows as "µ").
**Zero-width KiCad polylines are dropped by import-kicad** (give leads a real width).
- The `altium-schlib` READER decodes Y at the wrong offset — don't trust it either.
### Non-symbol notes
> **Which binary?** `replace-clone` and `merge-fp-libs` currently live in the lower-level
> **`altium-codec`** dev crate (`/home/adom/project/altium-writer/altium-codec/`), not yet folded into
> the shipped `adom-lbr` binary — `cargo build --release` there, or fall back to the manual
> `olefile`+`zlib` extraction (golden-path step 1), which needs no special binary and always works.
> `adom-lbr` itself ships `import/export-altium`, `export-intlib`, `add-to-schlib`, `import-altium`.
- `encode-fp-many <[Footprint]> <out.PcbLib>` builds a multi-footprint PcbLib — but writes **copper
pads ONLY** (no silk/fab/courtyard/keepout/3D; the model is `{name,pads}`). Pad *placement* is
reliable; the footprint is just bare. For real layers use genuine footprints + `merge-fp-libs` (see
the ⚠️ reality-check note up top).
- `merge-fp-libs <out.PcbLib> <spec.json>` (spec=`[{pcblib,name}]`) — merge N genuine single-footprint
PcbLibs into one, renaming each. Copies every primitive + embedded STEP verbatim; source[0] supplies
the shared `Library/*` boilerplate (all sources must be from the same converter). The way to get
full-layer footprints into a user's library without generating them.
- Symbol model has a `description` field → RECORD=1 ComponentDescription.
- For a STANDARD package (SOT-223, 0402/0603/0805), use the **KiCad standard footprint**, not a
ds2sf datasheet extraction — ds2sf mis-laid the SOT-223 pads.