← Commit history

demo: every surface card now shows a live progress bar - poll verb derived from the show verb's namespace, so a bridge that ships <prefix>_progress animates with no per-app code

John Lauer ·2c8e932bc0 ·2mo ago ·parent 0bfe8ae
1 file changed +45
web-control-demo.html+45
@@ -829,12 +829,57 @@ for (const w of mine) {   // cfg.pickTitle: render just the one window this surface is about.   // cfg.fallback {cmd,args}: retried when the primary verb is unknown (a not-yet-   //   shipped show verb degrades to today's open verb instead of a dead button).+  // Live progress for a surface open, using the SAME canonical renderer as the+  // demo cards. Opening a symbol / board / 3D view is a 30-60s job, so a card+  // that shows only a text log leaves the user with no sense of how far along it+  // is - the exact gap the measured-progress contract exists to close.+  //+  // The poll verb is DERIVED from the show verb's namespace+  // (kicad_show_symbol -> kicad_progress), so any bridge that ships one gets a+  // bar here with no per-app code, and one that does not (fusion today, which+  // has only fusion_demo_progress) simply keeps the text log: the poll 404s and+  // is ignored. That is the generic-renderer rule applied to the demo itself.+  //+  // It polls ALONGSIDE the slow verb rather than after it, for the usual reason:+  // the show verb's own response cannot animate anything, because it does not+  // arrive until the work is already done.+  function startSurfaceProgress(c, showCmd) {+    var prefix = String(showCmd || "").split("_")[0];+    if (!prefix) return;+    var pollCmd = prefix + "_progress";+    c._lastShotKey = null;+    (function tick() {+      // The card disables its button for the whole run and re-enables it on+      // EVERY exit path, so that flag is the one stop condition that cannot+      // leave this loop polling forever.+      if (!c.btn.disabled) return;+      postVerb(pollCmd, {}).then(function (res) {+        if (!c.btn.disabled) return;+        var block = readProgressBlock(res.j);+        if (block) {+          renderProgressBlock(c.card, block);+          var ss = readStepShot(block);+          var img = ss && (ss.image || ss.base64);+          if (img) {+            var key = (ss.step || "") + "|" + (ss.title || "") + "|" + String(img).length;+            if (key !== c._lastShotKey) {+              c._lastShotKey = key;+              addShot(c.card, img, "step: " + (ss.step || ss.title || "step"));+            }+          }+        }+        setTimeout(tick, 1000);+      }).catch(function () { setTimeout(tick, 1500); });+    })();+  }+   function showSurface(c, cfg) {     c.btn.disabled = true;     cardLog(c.card).textContent = "";     var sb = c.card.querySelector("div.shots"); if (sb) sb.innerHTML = "";     setRes(c.res, "pending", "asking permission (choose “Allow 1 hr”) ...", "on " + esc(selfLabel()) + " - " + (cfg.note || "opens in the background, then we screenshot it"));     clog(c, "Target machine: " + selfLabel());+    startSurfaceProgress(c, cfg.showCmd);     var staged = cfg.stage ? cfg.stage(c) : Promise.resolve();     staged.then(function () {       var args = (typeof cfg.showArgs === "function") ? cfg.showArgs() : (cfg.showArgs || {});