app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Drive your own KiCad from an AI, in the background: open every editor, read the whole board as data (the EDA Bridge Standard board model), stack-up, part heights, netlists, place, route, pour, DRC and exports.
← Commit history
1.0.14: NATIVE (Rust) build. 1.0.14: an editor window the bridge OPENS is put back on the monitor the user keeps that editor on, not just after a restart (wiki #48, the opens-or-restarts half of the complaint).
3 files changed
+53−28
rust/Cargo.lock+3−3@@ -244,7 +244,7 @@ dependencies = [ [[package]] name = "kicad-bridge"-version = "1.0.13"+version = "1.0.14" dependencies = [ "kicad-core", "kicad-platform",@@ -255,7 +255,7 @@ dependencies = [ [[package]] name = "kicad-core"-version = "1.0.13"+version = "1.0.14" dependencies = [ "kicad-ipc-rs", "kicad-platform",@@ -281,7 +281,7 @@ dependencies = [ [[package]] name = "kicad-platform"-version = "1.0.13"+version = "1.0.14" dependencies = [ "serde", "serde_json",
rust/Cargo.toml+1−1@@ -3,7 +3,7 @@ resolver = "2" members = ["crates/*"] [workspace.package]-version = "1.0.13"+version = "1.0.14" edition = "2021" license = "MIT" publish = false
rust/crates/kicad-bridge/src/verbs_windows.rs+49−24@@ -651,30 +651,34 @@ pub(crate) fn restore_placement() -> Vec<Value> { if mem.is_empty() { return Vec::new(); }- let mut out = Vec::new();- for w in kicad_windows() {- let kind = w.kind();- let Some(remembered) = mem.get(kind.as_str()) else { continue };- let r = &remembered.place;- let current = match native().placement(w.hwnd) {- Ok(p) => p,- Err(e) => {- out.push(json!({"window": kind.as_str(), "hwnd": w.hwnd, "moved": false, "error": e}));- continue;- }- };- let on_monitor = native().on_a_monitor((r.x, r.y, r.width, r.height)).unwrap_or(false);- match window_memory::decide(r, ¤t, on_monitor) {- Restore::Skip(why) => out.push(json!({"window": kind.as_str(), "hwnd": w.hwnd, "moved": false, "why": why})),- Restore::Move(p) => match native().set_placement(w.hwnd, &p) {- Ok(()) => out.push(json!({"window": kind.as_str(), "hwnd": w.hwnd, "moved": true,- "to": {"x": p.x, "y": p.y, "width": p.width, "height": p.height, "maximized": p.maximized},- "rememberedAt": remembered.at})),- Err(e) => out.push(json!({"window": kind.as_str(), "hwnd": w.hwnd, "moved": false, "error": e})),- },- }- }- out+ kicad_windows().iter().filter_map(|w| restore_one(&mem, w.kind(), w.hwnd)).collect()+}++/// Wiki #48: one freshly opened window put back on the monitor the user keeps that kind of+/// window on. Called when a verb OPENS an editor, not only when it restarts KiCad, because+/// "the bridge opens or restarts KiCad and it comes back on the laptop screen" is one+/// complaint with two halves. Returns None when nothing is remembered for that kind.+pub(crate) fn restore_placement_one(kind: WindowKind, hwnd: u64) -> Option<Value> {+ restore_one(&window_memory::load(), kind, hwnd)+}++fn restore_one(mem: &window_memory::Memory, kind: WindowKind, hwnd: u64) -> Option<Value> {+ let remembered = mem.get(kind.as_str())?;+ let r = &remembered.place;+ let current = match native().placement(hwnd) {+ Ok(p) => p,+ Err(e) => return Some(json!({"window": kind.as_str(), "hwnd": hwnd, "moved": false, "error": e})),+ };+ let on_monitor = native().on_a_monitor((r.x, r.y, r.width, r.height)).unwrap_or(false);+ Some(match window_memory::decide(r, ¤t, on_monitor) {+ Restore::Skip(why) => json!({"window": kind.as_str(), "hwnd": hwnd, "moved": false, "why": why}),+ Restore::Move(p) => match native().set_placement(hwnd, &p) {+ Ok(()) => json!({"window": kind.as_str(), "hwnd": hwnd, "moved": true,+ "to": {"x": p.x, "y": p.y, "width": p.width, "height": p.height, "maximized": p.maximized},+ "rememberedAt": remembered.at}),+ Err(e) => json!({"window": kind.as_str(), "hwnd": hwnd, "moved": false, "error": e}),+ },+ }) } fn find_project_manager() -> Option<u64> {@@ -1333,6 +1337,22 @@ fn ensure_project_manager(info: &KicadInfo, pathways: &mut Vec<String>) -> Resul /// Open an editor frame: an existing one wins; else the native menu walk on each host /// frame (WM_COMMAND, repost once at 10 s); else UIA Invoke. Returns (hwnd, mechanism). fn open_editor_frame(info: &KicadInfo, spec: &OpenSpec, budget: &Budget, pathways: &mut Vec<String>) -> Result<(u64, &'static str), Value> {+ let out = open_editor_frame_inner(info, spec, budget, pathways);+ // Wiki #48: a frame the bridge just opened is placed by Windows defaults, which means the+ // primary display. An existing window is the user's own and is never moved.+ if let Ok((hwnd, mechanism)) = &out {+ if *mechanism != "existing" {+ if let Some(row) = restore_placement_one(spec.kind, *hwnd) {+ if row["moved"] == json!(true) {+ pathways.push(format!("placement-restored:{}", spec.kind.as_str()));+ }+ }+ }+ }+ out+}++fn open_editor_frame_inner(info: &KicadInfo, spec: &OpenSpec, budget: &Budget, pathways: &mut Vec<String>) -> Result<(u64, &'static str), Value> { invalidate(); if let Some(h) = first_of_kind(spec.kind) { return Ok((h, "existing"));@@ -1583,6 +1603,11 @@ pub(crate) fn open_file(state: &mut State, args: &Value, kind: WindowKind) -> Va out["hwnd"] = json!(w.hwnd); out["title"] = json!(w.title); out["windowAppeared"] = json!(true);+ // Wiki #48: this window was spawned by us, so Windows placed it on the primary+ // display; put it back on the monitor the user keeps this editor on.+ if let Some(row) = restore_placement_one(kind, w.hwnd) {+ out["placementRestored"] = row;+ } } None => { let alive = native().process_alive(pid);