← All Pull Requests

1.0.102: no paint wait or blank warning for an empty editor #66

Merged opened by Colby Knox 2026-11-05

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

--- a/rust/Cargo.toml+++ b/rust/Cargo.toml@@ -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"+--- a/rust/Cargo.lock+++ b/rust/Cargo.lock@@ -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"+--- a/rust/crates/kicad-bridge/src/verbs_show.rs+++ b/rust/crates/kicad-bridge/src/verbs_show.rs@@ -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

No comments yet.

Log in to comment.