94d7a6f6d8884db6fee1e7d9b95d9c2b409cb0ef
chmalee
  Fri Jul 17 14:10:26 2026 -0700
hubSpace: add Copy link to hub button to the hub-browsing banner, refs #37705

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index 7623d5c12ec..64728facd16 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -1546,30 +1546,41 @@
         if (!url) return;
         navigator.clipboard.writeText(url).then(function() {
             let orig = btn.textContent;
             btn.textContent = "Copied";
             setTimeout(function() { btn.textContent = orig; }, 1500);
         }, function() {
             alert("Failed to copy link: " + url);
         });
     }
 
     function showHubBanner(hubName) {
         let banner = document.getElementById("hubBanner");
         let nameSpan = document.getElementById("hubBannerName");
         if (!banner || !nameSpan) return;
         nameSpan.textContent = hubName;
+        // stash a shareable connect link on the copy button, or hide it if unavailable
+        let copyBtn = document.getElementById("hubBannerCopyBtn");
+        if (copyBtn) {
+            let link = hubShareLink(hubName);
+            if (link) {
+                copyBtn.setAttribute("data-url", link);
+                copyBtn.style.display = "";
+            } else {
+                copyBtn.style.display = "none";
+            }
+        }
         banner.style.display = "";
     }
 
     function hideHubBanner() {
         let banner = document.getElementById("hubBanner");
         if (banner) banner.style.display = "none";
     }
 
     // helper object so we don't need to use an AbortController to update
     // the data this function is using
     let selectedData = {};
     // track which items the user directly selected (vs children of selected directories)
     let directlySelected = {};
     function viewAllInGenomeBrowser(ev) {
         // redirect to hgTracks with these tracks/hubs open
@@ -2345,30 +2356,34 @@
             document.getElementById("rootBreadcrumb").addEventListener("click", function(e) {
                 dataTableShowTopLevel(table);
                 dataTableCustomOrder(table);
                 dataTableEmptyBreadcrumb(table);
                 table.draw();
             });
         } else {
             table.buttons(".uploadButton").disable();
         }
         let hubBannerBtn = document.getElementById("hubBannerViewBtn");
         if (hubBannerBtn) {
             hubBannerBtn.addEventListener("click", function(e) {
                 viewHubInGenomeBrowser(uiState.currentHub);
             });
         }
+        let hubBannerCopyBtn = document.getElementById("hubBannerCopyBtn");
+        if (hubBannerCopyBtn) {
+            hubBannerCopyBtn.addEventListener("click", copyHubLinkFromBanner);
+        }
         table.on("select", function(e, dt, type, indexes) {
             indexes.forEach(function(i) {
                 doRowSelect(e.type, dt, i);
             });
         });
         table.on("deselect", function(e, dt, type, indexes) {
             indexes.forEach(function(i) {
                 doRowSelect(e.type, dt, i);
             });
         });
         table.on("click", function(e) {
             let copyIcon = e.target.closest ? e.target.closest(".copyLinkIcon") : null;
             if (copyIcon) {
                 e.stopPropagation();
                 e.preventDefault();