← Commit history
dashboard/kicad_dashboard.py+3−2
@@ -49,7 +49,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from urllib.parse import unquote, urlparse -VERSION = "2.1.0"+VERSION = "2.1.1" AI_THREAD = "kicad-dashboard" DEFAULT_PORT = 8931 UI_DIR = Path(__file__).resolve().parent / "ui"@@ -945,7 +945,8 @@ class Handler(BaseHTTPRequestHandler):      def do_GET(self):         path = urlparse(self.path).path.rstrip("/") or "/"-        if path in ("/favicon.png", "/favicon.ico", "/favicon.svg", "/kicad-logo.png"):+        if path.endswith(("/favicon.png", "/favicon.ico", "/favicon.svg", "/kicad-logo.png")):+            # endswith: the adom.cloud proxy mounts the app under a sub-path.             try:                 return self._send(200, "image/png", LOGO_PATH.read_bytes())             except Exception:
dashboard/ui/index.html+6
@@ -17,6 +17,12 @@ @font-face{font-family:'Satoshi';src:url('https://adom.inc/fonts/Satoshi-Regular.woff2') format('woff2');font-weight:400} @font-face{font-family:'Satoshi';src:url('https://adom.inc/fonts/Satoshi-Medium.woff2') format('woff2');font-weight:500} body{background:var(--bg);color:var(--text-body);font:15px/1.5 'Satoshi',sans-serif;min-height:100vh}+/* House scrollbar everywhere (scrollbars sub-skill): thin, transparent track, translucent pill, teal on hover. */+html{scrollbar-width:thin;scrollbar-color:rgba(139,148,158,.35) transparent}+::-webkit-scrollbar{width:8px;height:8px}+::-webkit-scrollbar-track{background:transparent}+::-webkit-scrollbar-thumb{background:rgba(139,148,158,.35);border-radius:999px;border:2px solid transparent;background-clip:padding-box}+::-webkit-scrollbar-thumb:hover{background:var(--accent);background-clip:padding-box} header{height:44px;display:flex;align-items:center;gap:12px;padding:0 16px;background:var(--surface);border-bottom:1px solid var(--border)} .brand{display:flex;align-items:center;gap:8px;font-family:'Familjen Grotesk',sans-serif;font-weight:600;color:var(--accent);white-space:nowrap;cursor:default} .brand img{width:18px;height:18px;border-radius:4px;display:block}
package.json+3−2
@@ -1,6 +1,6 @@ {   "slug": "kicad-bridge",-  "version": "1.0.51",+  "version": "1.0.52",   "type": "app",   "description": "Skills for your container so your AI knows how to drive the KiCad bridge. The bridge runtime itself is the release zip; Adom Bridge loads that.",   "tags": [@@ -47,7 +47,8 @@     "dashboard/ui/assets/shots/taskbar-none.png",     "dashboard/ui/assets/icons/settings-cog.svg",     "kicad-bridge-hero-v6.png",-    "docs/img/dock-kicad.svg"+    "docs/img/dock-kicad.svg",+    "tools/capture_taskbar_shots.py"   ],   "hero": {     "path": "kicad-bridge-hero-v6.png"
page.json+3−2
@@ -4,7 +4,7 @@   "slug": "kicad-bridge",   "title": "KiCad - the KiCad Bridge",   "brief": "Reference implementation of the KiCad bridge — multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.",-  "version": "1.0.51",+  "version": "1.0.52",   "tags": [     "kicad",     "pcb",@@ -106,7 +106,8 @@     "dashboard/ui/assets/shots/taskbar-none.png",     "dashboard/ui/assets/icons/settings-cog.svg",     "kicad-bridge-hero-v6.png",-    "docs/img/dock-kicad.svg"+    "docs/img/dock-kicad.svg",+    "tools/capture_taskbar_shots.py"   ],   "org": "adom",   "name": "kicad-bridge",
skills/kicad-bridge-dev/SKILL.md+14
@@ -628,6 +628,20 @@ Implementation (win_taskbar.py):      Program Files shortcuts (all targets exist), "it's upstream default,      live with it" (ConfRoomROG disproved). +### Dashboard settings follow the Adom settings-layout skill (2026-08-23)++The cog opens a DISMISSIBLE MODAL (Escape, X, click-outside, 25% dim, never+blur), cards with the 3px teal accent, breadcrumb row names, Default hints,+the house toggle, two-click Restore defaults, optimistic 5s-bounded saves.+`activityIndicator` is CHOICE CARDS, one REAL capture per option: the PNGs in+`dashboard/ui/assets/shots/` are 1:1 crops of AdomLapper's actual taskbar in+each state (bar painted via desktop_taskbar, flash via FlashWindowEx, badge via+the overlay verb). NEVER replace them with a drawing; re-capture with+`tools/capture_taskbar_shots.py` after putting the button in the state. Win11+keeps the attention tint after FLASHW_STOP: hide/re-show the window (never+foreground it) to clear it. Backend trap fixed the same day: set_settings+bool()'d every key, so the enum persisted as True.+ ### The footprint stale-cache remedy is a TREE REFRESH, not a restart (2026-08-22, #51)  `View > Refresh` (F5) on the Footprint Editor frame reloads the library TREE,
tools/capture_taskbar_shots.pyadded+57
@@ -0,0 +1,57 @@+#!/usr/bin/env python3+"""Re-capture the REAL taskbar screenshots the dashboard settings modal shows+(settings-layout skill: never fabricate). Drives AdomLapper: finds the pcbnew+button by its green icon, crops 168x56 at 1:1. Usage: <name> [frames]; put the+button in the state first (desktop_taskbar progress/overlay, FlashWindowEx)."""+import base64, io, json, subprocess, sys, time+from PIL import Image++SHOTS = "/home/adom/project/adom-desktop-kicad-bridge/dashboard/ui/assets/shots"+W, H = 168, 56++def grab():+    out = subprocess.run(["timeout", "120", "adom-bridge", "--ai-thread", "kicad-icon",+                          "--target", "AdomLapper", "desktop_screenshot_screen", "{}"],+                         capture_output=True, text=True).stdout+    d = json.loads(out); inner = json.loads(d["output"])["data"]+    return Image.open(io.BytesIO(base64.b64decode(inner["image"]))).convert("RGB")++def button_center(im):+    w, h = im.size+    band = im.crop((0, h - 58, w, h)); px = band.load()+    cols = []+    for x in range(band.width):+        n = sum(1 for y in range(band.height)+                if (lambda r, g, b: g > 110 and g > r + 40 and g > b + 40 and r < 120)(*px[x, y]))+        if n >= 6: cols.append(x)+    runs, s, p = [], None, None+    for x in cols:+        if s is None: s = x+        elif x != p + 1: runs.append((s, p)); s = x+        p = x+    if s is not None: runs.append((s, p))+    runs = [r for r in runs if 14 <= r[1] - r[0] <= 30]+    if not runs: raise SystemExit("green icon not found")+    a, b = runs[-1]+    return (a + b) // 2, h - 30++def orange(im):+    px = im.load(); n = 0+    for x in range(im.width):+        for y in range(im.height):+            r, g, b = px[x, y]+            if r > 170 and 70 < g < 180 and b < 90: n += 1+    return n++name = sys.argv[1]; frames = int(sys.argv[2]) if len(sys.argv) > 2 else 1+best, bestScore = None, -1+for i in range(frames):+    im = grab(); cx, cy = button_center(im)+    crop = im.crop((cx - W // 2, cy - H // 2, cx + W // 2, cy + H // 2))+    sc = orange(crop)+    print(f"frame {i}: center=({cx},{cy}) orange={sc}")+    if sc > bestScore: best, bestScore = crop, sc+    if frames > 1: time.sleep(0.45)+best.save(f"{SHOTS}/{name}.png")+best.resize((W * 4, H * 4), Image.NEAREST).save(f"/tmp/claude-1001/-home-adom-project/a82462cd-99a1-4a08-b142-383081e6ec8a/scratchpad/shot-{name}-x4.png")+print("saved", name, "orange", bestScore)