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.64
5 files changed
+24−20
dashboard/kicad_dashboard.py+1−1@@ -49,7 +49,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from urllib.parse import unquote, urlparse -VERSION = "2.2.5"+VERSION = "2.2.6" AI_THREAD = "kicad-dashboard" DEFAULT_PORT = 8931 UI_DIR = Path(__file__).resolve().parent / "ui"
dashboard/ui/index.html+18−16@@ -209,7 +209,7 @@ main a:hover{border-bottom-color:var(--accent)} <section class="say" id="say" aria-labelledby="sayTitle"> <div class="say-head"> <h2 id="sayTitle" style="margin:0">Say it to your AI</h2>- <span class="say-sub">You do not drive this dashboard. You ask your AI, using the word <b>kicad</b>. Click a phrase to copy it, then paste it to your AI.</span>+ <span class="say-sub">You do not drive this dashboard. You ask your AI, using the word <b>kicad</b>. Click a phrase or its Try it to copy it, then paste it into any AI thread.</span> </div> <div class="say-list" id="sayList"></div> </section>@@ -348,7 +348,11 @@ const $=s=>document.querySelector(s); const consoleBuf=[]; ["log","warn","error"].forEach(k=>{const o=console[k].bind(console);console[k]=(...a)=>{consoleBuf.push(k+": "+a.join(" "));if(consoleBuf.length>300)consoleBuf.shift();o(...a);};}); window.onerror=(m)=>{consoleBuf.push("onerror: "+m);};-function toast(msg,kind){const t=document.createElement('div');t.className='toast '+(kind||'');t.textContent=msg;$('#toasts').appendChild(t);setTimeout(()=>t.remove(),kind==='err'?9000:4000);}+function toast(msg,kind){const t=document.createElement('div');t.className='toast '+(kind||'');+ const ICONS={ok:'M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z',err:'M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8 8 8 0 0 1 8-8 8 8 0 0 1 8 8 8 8 0 0 1-8 8'};+ if(ICONS[kind]){t.innerHTML='<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" style="flex:none;color:var(--'+(kind==='ok'?'green':'red')+')"><path fill="currentColor" d="'+ICONS[kind]+'"/></svg><span></span>';t.lastChild.textContent=msg;t.style.display='flex';t.style.alignItems='center';t.style.gap='8px';}+ else t.textContent=msg;+ $('#toasts').appendChild(t);setTimeout(()=>t.remove(),kind==='err'?9000:4000);} async function api(path,opts){const r=await fetch(path,opts);return r.json();} async function act(action,args){return api('api/action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action,target:curTarget||undefined,args:args||{}})});} @@ -465,7 +469,12 @@ $('#btnPartTour').onclick=async()=>{ // which wakes ah's parked AI backend - one press could crash the app the user // is standing in. Copy-only until the dock thread lifts the park; the server // action and this button code stay, flip the flag to restore.-const TRY_IT_PARKED=true;+const TRY_IT_PARKED=true; // still parks the SERVER staging path; the button below never calls it+async function copyText(txt){+ try{await navigator.clipboard.writeText(txt);return true;}catch(e){}+ try{const ta=document.createElement('textarea');ta.value=txt;ta.style.position='fixed';ta.style.opacity='0';+ document.body.appendChild(ta);ta.select();const ok=document.execCommand('copy');ta.remove();return ok;}catch(e){return false;}+} async function buildSay(){ let phrases=[]; try{const r=await act('say');phrases=((r.data||r).phrases)||[];}catch(e){}@@ -476,21 +485,14 @@ async function buildSay(){ const b=document.createElement('button');b.className='say-phrase';b.type='button'; b.setAttribute('data-tip','Copy this phrase'); b.innerHTML='“'+p.replace(/kicad/gi,m=>'<em>'+m+'</em>')+'”';- b.onclick=async()=>{try{await navigator.clipboard.writeText(p);toast('copied: '+p,'ok');}catch(e){toast('could not copy: '+e,'err');}};+ b.onclick=async()=>{(await copyText(p))?toast('Copied to clipboard. Paste it into any AI thread.','ok'):toast('could not copy - select and copy it by hand','err');}; item.appendChild(b);- if(TRY_IT_PARKED){list.appendChild(item);continue;}+ // "Try it" copies the phrase - it makes ZERO /ai-threads calls (John,+ // 2026-08-23 evening: no live staging for now; the user pastes into any+ // AI thread themselves, and the toast tells them exactly that). const t=document.createElement('button');t.className='say-try';t.type='button';t.textContent='Try it';- t.setAttribute('data-tip','Stage this phrase in this app\'s AI thread (not submitted, you press Enter)');- t.onclick=async()=>{- t.disabled=true;- try{- const r=await act('try_phrase',{phrase:p});const d=r.data||r;- if(d.staged){toast('staged in your AI thread: '+p+' (press Enter there to send)','ok');}- else{try{await navigator.clipboard.writeText(p);}catch(e){}- toast('copied instead: '+(d.reason||'could not reach your AI thread')+'. Paste it into your AI.','');}- }catch(e){toast('try failed: '+e,'err');}- finally{t.disabled=false;}- };+ t.setAttribute('data-tip','Copies this phrase - paste it into any AI thread');+ t.onclick=async()=>{(await copyText(p))?toast('Copied to clipboard. Paste it into any AI thread.','ok'):toast('could not copy - select and copy it by hand','err');}; item.appendChild(t);list.appendChild(item); } }
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "kicad-bridge",- "version": "1.0.63",+ "version": "1.0.64", "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.63",+ "version": "1.0.64", "tags": [ "kicad", "pcb",
skills/kicad-bridge-dev/SKILL.md+3−1@@ -650,7 +650,9 @@ bool()'d every key, so the enum persisted as True. ### PARKED: the dashboard's "Try it" staging (ship night 2026-08-23) -The Say it strip is COPY-ONLY until the dock thread gives the all-clear: the+RULING UPDATE (same night): the Try it BUTTONS are back, but as COPY + TOAST+("Copied to clipboard. Paste it into any AI thread.") with ZERO /ai-threads+calls from page or server. Only the STAGING path stays parked: the ai-threads run-prompt path has an unverified crash fix (it has killed the whole ah process on a dev shell), and any /ai-threads call wakes ah's parked AI backend. TWO flags gate it, and BOTH must flip to unpark: