← Commit history

Update 5 file(s)

John Lauer ·df03435533 ·1mo ago ·parent 0baacec
5 files changed +68−8
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.199\ No newline at end of file+0.9.200\ No newline at end of file
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.199",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.199/adom-bridge-kicad-v0.9.199.zip",-  "sha256": "a9af58611683d9f4ed4af9ff92cd851f26eeeae7914a23d9c15938585ff76f78",-  "size": 469492,+  "version": "0.9.200",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.200/adom-bridge-kicad-v0.9.200.zip",+  "sha256": "799b09738cff9de8bdb1112cfe80267052403cdeaa12e7874e4e4c875cd27674",+  "size": 470688,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-20T01:45:00Z",+  "released_at": "2026-08-20T09:35:00Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.199",+  "version": "0.9.200",   "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",
handlers/kicad_ui.py+41
@@ -265,6 +265,44 @@ def _write_bmp(path: str, width: int, height: int, pixel_data: bytes):             f.write(bytes(row_24))  ++def _canvas_uniformity(pixels: bytes, width: int, height: int) -> dict:+    """Is the editor's drawing CANVAS actually rendered, or an empty rectangle?++    0.9.200. A capture can show the right title, the right tree row and the right+    properties panel while the canvas itself is blank - which is how an evidence+    thumbnail ends up proving nothing. Seen on a conference-room box where every+    capture came back with an empty canvas (ab's own capture agreed, so it is not+    the capture path); KiCad renders its canvas through OpenGL, which does not+    produce a picture on a host with no usable GPU or no active console session.++    Cheap and dependency-free: sample the canvas region straight out of the raw+    BGRA buffer and count distinct colours. A drawn canvas carries grid dots,+    axes and the part itself; an unrendered one is one flat colour."""+    try:+        # The canvas is the large area right of the library/properties panels.+        x0, x1 = int(width * 0.35), int(width * 0.95)+        y0, y1 = int(height * 0.15), int(height * 0.85)+        if x1 <= x0 or y1 <= y0:+            return {"checked": False}+        step_x = max(1, (x1 - x0) // 60)+        step_y = max(1, (y1 - y0) // 60)+        colors = set()+        samples = 0+        for y in range(y0, y1, step_y):+            row = (height - 1 - y) * width * 4     # GetDIBits rows are bottom-up+            for x in range(x0, x1, step_x):+                i = row + x * 4+                colors.add(pixels[i:i + 3])+                samples += 1+                if len(colors) > 12:               # clearly drawn; stop early+                    return {"checked": True, "distinctColors": len(colors),+                            "rendered": True, "samples": samples}+        return {"checked": True, "distinctColors": len(colors),+                "rendered": len(colors) > 3, "samples": samples}+    except Exception:+        return {"checked": False}+ def _screenshot_hwnd(hwnd: int, label: str = "", max_dim: int = 1568) -> dict:     """Capture a window by HWND using PrintWindow. Returns {success, savedTo, sizeKB}."""     # A park-minimized window (focus machinery) can't PrintWindow reliably —@@ -328,6 +366,8 @@ def _screenshot_hwnd(hwnd: int, label: str = "", max_dim: int = 1568) -> dict:     pixel_buf = ctypes.create_string_buffer(buf_size)     gdi32.GetDIBits(hdc_mem, hbmp, 0, height, pixel_buf, ctypes.byref(bmi), DIB_RGB_COLORS) +    _canvas = _canvas_uniformity(pixel_buf.raw, width, height)+     gdi32.SelectObject(hdc_mem, old_bmp)     gdi32.DeleteObject(hbmp)     gdi32.DeleteDC(hdc_mem)@@ -377,6 +417,7 @@ def _screenshot_hwnd(hwnd: int, label: str = "", max_dim: int = 1568) -> dict:         "savedTo": posix,         "fullPath": posix,         "sizeKB": round(size_kb, 1),+        "canvas": _canvas,     }  
handlers/show.py+20−1
@@ -145,7 +145,8 @@ def _capture_evidence(win, label: str) -> dict | None:         shot = _screenshot_hwnd(hwnd, label=label)         if not shot or not shot.get("success"):             return None-        return {+        canvas = shot.get("canvas") or {}+        ev = {             "window": title,             "hwnd": hwnd,             "screenshotPath": shot.get("fullPath") or shot.get("savedTo"),@@ -156,6 +157,24 @@ def _capture_evidence(win, label: str) -> dict | None:                       "caller's container, so it can be read or shown as a thumbnail "                       "without a separate pull_file."),         }+        # A thumbnail whose CANVAS is empty proves the editor is on the part, not+        # that the part is drawn - and it looks like a success to anyone glancing+        # at it. Say so on the evidence itself rather than letting a blank picture+        # speak for the verb.+        if canvas.get("checked"):+            ev["canvasRendered"] = bool(canvas.get("rendered"))+            if not canvas.get("rendered"):+                ev["_canvasHint"] = (+                    "The window is correct but its drawing canvas came back BLANK "+                    f"({canvas.get('distinctColors')} distinct colour(s)). This is not a "+                    "capture fault - KiCad draws the canvas through OpenGL, which "+                    "produces no picture on a host with no usable GPU or no active "+                    "console session (a locked or disconnected desktop does it too). "+                    "Treat this thumbnail as proof of the WINDOW, not of the part. "+                    "kicad_enable_software_opengl is the last-resort fix on a box "+                    "that genuinely has no GPU."+                )+        return ev     except Exception:         return None