← Commit history

picker shows versions + latest + staleness

John Lauer ·07643ed62a ·2mo ago ·parent bf1ebce
1 file changed +30−7
web-control-demo.html+30−7
@@ -186,17 +186,40 @@ for (const w of mine) {   // WHO is "this machine": ab 2.0.53+ reports its relay client name at
   // /status.machine, so every card can say "on AdomLapper" instead of a vague
   // "this machine" (John: "always tell me which machine this demo is running on").
-  var selfName = null;
+  var selfName = null, selfVersion = null, latestVersion = null;
   function loadSelfName() {
     return fetch(AB + "/status").then(function (r) { return r.json(); }).then(function (s) {
       var m = s && s.machine;
       if (m && (m.clientName || m.hostname)) selfName = m.clientName || m.hostname;
+      if (s && s.version) selfVersion = s.version;
       return selfName;
     }).catch(function () { return null; });
   }
-  loadSelfName().then(function () {
-    // Name the "self" option as soon as we know who self IS - no Detect needed.
-    if (selfName && sel.options.length && !sel.options[0].value) sel.options[0].textContent = "this machine (" + selfName + ")";
+  // The LATEST published ab, from the wiki's own update manifest (the same file
+  // the auto-updater reads) - so the picker can say whether a machine is
+  // up to date, not just what it runs (John, 2026-08-17).
+  function loadLatestVersion() {
+    return fetch("https://wiki.adom.inc/api/v1/pages/adom-bridge/files/version.json")
+      .then(function (r) { return r.json(); })
+      .then(function (v) { latestVersion = v && v.version; return latestVersion; })
+      .catch(function () { return null; });
+  }
+  function verParts(v) { return String(v || "0").split(".").map(function (x) { return parseInt(x, 10) || 0; }); }
+  function verBehind(v) { // true when v < latestVersion
+    if (!v || !latestVersion) return false;
+    var a = verParts(v), b = verParts(latestVersion);
+    for (var i = 0; i < 3; i++) { if ((a[i] || 0) < (b[i] || 0)) return true; if ((a[i] || 0) > (b[i] || 0)) return false; }
+    return false;
+  }
+  function verTag(v) { // "(v2.0.54 - update available)" | "(v2.0.55, latest)" | "(v2.0.55)"
+    if (!v) return "";
+    if (!latestVersion) return " (v" + v + ")";
+    return verBehind(v) ? " (v" + v + " - update available, latest " + latestVersion + ")" : " (v" + v + ", latest)";
+  }
+  function selfOptionLabel() { return "this machine (" + selfLabel() + ")" + (selfVersion ? verTag(selfVersion) : ""); }
+  Promise.all([loadSelfName(), loadLatestVersion()]).then(function () {
+    // Name + version the "self" option as soon as we know - no Detect needed.
+    if (selfName && sel.options.length && !sel.options[0].value) sel.options[0].textContent = selfOptionLabel();
   });
   // The label for where a run lands. Verbs that pass no target run on SELF.
   function machineLabel(target) {
@@ -1068,10 +1091,10 @@ for (const w of mine) {           var n = (t.name || t.hostname || "").toLowerCase();
           return !(selfName && n === selfName.toLowerCase());
         });
-        sel.innerHTML = '<option value="">this machine (' + esc(selfLabel()) + ')</option>';
-        peers.forEach(function (t) { var o = document.createElement("option"); o.value = t.name || t.hostname; o.textContent = (t.name || t.hostname) + (t.version ? " (v" + t.version + ")" : ""); sel.appendChild(o); });
+        sel.innerHTML = '<option value="">' + esc(selfOptionLabel()) + '</option>';
+        peers.forEach(function (t) { var o = document.createElement("option"); o.value = t.name || t.hostname; o.textContent = (t.name || t.hostname) + verTag(t.version); sel.appendChild(o); });
         if (peers.length >= 1) { var a = document.createElement("option"); a.value = "all"; a.textContent = "ALL machines (fan out)"; sel.appendChild(a); }
-        document.getElementById("target-count").textContent = selfLabel() + (peers.length ? " + " + peers.length + " more" : " only");
+        document.getElementById("target-count").textContent = selfLabel() + (peers.length ? " + " + peers.length + " more" : " only") + (latestVersion ? " ยท latest ab v" + latestVersion : "");
       })
       .catch(function (e) {
         btn.disabled = false; fetchDied(e);