f716736ad2d4a1f8832bb945e2d21ea95ba7140b
max
  Fri Sep 18 09:03:07 2026 -0700
hgSession: fix oversized save-card inputs, move backup link into Advanced

#Preview2 week - bugs introduced now will need a build patch to fix
The name/description boxes on the save card used flex-basis for width inside
a column flex container, where flex-basis sets height instead - they were
rendering hundreds of pixels tall. Also folds "Back up custom tracks" into
the Advanced panel as a single link across the top, above the load/save
columns, instead of its own bar under the save card.

refs #38311

diff --git src/hg/js/hgSession.js src/hg/js/hgSession.js
index c5e18662372..8365635af3f 100644
--- src/hg/js/hgSession.js
+++ src/hg/js/hgSession.js
@@ -1,23 +1,23 @@
 // hgSession.js - the experimental client-rendered "My Sessions" page.
 //
 // An opt-in modern alternative to the classic server-rendered hgSession page, applying hgBlat's
 // facelift strategy (#37996): hgSession.c emits the session list and page config as an inline JSON
 // global (hgSessionData) into an empty #sessionApp container, and this file builds the UI - a
-// save-current-view card, a custom-track backup button, a searchable/sortable DataTable of saved
-// sessions with inline Overwrite/Share/Edit/Delete, and an "Advanced" panel split into a load
-// column and a save column.
+// save-current-view card, a searchable/sortable DataTable of saved sessions with inline
+// Overwrite/Share/Edit/Delete, and an "Advanced" panel split into a load column and a save
+// column, with the custom-track backup link across the top of both.
 //
 // The inline table actions POST to small JSON endpoints in hgSession.c (hgS_doDeleteJson, etc.) and
 // update the table in place.  Navigation actions (load a session, load from URL/file, save to file,
 // reset) are ordinary form submits/links against the existing hgSession actions.
 //
 // Styling: shared house-style components in gbModern.css (.gbPill, .gbCard, .gbModal*, .gbTable,
 // .gbBanner, .gbSection), session-specific layout in hgSession.css.
 
 /* global $, hgSessionData, convertTitleTagsToMouseovers, titleTagToMouseover, addMouseover */
 /* global htmlEncode, commify, gbShowTimingDialog */
 
 // Cart action variables (must match the hgs* defines in hgSession.h; hgSessionPrefix is "hgS_").
 var SESS_ACT = {
     save:      'hgS_doSaveSessionJson',
     rename:    'hgS_doRenameSessionJson',
@@ -611,58 +611,53 @@
     var saveFile =
         '<div class="sessAdvItem"><span class="lab">Save settings to a file</span>' +
         '<form class="sessAdvRow" action="hgSession" method="POST">' + sid +
         '<input class="sessAdvInput" type="text" name="hgS_saveLocalFileName" ' +
         'placeholder="File name (blank = show in browser)">' +
         '<label class="sessSaveCheck"><input type="checkbox" name="hgS_saveLocalFileCompress" ' +
         'value="gzip"> gzip</label>' +
         '<button type="submit" class="gbPill" name="hgS_doSaveLocal" value="submit" ' +
         'title="Download the current browser settings as a session file">Save</button></form></div>';
     var other =
         '<div class="sessAdvItem"><span class="lab">Other</span>' +
         '<div class="sessAdvLinks">' +
         '<a href="' + sessEnc(C.resetUrl) + '" ' +
         'title="Reset all browser settings to their defaults">Reset the browser to defaults</a>' +
         '</div></div>';
+    // A single link, not a button: custom-track backup used to be its own row down in the Save
+    // column; it now sits across the very top of the panel, above both columns.
+    var backup =
+        '<div class="sessAdvTop"><a href="hgSession?hgS_showDownload_=Submit&' +
+        sessEnc(C.cartVar) + '=' + sessEnc(C.hgsid) + '" ' +
+        'title="Download your custom tracks as a .tar.gz archive you can reload later">' +
+        'Back up custom tracks (.tar.gz)</a></div>';
 
     // Two columns - what you load from, and what you save to - rather than one undifferentiated
     // list; "Other" (reset) belongs to neither, so it spans both underneath.
     return '<div class="sessAdv">' +
         '<div class="sessAdvHead" id="sessAdvHead"><span class="caret">▸</span>' +
         '<span>Advanced — load another user’s session, load from a URL or file, ' +
         'save to a file, reset the browser</span></div>' +
         '<div class="sessAdvBody" id="sessAdvBody" style="display:none">' +
+        backup +
         '<div class="sessAdvCol"><div class="sessAdvColHead">Load</div>' +
         loadUser + loadUrl + loadFile + '</div>' +
         '<div class="sessAdvCol"><div class="sessAdvColHead">Save</div>' + saveFile + '</div>' +
         '<div class="sessAdvOther">' + other + '</div>' +
         '</div></div>';
 }
 
-function sessBackupHtml(C) {
-    // Backing up custom tracks used to be buried in the Advanced panel; it now sits just under the
-    // "Overwrite now" bar so it is visible without opening Advanced.
-    if (!C.loggedIn) { return ''; }
-    var sid = '<input type="hidden" name="' + sessEnc(C.cartVar) + '" value="' + sessEnc(C.hgsid) + '">';
-    return '<div class="sessBackup">' +
-        '<span>Custom tracks aren’t part of a saved session link.</span>' +
-        '<form action="hgSession" method="POST">' + sid +
-        '<button type="submit" class="gbPill" name="hgS_showDownload_" value="Submit" ' +
-        'title="Download your custom tracks as a .tar.gz archive you can reload later">' +
-        'Back up custom tracks (.tar.gz)</button></form></div>';
-}
-
 // ---- build the whole page -----------------------------------------------
 
 function sessAccountHtml(C) {
     // The signed-in account line (Signed in as X · Sign out · Change password) now lives in the
     // top-right menu bar, so it is intentionally not rendered here.  Kept commented out so QA can
     // add it back if wanted:
     /*
     if (C.loggedIn) {
         var s = 'Signed in as <b>' + sessEnc(C.userName) + '</b>';
         if (C.logoutUrl) { s += ' · <a href="' + sessEnc(C.logoutUrl) + '">Sign out</a>'; }
         if (C.changePasswordUrl) {
             s += ' · <a href="' + sessEnc(C.changePasswordUrl) + '">Change password</a>';
         }
         return s;
     }
@@ -826,31 +821,30 @@
         s.serverUrl = '';   // this is that server, so the Server cell here is text, not a link
     });
 
     // The session most recently saved/overwritten (max lastUse), for the one-click "Overwrite now".
     // Only this server's own sessions: "Overwrite now" saves through this server's cart.
     var recent = null;
     (sessData.sessions || []).forEach(function(s) {
         if (s.remote) { return; }
         if (!recent || s.lastUseEpoch > recent.lastUseEpoch) { recent = s; }
     });
 
     app.html(
         intro +
         '<div id="sessMsg" class="sessMsg"></div>' +
         sessRecentHtml(recent) +
-        sessBackupHtml(C) +
         sessSaveCardHtml(C) +
         sessAdvancedHtml(C) +
         sessTableHtml(C)
     );
 
     // Save current view.  Enter in either the name or the description field saves.
     $('#sessSaveBtn').on('click', sessDoSave);
     $('#sessSaveName, #sessSaveDesc').on('keydown', function(ev) {
         if (ev.key === 'Enter') { ev.preventDefault(); sessDoSave(); }
     });
 
     // One-click overwrite of the most recently saved session.
     if (recent) {
         $('#sessUpdateNow').on('click', function() {
             sessConfirm({