dc34085c59de1a3a941e2c34318d9fd8af753579
chmalee
  Fri Jul 17 14:27:46 2026 -0700
hubSpace: rename share buttons to Share hub, keep share banner always present, 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 64728facd16..3f1ac0a5e3e 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -1706,57 +1706,63 @@
             }
             span.textContent = label;
             bannerSpan.textContent = label;
             // (re) set up the handlers for the selected file info div:
             let viewBtn = document.getElementById("viewSelectedFiles");
             viewBtn.addEventListener("click", viewAllInGenomeBrowser);
             viewBtn.textContent = "View selected";
             let deleteBtn = document.getElementById("deleteSelectedFiles");
             deleteBtn.style.display = "inline-block";
             deleteBtn.addEventListener("click", deleteFileList);
             deleteBtn.textContent = "Delete selected";
             // mirror the controls in the banner above the table
             let bannerViewBtn = document.getElementById("viewSelectedFilesBanner");
             bannerViewBtn.addEventListener("click", viewAllInGenomeBrowser);
             bannerViewBtn.textContent = "View selected";
+            bannerViewBtn.style.display = "inline-block";
             let bannerDeleteBtn = document.getElementById("deleteSelectedFilesBanner");
             bannerDeleteBtn.addEventListener("click", deleteFileList);
             bannerDeleteBtn.textContent = "Delete selected";
+            bannerDeleteBtn.style.display = "inline-block";
             // when exactly one hub is selected, offer a shareable connect link
             let copyBtn = document.getElementById("copyHubLinkBanner");
             let singleHub = (data.length === 1 && data[0].fileType === "dir" &&
                 !data[0].parentDir && hubHasHubTxt(data[0].fullPath)) ? data[0].fullPath : null;
             let singleHubLink = singleHub ? hubShareLink(singleHub) : null;
             if (singleHubLink) {
-                copyBtn.textContent = "Copy link to hub";
+                copyBtn.textContent = "Share hub";
                 copyBtn.setAttribute("data-url", singleHubLink);
                 copyBtn.addEventListener("click", copyHubLinkFromBanner);
                 copyBtn.style.display = "inline-block";
             } else {
                 copyBtn.style.display = "none";
             }
         } else {
             span.textContent = "";
-            bannerSpan.textContent = "";
+            // banner stays present at the top level, so show a zero count and no buttons
+            bannerSpan.textContent = "0 hub";
+            document.getElementById("viewSelectedFilesBanner").style.display = "none";
+            document.getElementById("deleteSelectedFilesBanner").style.display = "none";
             document.getElementById("copyHubLinkBanner").style.display = "none";
         }
 
         // set the visibility of the placeholder text and info text
         spanParentDiv.style.display = numSelected === 0 ? "none": "block";
         let placeholder = document.getElementById("placeHolderInfo");
         placeholder.style.display = numSelected === 0 ? "block" : "none";
-        banner.style.display = (numSelected === 0 || !atTopLevel) ? "none" : "";
+        // the share banner is always shown at the top level, hidden inside a hub
+        banner.style.display = atTopLevel ? "" : "none";
     }
 
     function handleCheckboxSelect(evtype, table, selectedRow) {
         // depending on the state of the checkbox, we will be adding information
         // to the div, or removing information. We also potentially checked/unchecked
         // all of the checkboxes if the selectAll box was clicked.
 
         // The data variable will hold all the information we want to keep visible in the info div
         let data = [];
         // The selectedData global holds the actual information needed for the view/delete buttons
         // to work, so data plus any child rows
         selectedData = {};
         // Track only the rows the user directly selected (not children)
         directlySelected = {};
 
@@ -1828,57 +1834,59 @@
         let currSearches = table.search.fixed().toArray();
         currSearches.forEach((name) => table.search.fixed(name, null));
     }
 
     function dataTableShowTopLevel(table) {
         // show all the "root" files, which are files (probably mostly directories)
         // with no parentDir
         clearSearch(table);
         // deselect any selected rows like Finder et al when moving into/upto a directory
         table.rows({selected: true}).deselect();
         table.search.fixed("showRoot", function(searchStr, rowData, rowIx) {
             return !rowData.parentDir;
         });
         uiState.currentHub = "";
         hideHubBanner();
+        updateSelectedFileDiv(null);
     }
 
     function dataTableShowDir(table, dirName, dirFullPath) {
         // show the directory and all immediate children of the directory
         clearSearch(table);
         // deselect any selected rows like Finder et al when moving into/upto a directory
         table.rows({selected: true}).deselect();
         // Callers must call table.draw() after this so filter + order changes
         // from showDir/customOrder render in a single redraw.
         table.search.fixed("oneHub", function(searchStr, rowData, rowIx) {
             // calculate the fullPath of this rows parentDir in case the dirName passed
             // to this function has the same name as a parentDir further up in the
             // listing. For example, consider a test/test/tmp.txt layout, where "test"
             // is the parentDir of tmp.txt and the test subdirectory
             let parentDirFull = rowData.fullPath.split("/").slice(0,-1).join("/");
             if (rowData.parentDir === dirName && parentDirFull === dirFullPath) {
                 return true;
             } else if (rowData.fullPath === dirFullPath) {
                 // also return the directory itself
                 return true;
             } else {
                 return false;
             }
         });
         uiState.currentHub = dirName;
         dataTableCreateBreadcrumb(table, dirName, dirFullPath);
         showHubBanner(dirName);
+        updateSelectedFileDiv(null);
     }
 
     // when we move into a new directory, we remove the row from the table
     // and add it's html into the header, keep the row object around so
     // we can add it back in later
     let oldRowData = null;
     function dataTableCustomOrder(table, dirData) {
         // figure out the order the rows of the table should be in
         // if dirData is null, sort on  uploadTime first
         // if dirData exists, that is the first row, followed by everything else
         // in uploadTime order
         if (!dirData) {
             // make sure the old row can show up again in the table
             let thead = document.querySelector(".dt-scroll-headInner > table:nth-child(1) > thead:nth-child(1)");
             if (thead.childNodes.length > 1) {