← Commit history

demo: pick the part from a dropdown of 10 real wiki components (40 KB resistor to a 3.7 MB JST header) instead of hardcoding IN-S42ATR - the three largest deliberately cross the /command body cap so ab#90 is testable from the page

John Lauer ·fe2019c3f5 ·2mo ago ·parent ce02a4a
1 file changed +115−21
web-control-demo.html+115−21
@@ -161,10 +161,17 @@   <div class="grp">Do something - each asks permission, streams live status, then captures the app's FULL window state</div>   <div class="cards" id="grp-do"></div> -  <div class="grp">KiCad surfaces - a REAL wiki part, end to end: <a href="https://wiki.adom.inc/aravk/in-s42atr" target="_blank" rel="noopener">Inolux IN-S42ATR</a> (red 0402 SMD LED). Each button fetches the part's files from its wiki page, installs them into your real KiCad, opens JUST that surface in the background, and renders it from kicad_state. This is the whole circle: wiki component to the app the EE designs in.</div>+  <div class="bar" id="part-bar">+    <span>Demo part:</span>+    <span id="part-picker"></span>+    <span id="part-note" class="pill" style="border:none"></span>+    <a id="part-link" href="#" target="_blank" rel="noopener" style="font-size:12px">open on the wiki &#8599;</a>+  </div>++  <div class="grp">KiCad surfaces - a REAL wiki part, end to end. Each button fetches the SELECTED part's files from its wiki page, installs them into your real KiCad, opens JUST that surface in the background, and renders it from kicad_state. This is the whole circle: wiki component to the app the EE designs in.</div>   <div class="cards" id="grp-kicad"></div> -  <div class="grp">Fusion surfaces - the SAME wiki part (IN-S42ATR.lbr + .step from its page), one first-class verb per content type (fusion_show_*, v1.9.10+). Component surfaces stage the library automatically; design surfaces additionally need the part project staged (fusion-bridge #30).</div>+  <div class="grp">Fusion surfaces - the SAME selected part (.lbr + .step from its page), one first-class verb per content type (fusion_show_*, v1.9.10+). Component surfaces stage the library automatically; design surfaces additionally need the part project staged (fusion-bridge #30).</div>   <div class="cards" id="grp-fusion"></div>    <div class="grp">Multi-machine - fan a verb across every connected computer</div>@@ -1292,13 +1299,94 @@ for (const w of mine) {   // 3D board needs a routed board, needs a schematic, needs the library — so   // each stage function climbs exactly as far as its surface requires, and each   // is MEMOIZED (one staging per page load; every later button reuses it).-  var PART = {-    name: "IN-S42ATR",-    kicadLib: "Adom",                       // kicad_install_symbol installs into this library-    page: "https://wiki.adom.inc/aravk/in-s42atr",-    filesBase: "https://wiki.adom.inc/api/pages/aravk/in-s42atr/files/",-    dir: "~/Documents/adom-demo-parts/IN-S42ATR/"   // staged on the PC via write_file (~ expands)-  };+  // The demo part is PICKABLE, not hardcoded. Ten real wiki parts spanning the+  // size range that actually matters here: a 40 KB resistor up to a 3.9 MB+  // connector. STEP size is the interesting axis because staging goes through+  // write_file as base64 inside the /command JSON body, which caps out around+  // 2.5 MB - so the last three deliberately cross that line (see ab#90). Sizes+  // are the real .step bytes, measured off each page's files listing.+  //+  // Every entry was verified to carry ALL FOUR required files+  // (.kicad_sym + .kicad_mod + .step + .lbr). Plenty of jellybeans on the wiki+  // (1N4148, 2N7000, LM1117, TL074) are missing one and would fail halfway+  // through staging, so they are deliberately not offered.+  var PARTS = [+    { name: "CR0603-FX-5601ELF",   owner: "aravk", slug: "cr0603-fx-5601elf",   step: 40618,   kind: "resistor 0603" },+    { name: "CL21B104KBCNFNC",     owner: "aravk", slug: "cl21b104kbcnfnc",     step: 155456,  kind: "capacitor 0805" },+    { name: "WS2812B-2020",        owner: "aravk", slug: "ws2812b-2020",        step: 120696,  kind: "addressable LED" },+    { name: "IN-S42ATR",           owner: "aravk", slug: "in-s42atr",           step: 214421,  kind: "red 0402 LED" },+    { name: "tja1051t-3-118",      owner: "adom",  slug: "tja1051t-3-118",      step: 434142,  kind: "CAN transceiver" },+    { name: "nRF52840-CKAA",       owner: "john",  slug: "nrf52840-ckaa",       step: 593793,  kind: "BLE SoC (WLCSP)" },+    { name: "type-c-31-m-12",      owner: "adom",  slug: "type-c-31-m-12",      step: 1059856, kind: "USB-C receptacle" },+    { name: "ads1220irvar",        owner: "adom",  slug: "ads1220irvar",        step: 1930666, kind: "24-bit ADC - the ab#90 fixture" },+    { name: "rp2350b",             owner: "adom",  slug: "rp2350b",             step: 3059576, kind: "RP2350B MCU" },+    { name: "s10b-ph-sm4-tb",      owner: "adom",  slug: "s10b-ph-sm4-tb",      step: 3921735, kind: "JST PH 10-pin header - largest" }+  ];+  // A spec's label is a FUNCTION when it names the part (so it can be re-rendered+  // when the part changes) and a plain STRING when it does not ("2D board", ...).+  // Accept both rather than forcing every spec to become a function.+  function labelOf(spec) { return typeof spec.label === "function" ? spec.label() : spec.label; }+  function labelFnOf(spec) { return typeof spec.label === "function" ? spec.label : function () { return spec.label; }; }++  // Cards whose LABEL names the part, so switching parts re-renders them.+  var PART_CARDS = [];++  function fmtKB(n) { return n >= 1048576 ? (n / 1048576).toFixed(2) + " MB" : Math.round(n / 1024) + " KB"; }++  // Switch the demo part. Everything the OLD part staged is per-part state and+  // must be dropped, or a later click would open the previous part's project:+  // kicadProject / lbrPath / stepPath are absolute paths under the old part's+  // directory, and _fusionPart caches the staged library.+  function setPart(idx) {+    PART = partFrom(PARTS[idx]);+    _fusionPart = null;+    PART_CARDS.forEach(function (e) {+      e.cc.btn.textContent = e.label();+      setRes(e.cc.res, "idle", "not run yet");+      var log = e.cc.card.querySelector("pre.slog"); if (log) log.textContent = "";+      var shots = e.cc.card.querySelector("div.shots"); if (shots) shots.innerHTML = "";+      var bar = e.cc.card.querySelector("div.pbar"); if (bar) bar.remove();+      var meta = e.cc.card.querySelector(".pbar-meta"); if (meta) meta.remove();+    });+    var link = document.getElementById("part-link");+    if (link) { link.href = PART.page; link.textContent = "open " + PART.name + " on the wiki ↗"; }+    var note = document.getElementById("part-note");+    if (note) {+      note.innerHTML = esc(PART.kind) + " - .step is <strong>" + fmtKB(PART.stepBytes) + "</strong>" ++        (PART.stepBytes > 2000000+          ? ' <span style="color:#d29922">- over the ~2.5 MB /command body cap, so staging the 3D model is expected to 413 (ab#90); the symbol and footprint still work</span>'+          : "");+    }+  }++  function buildPartPicker() {+    var host = document.getElementById("part-picker");+    if (!host) return;+    var sel = document.createElement("select");+    PARTS.forEach(function (d, i) {+      var o = document.createElement("option");+      o.value = String(i);+      o.textContent = d.name + "  -  " + d.kind + "  (" + fmtKB(d.step) + ")";+      sel.appendChild(o);+    });+    sel.value = "3";+    sel.addEventListener("change", function () { setPart(parseInt(sel.value, 10)); });+    host.appendChild(sel);+    setPart(3);+  }++  function partFrom(d) {+    return {+      name: d.name,+      kicadLib: "Adom",                     // kicad_install_symbol reports the real library back+      stepBytes: d.step,+      kind: d.kind,+      page: "https://wiki.adom.inc/" + d.owner + "/" + d.slug,+      filesBase: "https://wiki.adom.inc/api/pages/" + d.owner + "/" + d.slug + "/files/",+      dir: "~/Documents/adom-demo-parts/" + d.name + "/"   // staged via write_file (~ expands)+    };+  }+  var PART = partFrom(PARTS[3]);            // IN-S42ATR: small, fast, known-good default   function fetchPartText(f) { return fetch(PART.filesBase + f).then(function (r) { if (!r.ok) throw new Error("wiki fetch " + f + ": HTTP " + r.status); return r.text(); }); }   function fetchPartBytes(f) { return fetch(PART.filesBase + f).then(function (r) { if (!r.ok) throw new Error("wiki fetch " + f + ": HTTP " + r.status); return r.arrayBuffer(); }); }   function b64Text(s) { return btoa(unescape(encodeURIComponent(s))); }@@ -1385,16 +1473,17 @@ for (const w of mine) {   // ALL EIGHT open cold + background (the 3D warm-only limitation is obsolete:   // kicad-bridge #26 closed via the Win32 menu tier in 0.9.99).   [-    { label: "Symbol (" + PART.name + ")", cmd: "kicad_show_symbol", args: function () { return { symbolName: PART.name, libraryName: PART.kicadLib }; }, stage: stageKicadPart, pick: "symbol editor", note: "installs the part from its wiki page, then shows JUST the symbol" },-    { label: "Footprint (" + PART.name + ")", cmd: "kicad_show_footprint", args: function () { return { footprintName: PART.name, library: PART.kicadLib }; }, stage: stageKicadPart, pick: "footprint editor", note: "just the land pattern: pads + courtyard" },-    { label: "3D chip (" + PART.name + ")", cmd: "kicad_show_3d_chip", args: function () { return { footprintName: PART.name, library: PART.kicadLib }; }, stage: stageKicadPart, pick: "3d viewer", note: "the part's STEP model on its footprint" },-    { label: "Library (" + PART.name + " in '" + PART.kicadLib + "')", cmd: "kicad_show_library", args: function () { return { libraryName: PART.kicadLib }; }, stage: stageKicadPart, pick: "symbol editor", note: "the library holding the symbol + footprint + 3D model" },-    { label: "Schematic (just the " + PART.name + ")", cmd: "kicad_show_schematic", args: function () { return { filePath: (PART.kicadProject || {}).schPath }; }, stage: stageKicadProject, pick: "schematic editor", note: "the generated single-component schematic (kicad_make_part_project)" },+    { label: function () { return "Symbol (" + PART.name + ")"; }, cmd: "kicad_show_symbol", args: function () { return { symbolName: PART.name, libraryName: PART.kicadLib }; }, stage: stageKicadPart, pick: "symbol editor", note: "installs the part from its wiki page, then shows JUST the symbol" },+    { label: function () { return "Footprint (" + PART.name + ")"; }, cmd: "kicad_show_footprint", args: function () { return { footprintName: PART.name, library: PART.kicadLib }; }, stage: stageKicadPart, pick: "footprint editor", note: "just the land pattern: pads + courtyard" },+    { label: function () { return "3D chip (" + PART.name + ")"; }, cmd: "kicad_show_3d_chip", args: function () { return { footprintName: PART.name, library: PART.kicadLib }; }, stage: stageKicadPart, pick: "3d viewer", note: "the part's STEP model on its footprint" },+    { label: function () { return "Library (" + PART.name + " in '" + PART.kicadLib + "')"; }, cmd: "kicad_show_library", args: function () { return { libraryName: PART.kicadLib }; }, stage: stageKicadPart, pick: "symbol editor", note: "the library holding the symbol + footprint + 3D model" },+    { label: function () { return "Schematic (just the " + PART.name + ")"; }, cmd: "kicad_show_schematic", args: function () { return { filePath: (PART.kicadProject || {}).schPath }; }, stage: stageKicadProject, pick: "schematic editor", note: "the generated single-component schematic (kicad_make_part_project)" },     { label: "2D board (part placed, tight edge cuts)", cmd: "kicad_show_2d_board", args: function () { return { filePath: (PART.kicadProject || {}).pcbPath }; }, stage: stageKicadProject, pick: "pcb editor", note: "the part-dominant board" },     { label: "3D board (real 3D body on the board)", cmd: "kicad_show_3d_board", args: function () { return { filePath: (PART.kicadProject || {}).pcbPath }; }, stage: stageKicadProject, pick: "3d viewer", note: "the populated board in 3D - the whole ladder underneath it" },     { label: "Project (top of the tree)", cmd: "kicad_show_project", args: function () { return { filePath: (PART.kicadProject || {}).proPath }; }, stage: stageKicadProject, pick: "kicad 10", note: "the Project Manager: schematic + board hang off it" }   ].forEach(function (s) {-    var cc = card("grp-kicad", "verb", s.label);+    var cc = card("grp-kicad", "verb", labelOf(s));+    PART_CARDS.push({ cc: cc, label: labelFnOf(s) });     cc.btn.onclick = function () { showSurface(cc, { showCmd: s.cmd, showArgs: s.args, stage: s.stage, pickTitle: s.pick, stateCmd: "kicad_state", note: s.note }); };   });   var cKState = card("grp-kicad", "read", "Show KiCad state (every window)");@@ -1449,16 +1538,17 @@ for (const w of mine) {   // fusion_state (fusion-bridge #28) is the render path, so pickTitle:"fusion"   // pulls the surface's window out of the state array.   [-    { label: "Symbol (" + PART.name + ")", cmd: "fusion_show_symbol", args: function () { return { name: PART.name, libraryPath: PART.lbrPath }; }, stage: stageFusionPart, note: "full pinout in the symbol editor" },-    { label: "Footprint (" + PART.name + ")", cmd: "fusion_show_footprint", args: function () { return { name: PART.name, libraryPath: PART.lbrPath }; }, stage: stageFusionPart, note: "pads + layers in the footprint editor" },-    { label: "3D package (" + PART.name + ")", cmd: "fusion_show_3d_package", args: function () { return { name: PART.name, libraryPath: PART.lbrPath, stepPath: PART.stepPath }; }, stage: stageFusionPart, note: "attaches the part's STEP to its footprint" },-    { label: "Library (" + PART.name + ".lbr)", cmd: "fusion_show_library", args: function () { return { libraryPath: PART.lbrPath }; }, stage: stageFusionPart, note: "the Electronics Library with symbol + footprint + 3D" },-    { label: "Schematic (just the " + PART.name + ")", cmd: "fusion_show_schematic", args: function () { return {}; }, stage: stageFusionProject, note: "the generated single-component design's schematic view" },+    { label: function () { return "Symbol (" + PART.name + ")"; }, cmd: "fusion_show_symbol", args: function () { return { name: PART.name, libraryPath: PART.lbrPath }; }, stage: stageFusionPart, note: "full pinout in the symbol editor" },+    { label: function () { return "Footprint (" + PART.name + ")"; }, cmd: "fusion_show_footprint", args: function () { return { name: PART.name, libraryPath: PART.lbrPath }; }, stage: stageFusionPart, note: "pads + layers in the footprint editor" },+    { label: function () { return "3D package (" + PART.name + ")"; }, cmd: "fusion_show_3d_package", args: function () { return { name: PART.name, libraryPath: PART.lbrPath, stepPath: PART.stepPath }; }, stage: stageFusionPart, note: "attaches the part's STEP to its footprint" },+    { label: function () { return "Library (" + PART.name + ".lbr)"; }, cmd: "fusion_show_library", args: function () { return { libraryPath: PART.lbrPath }; }, stage: stageFusionPart, note: "the Electronics Library with symbol + footprint + 3D" },+    { label: function () { return "Schematic (just the " + PART.name + ")"; }, cmd: "fusion_show_schematic", args: function () { return {}; }, stage: stageFusionProject, note: "the generated single-component design's schematic view" },     { label: "2D board (part placed)", cmd: "fusion_show_2d_board", args: function () { return {}; }, stage: stageFusionProject, note: "the PCB editor view of the staged design" },     { label: "3D board (real 3D body)", cmd: "fusion_show_3d_board", args: function () { return {}; }, stage: stageFusionProject, note: "populated board in 3D: the bound urn renders the real component" },     { label: "Project (top of the design)", cmd: "fusion_show_project", args: function () { return {}; }, stage: stageFusionProject, note: "the generated project's top level" }   ].forEach(function (s) {-    var cc = card("grp-fusion", "verb", s.label);+    var cc = card("grp-fusion", "verb", labelOf(s));+    PART_CARDS.push({ cc: cc, label: labelFnOf(s) });     cc.btn.onclick = function () {       // fallback stays a FUNCTION: its args reference paths the stage step produces.       showSurface(cc, { showCmd: s.cmd, showArgs: s.args, stage: s.stage, fallback: s.fallback, pickTitle: "fusion", stateCmd: "fusion_state", startCmd: "fusion_start", windowInfoCmd: "fusion_window_info", note: s.note });@@ -1564,6 +1654,10 @@ for (const w of mine) {         logLine("targets failed: " + e);       });   };++  // Build the part picker LAST: it re-labels the KiCad/Fusion cards, so every+  // card must already exist.+  buildPartPicker(); })(); </script> </body>