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.
1.0.102: no paint wait or blank warning for an empty editor #66
1.0.102: an editor with no part loaded no longer waits for a canvas that will never paint.
After 1.0.101, a library-only kicad_show_library leaves the canvas empty, so the capture polled 8 to 11 times for a part and then warned that the canvas was blank. An empty editor now takes one capture and answers canvasExpectedEmpty: true with no warning. A loaded part keeps its wait and its warning.
Tested live on DESKTOP-BAR6S0F, KiCad 10.0.8: cold call 33 s to 13 s, repeat call 28 s to 8 s, proof image still shows the library's tree. Battery on the touched verbs only: 3/0/1, no foreground grabs.
🤖 Generated with Claude Code
Diff Skip to comments
@@ -1,27 +1,28 @@⋯ 2 unchanged lines ⋯ members = ["crates/*"] [workspace.package]-version = "1.0.101"+version = "1.0.102" edition = "2021" license = "MIT" publish = false⋯ 15 unchanged lines ⋯ codegen-units = 1 strip = true panic = "abort"+@@ -1,1307 +1,1308 @@⋯ 318 unchanged lines ⋯ [[package]] name = "kicad-bridge"-version = "1.0.101"+version = "1.0.102" dependencies = [ "adom-eda-checks", "kicad-core",⋯ 5 unchanged lines ⋯ [[package]] name = "kicad-core"-version = "1.0.101"+version = "1.0.102" dependencies = [ "kicad-ipc-rs", "kicad-platform",⋯ 20 unchanged lines ⋯ [[package]] name = "kicad-platform"-version = "1.0.101"+version = "1.0.102" dependencies = [ "serde", "serde_json",⋯ 940 unchanged lines ⋯ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"+@@ -1,1005 +1,1046 @@⋯ 284 unchanged lines ⋯ // ── Evidence and the shot ───────────────────────────────────────────────────── +/// An editor with no part loaded ("[no symbol loaded] — Symbol Editor"): its canvas has only the+/// grid to show, so a blank one is the right answer and there is nothing to wait for.+fn editor_is_empty(title: &str) -> bool {+ let t = title.trim_start_matches('*').trim_start();+ t.starts_with("[no ") && t.contains(" loaded]")+}++/// How many captures to try for a canvas to paint.+fn paint_attempts(title: &str, attempts: usize) -> usize {+ if editor_is_empty(title) { 1 } else { attempts }+}++/// A blank canvas in an empty editor is not a fault: say that instead of warning about it.+fn note_empty_canvas(out: &mut Value, title: &str) {+ if editor_is_empty(title) {+ if let Some(o) = out.as_object_mut() {+ o.remove("_canvasHint");+ }+ out["canvasExpectedEmpty"] = json!(true);+ }+}+ /// Capture a shot of `hwnd` through ab, polling until the GL canvas has painted (0.9.202: /// poll rather than assume; each attempt is a real capture). Returns (shot, attempts waited). fn capture_settled(hwnd: u64, label: &str, resize_max: Option<u32>, max_attempts: usize) -> Result<(Value, usize), String> {⋯ 28 unchanged lines ⋯ /// The `evidence` block (Python `_capture_evidence`): PROOF the surface is really on screen, /// captured in the background from the verified hwnd. fn capture_evidence(w: &win::Win, label: &str) -> Result<Value, String> {- let (shot, waited) = capture_settled(w.hwnd, label, None, 8)?;+ let (shot, waited) = capture_settled(w.hwnd, label, None, paint_attempts(&w.title, 8))?; let path = shot.get("fullPath").or_else(|| shot.get("savedTo")).cloned().unwrap_or(Value::Null); let mut ev = json!({ "window": w.title,⋯ 11 unchanged lines ⋯ } canvas_fields(&mut ev, &shot, waited, "The window is correct but its drawing canvas is still BLANK ({colors} distinct colour(s)) after waiting {waited}s for it to paint. Usually this just means KiCad is still loading libraries - check the status bar in the image. If it never paints, the canvas is an OpenGL surface and yields no picture on a host with no usable GPU or no active console session; kicad_enable_software_opengl is the last-resort fix there. Either way, treat this thumbnail as proof of the WINDOW, not of the part.");+ note_empty_canvas(&mut ev, &w.title); Ok(ev) } ⋯ 22 unchanged lines ⋯ let mw = clamp_max_width(max_width); // Wiki #52: the editor answers "ready" before its GL canvas first paints, and blanks // cluster on FAST parts. Own the settle here too (up to 10 s) and REPORT the verdict.- let (shot, waited) = capture_settled(w.hwnd, &format!("shot-{label}"), Some(mw), 11)?;+ let (shot, waited) = capture_settled(w.hwnd, &format!("shot-{label}"), Some(mw), paint_attempts(&w.title, 11))?; let full = shot.get("fullPath").or_else(|| shot.get("savedTo")).and_then(Value::as_str).unwrap_or("").to_string(); let safe = shot.get("safePath").and_then(Value::as_str).unwrap_or("").to_string(); let resized = !safe.is_empty() && std::path::Path::new(&safe).is_file();⋯ 29 unchanged lines ⋯ out["thumbnailUnavailable"] = json!(format!("ab returned no resized (.safe) variant for this capture, so `image` is the FULL-resolution frame and no thumbnailDataUrl is offered (the native bridge does not decode or downscale images; maxWidth {mw} was requested from ab). Render `image` directly or pull `path`.")); } canvas_fields(&mut out, &shot, waited, "The canvas region is still UNRENDERED after {waited}s of polling - treat this frame as proof of the window, not the part, and recapture rather than render it.");+ note_empty_canvas(&mut out, &w.title); Ok(out) } ⋯ 466 unchanged lines ⋯ #[cfg(test)] mod tests { use super::*;+ #[test]+ fn an_empty_editor_is_not_waited_on_or_warned_about() {+ assert_eq!(paint_attempts("[no symbol loaded] \u{2014} Symbol Editor", 11), 1);+ assert_eq!(paint_attempts("[no footprint loaded] \u{2014} Footprint Editor", 8), 1);+ assert_eq!(paint_attempts("Device:R [Read Only Library] \u{2014} Symbol Editor", 11), 11, "a loaded part still gets its wait");+ let mut v = json!({"canvasRendered": false, "_canvasHint": "still BLANK"});+ note_empty_canvas(&mut v, "[no symbol loaded] \u{2014} Symbol Editor");+ assert!(v.get("_canvasHint").is_none());+ assert_eq!(v["canvasExpectedEmpty"], true);+ let mut v = json!({"canvasRendered": false, "_canvasHint": "still BLANK"});+ note_empty_canvas(&mut v, "Device:R \u{2014} Symbol Editor");+ assert_eq!(v["_canvasHint"], "still BLANK", "a part that didn't paint is still a fault");+ assert!(v.get("canvasExpectedEmpty").is_none());+ } + #[test] fn catalog_matches_bridge_json_and_carries_no_em_dash() { let names: Vec<&str> = VERBS.iter().map(|v| v.name).collect();⋯ 123 unchanged lines ⋯ assert_eq!(surface_kind("show_project"), None); } }++
Comments
Log in to comment.
No comments yet.