app
Altium - the Altium Bridge
Public Made by Adomby adom
Adom Bridge for Altium Designer: a resident DelphiScript agent gives the AI eval, the 1,100-command process launcher, all 8 EDA surfaces with screenshots, compile/ERC, DRC, OutJob outputs, native SchLib/PcbLib authoring with STEP 3D bodies, and dialog handling by PID.
← Commit history
0.2.31 source: board_shape uses the proven outline pattern, nil-guarded
6 files changed
+35−34
bridge-rs/Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "altium-bridge"-version = "0.2.30"+version = "0.2.31" dependencies = [ "getrandom", "serde",
bridge-rs/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "altium-bridge"-version = "0.2.30"+version = "0.2.31" edition = "2021" description = "Adom altium-bridge — the bridge process, in Rust. Calls ab's verbs for the desktop; speaks file IPC to the resident DelphiScript agent inside Altium." license = "MIT"
bridge-rs/src/main.rs+1−1@@ -26,7 +26,7 @@ mod win32; use std::time::Instant; use tiny_http::{Header, Method, Response, Server}; -pub const BRIDGE_VERSION: &str = "0.2.30";+pub const BRIDGE_VERSION: &str = "0.2.31"; fn json_response(status: u16, body: &serde_json::Value) -> Response<std::io::Cursor<Vec<u8>>> { let data = serde_json::to_vec(body).unwrap_or_else(|_| b"{}".to_vec());
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "altium", "displayName": "Altium Designer",- "version": "0.2.30",+ "version": "0.2.31", "description": "Drive Altium Designer from Adom: a resident DelphiScript agent (eval + the 1,100-command process launcher), the X2 -R command line, UIA over the VCL shell, dialogs by PID, screenshots of every Altium window, and Altium 365.", "homepage": "https://wiki.adom.inc/adom/altium-bridge", "docs": "https://wiki.adom.inc/adom/altium-bridge",
page.json+1−1@@ -140,4 +140,4 @@ "skills/altium-wiki-import/SKILL.md", "uninstall.sh" ]-}+}
src/agent/AdomAgent.pas.tmpl+30−29@@ -3278,16 +3278,20 @@ End; { ------------------------------------------------------------ board_shape --- } -{ Redefine the CURRENT board shape as a wMm x hMm rectangle at (x0Mm,y0Mm) using- the editor process PCB:PlaceBoardOutline Mode=BOARDOUTLINE_FROM_SEL_PRIMS on- four temporary selected tracks (BoardOutline.PointCount/Segments is a silent- no-op in this engine, measured 2026-08-30). }+{ Redefine the CURRENT board shape as a wMm x hMm rectangle at (x0Mm,y0Mm).+ Until 0.2.31 this drew four temporary tracks and ran the editor process+ PCB:PlaceBoardOutline (Mode=BOARDOUTLINE_FROM_SEL_PRIMS), then read+ Board.BoardOutline.BoundingRectangle with NO nil guard. On AD 26.10.1 and on Altium Develop the+ process left no outline, the read raised on a nil object, and the script parked on a breakpoint+ with no error box (found by the verb battery on two builds, 2026-09-20). It now uses the pattern+ CmdTestBoard has always used and that renders correctly on every build: PointCount + Segments[]+ + Rebuild on the board's own outline, every access nil-guarded. } Function CmdBoardShape(Lines : TStringList) : String; Var Board : IPCB_Board;- T : IPCB_Track;- Trks : Array[0..3] Of IPCB_Track;- X0, Y0, X1, Y1, I : Integer;+ BO : IPCB_BoardOutline;+ Seg : TPolySegment;+ X0, Y0, X1, Y1 : Integer; R : TCoordRect; Begin Board := PCBServer.GetCurrentPCBBoard;@@ -3300,33 +3304,30 @@ Begin Y0 := MMsToCoord(AdomFloat(Param(Lines, 'y0Mm'))); X1 := X0 + MMsToCoord(AdomFloat(Param(Lines, 'wMm'))); Y1 := Y0 + MMsToCoord(AdomFloat(Param(Lines, 'hMm')));- PCBServer.PreProcess;- For I := 0 To 3 Do+ If (X1 <= X0) Or (Y1 <= Y0) Then Begin- T := PCBServer.PCBObjectFactory(eTrackObject, eNoDimension, eCreate_Default);- T.Layer := eMechanical1;- T.Width := MMsToCoord(0.1);- Case I Of- 0: Begin T.X1 := X0; T.Y1 := Y0; T.X2 := X1; T.Y2 := Y0; End;- 1: Begin T.X1 := X1; T.Y1 := Y0; T.X2 := X1; T.Y2 := Y1; End;- 2: Begin T.X1 := X1; T.Y1 := Y1; T.X2 := X0; T.Y2 := Y1; End;- 3: Begin T.X1 := X0; T.Y1 := Y1; T.X2 := X0; T.Y2 := Y0; End;- End;- Board.AddPCBObject(T);- PCBServer.SendMessageToRobots(Board.I_ObjectAddress, c_Broadcast, PCBM_BoardRegisteration, T.I_ObjectAddress);- Trks[I] := T;+ Result := '{' + JRaw('ok','false') + ',' + JStr('errorCode','bad_size') + ',' + JStr('error','wMm and hMm must both be greater than 0') + '}';+ Exit;+ End;+ BO := Board.BoardOutline;+ If BO = Nil Then+ Begin+ Result := '{' + JRaw('ok','false') + ',' + JStr('errorCode','no_board_outline') + ',' ++ JStr('error','this document has no board outline object (a PcbLib internal board has none); open a .PcbDoc') + '}';+ Exit; End;- PCBServer.PostProcess;- Board.SelectedObjects_Clear;- For I := 0 To 3 Do Trks[I].Selected := True;- ResetParameters;- AddStringParameter('Mode', 'BOARDOUTLINE_FROM_SEL_PRIMS');- RunProcess('PCB:PlaceBoardOutline'); PCBServer.PreProcess;- For I := 0 To 3 Do Board.RemovePCBObject(Trks[I]);+ BO.Invalidate;+ BO.PointCount := 4;+ Seg := BO.Segments[0]; Seg.vx := X0; Seg.vy := Y0; Seg.Kind := ePolySegmentLine; BO.Segments[0] := Seg;+ Seg := BO.Segments[1]; Seg.vx := X1; Seg.vy := Y0; Seg.Kind := ePolySegmentLine; BO.Segments[1] := Seg;+ Seg := BO.Segments[2]; Seg.vx := X1; Seg.vy := Y1; Seg.Kind := ePolySegmentLine; BO.Segments[2] := Seg;+ Seg := BO.Segments[3]; Seg.vx := X0; Seg.vy := Y1; Seg.Kind := ePolySegmentLine; BO.Segments[3] := Seg;+ BO.Rebuild;+ BO.Validate; PCBServer.PostProcess; Board.ViewManager_FullUpdate;- R := Board.BoardOutline.BoundingRectangle;+ R := BO.BoundingRectangle; Result := '{' + JRaw('ok','true') + ',' + JRaw('outlineMil', '[' + IntToStr(Round(CoordToMils(R.Left))) + ',' + IntToStr(Round(CoordToMils(R.Bottom))) + ',' + IntToStr(Round(CoordToMils(R.Right))) + ',' + IntToStr(Round(CoordToMils(R.Top))) + ']') + '}';