app
KiCad - the KiCad Bridge
Public Made by Adomby adom
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.
← Commit history
Publish 1.0.66
4 files changed
+34−5
dashboard/kicad_dashboard.py+4−1@@ -49,7 +49,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from urllib.parse import unquote, urlparse -VERSION = "2.2.7"+VERSION = "2.3.0" AI_THREAD = "kicad-dashboard" DEFAULT_PORT = 8931 UI_DIR = Path(__file__).resolve().parent / "ui"@@ -1003,6 +1003,9 @@ def do_action(target, name, args): return {"success": False, "error": "phrase required"} r = stage_phrase(phrase) return {"success": True, "phrase": phrase, **r}+ if name == "demo_control":+ cmd = str(args.get("cmd") or "").strip().lower()+ return ab(target, "kicad_demo", {"control": cmd}, timeout=30) if name == "restore_settings": return ab(target, "kicad_set_settings", {"restoreDefaults": True}, timeout=60) if name == "part_tour":
dashboard/ui/index.html+28−2@@ -228,7 +228,15 @@ main a:hover{border-bottom-color:var(--accent)} <div class="card"> <div class="led-label">Guided demo</div> <div class="led-sub">Six beats on a generated sample project: schematic, 2D board, 3D board, symbol, footprint, 3D chip. Runs entirely in the background, and each finished beat streams back a screenshot.</div>- <div class="btnrow"><button class="btn primary" id="btnDemo">Run the demo</button></div>+ <div class="btnrow">+ <button class="btn primary" id="btnDemo">Run the demo</button>+ <span id="demoCtl" style="display:none;gap:8px">+ <button class="btn" id="btnDemoPause" data-tip="Pause after the current beat finishes">Pause</button>+ <button class="btn" id="btnDemoPrev" data-tip="Replay the previous beat">Prev</button>+ <button class="btn" id="btnDemoNext" data-tip="Move on to the next beat">Next</button>+ <button class="btn" id="btnDemoExit" data-tip="Stop the tour, silence narration, clear the caption" style="color:var(--red)">Exit</button>+ </span>+ </div> <div class="bar" id="demoBar"><div></div></div> <div class="barlabel" id="demoLabel"></div> <div class="shots" id="demoShots"></div>@@ -436,8 +444,11 @@ $('#btnDemo').onclick=async()=>{ const s=await act('demo');const d=s.data||s; if(d.success===false){toast('demo failed to start: '+(d.error||'unknown'),'err');b.disabled=false;return;} $('#demoBar').style.display='block';+ $('#demoCtl').style.display='inline-flex';+ let demoPaused=false; const poll=setInterval(async()=>{ const p=await act('demo_progress');const q=p.data||p;+ if(typeof q.paused==='boolean'&&q.paused!==demoPaused){demoPaused=q.paused;$('#btnDemoPause').textContent=demoPaused?'Play':'Pause';} $('#demoBar>div').style.width=(q.percent||0)+'%'; $('#demoLabel').textContent=(q.percent||0)+'% '+(q.stepLabel||''); if(q.stepShot&&q.stepShot.image){@@ -445,9 +456,24 @@ $('#btnDemo').onclick=async()=>{ c.innerHTML='<img src="data:image/png;base64,'+q.stepShot.image+'" alt="'+(q.stepShot.title||'')+'"><div class="cap">'+(q.stepShot.title||q.stepShot.step||'')+'</div>'; $('#demoShots').appendChild(c); }- if(q.done){clearInterval(poll);b.disabled=false;toast(q.error?('demo failed: '+q.error):'demo complete','' + (q.error?'err':'ok'));refresh(true);}+ if(q.done){clearInterval(poll);b.disabled=false;$('#demoCtl').style.display='none';toast(q.error?('demo failed: '+q.error):(q.result&&q.result.exited?'demo stopped':'demo complete'),'' + (q.error?'err':'ok'));refresh(true);} },3000); };+// Demo transport: mirrors the ab toast buttons; the bridge applies commands+// between beats (a beat that is mid-open finishes first).+async function demoCtl(cmd){+ const r=await act('demo_control',{cmd});const d=(r&&r.data)||r||{};+ if(d.error){toast('demo control failed: '+d.error,'err');return;}+ if(cmd==='pause'){$('#btnDemoPause').textContent='Play';toast('pausing after this beat','ok');}+ if(cmd==='play'){$('#btnDemoPause').textContent='Pause';toast('resuming','ok');}+ if(cmd==='exit')toast('stopping the demo','ok');+ if(cmd==='next')toast('skipping ahead','ok');+ if(cmd==='prev')toast('replaying the previous beat','ok');+}+$('#btnDemoPause').onclick=()=>demoCtl($('#btnDemoPause').textContent==='Pause'?'pause':'play');+$('#btnDemoPrev').onclick=()=>demoCtl('prev');+$('#btnDemoNext').onclick=()=>demoCtl('next');+$('#btnDemoExit').onclick=()=>demoCtl('exit'); $('#btnPartTour').onclick=async()=>{ const page=$('#partPage').value.trim(); const b=$('#btnPartTour');b.disabled=true;
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "kicad-bridge",- "version": "1.0.65",+ "version": "1.0.66", "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": [
page.json+1−1@@ -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.65",+ "version": "1.0.66", "tags": [ "kicad", "pcb",