← Commit history
README.md+1−1
@@ -11,7 +11,7 @@ verbs.  ```bash adom-wiki pkg install barrett-land/adom-desktop-altium-bridge   # the skill, in the container-adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.50/altium-bridge-manifest.json"}'+adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.51/altium-bridge-manifest.json"}' adom-desktop --ai-thread "<your thread>" altium_ping '{}' ``` 
SKILL.md+1−1
@@ -12,7 +12,7 @@ forwards work into the running GUI as DelphiScript via `X2.EXE`. ## Install  ```bash-adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.50/altium-bridge-manifest.json"}'+adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.51/altium-bridge-manifest.json"}' adom-desktop --ai-thread "<your thread>" altium_ping '{}'   # -> {"altiumInstalled": true, "version": "..."} ``` 
altium-bridge-manifest.json+3−3
@@ -1,3 +1,3 @@-{ "manifest_version": 1, "name": "altium", "version": "0.0.50",-  "url": "https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.50/altium-bridge-v0.0.50.zip",-  "sha256": "529217a4b61861ca2ba4ff5d825f78d639d68b93ff426951e935ce3d79a1cf64", "size": 42781, "released_at": "2026-08-12T17:45:50Z" }+{ "manifest_version": 1, "name": "altium", "version": "0.0.51",+  "url": "https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.51/altium-bridge-v0.0.51.zip",+  "sha256": "0b67dbac6c1f2b8456693f982675eece2ba04c67c794efd5ebfb815b9fad800b", "size": 44940, "released_at": "2026-08-12T20:33:24Z" }
altium-bridge-v0.0.51.zipadded
⋯ 1 unchanged line ⋯
bridge-src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.0.50+0.0.51
bridge-src/__pycache__/server.cpython-312.pyc
⋯ 1 unchanged line ⋯
bridge-src/addin/AdomAltium/AdomAltium.pas+187
@@ -1950,6 +1950,192 @@ begin end;  { ---- shared command dispatch. Assumes the global Req is loaded and GReqId set. ---- }+{ ---- command: remap an imported footprint's LAYERS onto the house convention and+       reset stroke widths.++       Why this exists: Altium's KiCad importer does not map KiCad's layers onto+       Altium's. It creates NEW mechanical layers carrying KiCad's own names.+       Measured on an LQFP-48 and a SOT-223: silkscreen does land correctly on+       Top Overlay, but the courtyard lands on "F.CrtYd" and the fab outline on+       "F.Fab" rather than "Courtyard Top" / "Assembly Top". Nothing downstream that+       reads the house layers -- OutJobs, assembly drawings, DRC -- then sees them,+       so the footprint looks right on screen and is wrong everywhere else.++       The importer also drops every (stroke (width ...)) it reads, logging+       "Unknown child stroke of fp_line", and defaults each track to 10 mil. KiCad's+       0.12 mm silk and 0.05 mm courtyard therefore both arrive at 0.254 mm.++       remap  : "SrcName|DstName;SrcName|DstName"     matched BY LAYER NAME+       widths : "LayerName|microns;LayerName|microns" applied AFTER the remap+       footprint: a name, or '' / '*' for every footprint in the library.++       Widths are MICRONS so they stay integers (50 = 0.05 mm courtyard,+       127 = 5 mil silk, which is what the hand-built libraries here use).++       Layers are resolved by probing integer ids against Board.LayerName[] rather+       than naming eMechanicalNN constants: an undeclared identifier is a COMPILE+       error, which takes the whole DelphiScript engine down instead of failing this+       one command. ---- }+procedure CmdRemapFootprintLayers;+var+    PcbPath, FpName, RemapSpec, WidthSpec, SavedStr : String;+    SD       : IServerDocument;+    PcbLib   : IPCB_Library;+    Board    : IPCB_Board;+    Fp       : IPCB_LibComponent;+    It       : IPCB_GroupIterator;+    Prim     : IPCB_Primitive;+    layers   : TStringList;   { "<name>=<id>" }+    remaps   : TStringList;   { "<srcname>=<dstname>" }+    widths   : TStringList;   { "<layername>=<microns>" }+    i, li, dstId, moved, widthed, fpCount : Integer;+    nm, s, item, a, b : String;+    pos1, pos2 : Integer;+    SavedOK  : Boolean;+begin+    PcbPath   := Req.Values['pcblib'];+    FpName    := Req.Values['footprint'];+    RemapSpec := Req.Values['remap'];+    WidthSpec := Req.Values['widths'];+    if RemapSpec = '' then RemapSpec := 'F.CrtYd|Courtyard Top;F.Fab|Assembly Top';+    if WidthSpec = '' then WidthSpec := 'Top Overlay|127;Courtyard Top|50;Assembly Top|127';+    if PcbPath = '' then begin RespErr('missing pcblib'); Exit; end;++    GStage := 'open pcblib';+    SD := Client.OpenDocument('PCBLIB', PcbPath);+    if SD <> nil then Client.ShowDocument(SD);+    LastServerDoc := SD;+    if PCBServer = nil then begin RespErr('PCBServer nil'); Exit; end;+    PcbLib := PCBServer.GetCurrentPCBLibrary;+    if PcbLib = nil then begin RespErr('could not open: ' + PcbPath); Exit; end;+    Board := PcbLib.Board;+    if Board = nil then begin RespErr('no board on the library'); Exit; end;++    layers := TStringList.Create;+    remaps := TStringList.Create;+    widths := TStringList.Create;+    moved := 0; widthed := 0; fpCount := 0;+    try+        GStage := 'enumerate layers';+        for li := 1 to 100 do+        begin+            nm := '';+            try+                nm := Board.LayerName[li];+            except+                nm := '';+            end;+            if nm <> '' then layers.Add(nm + '=' + IntToStr(li));+        end;++        GStage := 'parse specs';+        s := RemapSpec;+        while s <> '' do+        begin+            pos1 := Pos(';', s);+            if pos1 > 0 then begin item := Copy(s, 1, pos1 - 1); Delete(s, 1, pos1); end+            else begin item := s; s := ''; end;+            pos2 := Pos('|', item);+            if pos2 > 0 then+            begin+                a := Trim(Copy(item, 1, pos2 - 1));+                b := Trim(Copy(item, pos2 + 1, Length(item)));+                if (a <> '') and (b <> '') then remaps.Add(a + '=' + b);+            end;+        end;+        s := WidthSpec;+        while s <> '' do+        begin+            pos1 := Pos(';', s);+            if pos1 > 0 then begin item := Copy(s, 1, pos1 - 1); Delete(s, 1, pos1); end+            else begin item := s; s := ''; end;+            pos2 := Pos('|', item);+            if pos2 > 0 then+            begin+                a := Trim(Copy(item, 1, pos2 - 1));+                b := Trim(Copy(item, pos2 + 1, Length(item)));+                if (a <> '') and (b <> '') then widths.Add(a + '=' + b);+            end;+        end;++        GStage := 'remap';+        PCBServer.PreProcess;+        for i := 0 to PcbLib.ComponentCount - 1 do+        begin+            Fp := PcbLib.GetComponent(i);+            if Fp <> nil then+            if (FpName = '') or (FpName = '*') or (Fp.Name = FpName) then+            begin+                Inc(fpCount);+                It := Fp.GroupIterator_Create;+                It.SetState_FilterAll;+                try+                    Prim := It.FirstPCBObject;+                    while Prim <> nil do+                    begin+                        nm := '';+                        try nm := Board.LayerName[Prim.Layer]; except nm := ''; end;++                        { move to the house layer, if this one is mapped }+                        if (nm <> '') and (remaps.Values[nm] <> '') then+                        begin+                            dstId := StrToIntDef(layers.Values[remaps.Values[nm]], -1);+                            if dstId > 0 then+                            begin+                                Prim.Layer := dstId;+                                Inc(moved);+                                try nm := Board.LayerName[Prim.Layer]; except end;+                            end;+                        end;++                        { reset the stroke width for whatever layer it now sits on.+                          Tracks expose Width, arcs expose LineWidth, and pads/regions+                          expose neither -- so try in turn rather than switch on an+                          ObjectId constant. }+                        if (nm <> '') and (widths.Values[nm] <> '') then+                        begin+                            li := StrToIntDef(widths.Values[nm], 0);+                            if li > 0 then+                            begin+                                try+                                    Prim.Width := MilsToCoord(li / 25.4);+                                    Inc(widthed);+                                except+                                    try+                                        Prim.LineWidth := MilsToCoord(li / 25.4);+                                        Inc(widthed);+                                    except+                                    end;+                                end;+                            end;+                        end;++                        Prim := It.NextPCBObject;+                    end;+                finally+                    Fp.GroupIterator_Destroy(It);+                end;+            end;+        end;+        PCBServer.PostProcess;+    finally+        layers.Free;+        remaps.Free;+        widths.Free;+    end;++    GStage := 'save';+    Board.ViewManager_FullUpdate;+    SavedOK := False;+    if LastServerDoc <> nil then SavedOK := LastServerDoc.DoFileSave('');+    if SavedOK then SavedStr := 'true' else SavedStr := 'false';+    RespOk(', "footprints": ' + IntToStr(fpCount) ++           ', "movedPrimitives": ' + IntToStr(moved) ++           ', "widthsSet": ' + IntToStr(widthed) ++           ', "saved": ' + SavedStr);+end;++ procedure Dispatch; var Cmd : String; begin@@ -1981,6 +2167,7 @@ begin         else if Cmd = 'add_body'         then CmdAddBody         else if Cmd = 'set_visibility'   then CmdSetVisibility         else if Cmd = 'set_text_style'   then CmdSetTextStyle+        else if Cmd = 'remap_fp_layers'  then CmdRemapFootprintLayers         else if Cmd = 'export_step'      then CmdExportStep         else if Cmd = 'run_outjob'       then CmdRunOutJob         else RespErr('unknown command: ' + Cmd);
bridge-src/bridge.json+2−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "altium",   "displayName": "Altium Bridge",-  "version": "0.0.50",+  "version": "0.0.51",   "description": "Drives Altium Designer from a container: build, clone and edit file-based symbol/footprint/Integrated libraries with LCSC/JLCPCB part numbers attached, and generate batch outputs (STEP, PDF, Gerber, BOM, CPL) from an OutJob. Pairs with the container-side altium-jlc orchestrator (adom-jlcpcb + chip-fetcher).",   "homepage": "https://wiki.adom.inc/adom/adom-desktop-altium-bridge",   "author": "Adom Inc.",@@ -44,6 +44,7 @@     "altium_add_body",     "altium_set_visibility",     "altium_set_text_style",+    "altium_remap_fp_layers",     "altium_copy_sch_component",     "altium_copy_footprint",     "altium_script_state",
bridge-src/server.py+17
@@ -177,6 +177,22 @@ def handle_set_visibility(args: dict) -> dict:                       timeout=args.get("timeout", 90))  +def handle_remap_fp_layers(args: dict) -> dict:+    """Remap an imported footprint's layers onto the house convention and reset+    stroke widths. Altium's KiCad importer does not map KiCad layers onto Altium's:+    silkscreen does land on Top Overlay, but the courtyard lands on 'F.CrtYd' and the+    fab outline on 'F.Fab' instead of 'Courtyard Top' / 'Assembly Top', so OutJobs,+    assembly drawings and DRC never see them. It also drops every stroke width, so+    KiCad's 0.12mm silk and 0.05mm courtyard both arrive at Altium's 10 mil default.++    remap  = "SrcName|DstName;..."      matched by layer NAME+    widths = "LayerName|microns;..."    applied after the remap (microns keep it integral)+    footprint = a name, or '' / '*' for the whole library."""+    keys = ("pcblib", "footprint", "remap", "widths")+    return addin_call("remap_fp_layers", {k: args.get(k) for k in keys},+                      timeout=args.get("timeout", 240))++ def handle_set_text_style(args: dict) -> dict:     """Normalise font + colour on a component's designator, comment and parameters.     Defaults are Altium's own: FontID 1 (Times New Roman 10) and colour 8388608,@@ -461,6 +477,7 @@ COMMAND_HANDLERS = {     "altium_add_body": handle_add_body,     "altium_set_visibility": handle_set_visibility,     "altium_set_text_style": handle_set_text_style,+    "altium_remap_fp_layers": handle_remap_fp_layers,     "altium_copy_sch_component": handle_copy_sch_component,     "altium_copy_footprint": handle_copy_footprint, }
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-desktop-altium-bridge",-  "version": "0.0.50",+  "version": "0.0.51",   "type": "app",   "description": "Adom Desktop bridge for Altium Designer: file-based symbol/footprint/IntLib authoring with LCSC/JLCPCB part numbers, Gerber/BOM/CPL batch output, and an in-process .NET extension adding parameterized headless verbs - STEP export with hole and copper options, and KiCad import with no wizard.",   "tags": [
page.json+11−7
@@ -1,8 +1,8 @@ {   "slug": "adom-desktop-altium-bridge",-  "version": "0.0.50",+  "version": "0.0.51",   "type": "app",-  "description": "Adom Desktop bridge for Altium Designer \u2014 build and edit file-based symbol/footprint/Integrated libraries with LCSC/JLCPCB part numbers attached, and generate batch outputs (STEP, PDF, Gerber, BOM, CPL) from an OutJob, by driving Altium via DelphiScript.",+  "description": "Adom Desktop bridge for Altium Designer: file-based symbol/footprint/IntLib authoring with LCSC/JLCPCB part numbers, Gerber/BOM/CPL batch output, and an in-process .NET extension adding parameterized headless verbs - STEP export with hole and copper options, and KiCad import with no wizard.",   "tags": [     "app",     "adom-desktop",@@ -18,6 +18,7 @@   },   "needs_sudo": false,   "hero": {+    "type": "image",     "path": "docs/hero.png"   },   "author": {@@ -25,13 +26,16 @@     "email": "[email protected]"   },   "discovery_triggers": [-    "add a part to my altium library",     "drive altium from claude",     "altium bridge",-    "export step from altium",+    "export step from altium headless",+    "add a part to my altium library",+    "altium extension",+    "install altium extension",+    "altium step export",     "run an altium outjob",-    "build an altium footprint",-    "altium symbol library"+    "import kicad into altium",+    "altium kicad importer"   ],   "discovery_pitch": "Use when the user wants Claude to author or edit Altium libraries, or to generate STEP/PDF/Gerber/BOM/CPL from an Altium board on their Windows machine.",   "sample_prompts": [@@ -48,4 +52,4 @@       "prompt": "Is the Altium bridge connected and what version is it?"     }   ]-}+}\ No newline at end of file