fc8de100a3437b9fc33bdeb0f459ca2a93e2f318
max
  Wed Sep 9 08:14:32 2026 -0700
hgSession: address the code review of the new Sessions page

Rename and unshare now keep the public listing's thumbnail with the session it
belongs to.  The picture's file name is built from the encoded session name, so
renaming a listed session left the listing pointing at nothing and the old file
behind, and dropping a session from the listing to a plain shared link kept the
picture.  The classic page had the same problem in a subtler form: it removed the
thumbnail after the row had already been renamed, so the old file survived.

Saving under a name that is already in use asks before it replaces that session,
using the failIfExists reply that the top-right Share a link menu already relies
on.  The description and "only I can load it" steps that follow a save now report
a failure instead of reloading in silence, and what thumbnailAdd has to say when
it cannot build a picture reaches the user instead of being freed unread.

A session description no longer travels through a title attribute.  The tooltip
machinery in utils.js inserts its text with innerHTML and an attribute is decoded
on the way, so a description containing angle brackets was interpreted as markup
rather than shown as typed.  It is attached, escaped, after each table draw, which
also gives the rows DataTables renders later the same styled mouseovers as the
rest of the page.

Also: the AJAX endpoints say so when there is no session by that name, instead of
reporting a no-op as a success; the new page always offers its way back to the
classic page, since the cart variable that got the user there sticks; and four
unused CSS rules, a dead element lookup and a dead local are gone.  hgConfCatalog
cited the wrong ticket for the two sessionNewPage flags.

refs #38180, refs #38157

diff --git src/hg/utils/hgConfCatalog/hgConfCatalog.py src/hg/utils/hgConfCatalog/hgConfCatalog.py
index e7e36ff3924..d92b27dfde7 100755
--- src/hg/utils/hgConfCatalog/hgConfCatalog.py
+++ src/hg/utils/hgConfCatalog/hgConfCatalog.py
@@ -489,46 +489,47 @@
                "one, which is what makes it a gate rather than a knob."),
         h("blatNewFormBanner", "flag", "hg/hgBlat/hgBlat.c",
           default="follows blatNewForm", role="gate", verified=True,
           ticket="37893",
           note="Whether the classic BLAT search form carries a banner offering "
                "the new one.  Defaults to whatever blatNewForm is set to: "
                "where the new form is enabled, a user who took its go-back "
                "link needs a way to return, and where it is disabled there is "
                "nothing to advertise.  Set explicitly to override either way.  "
                "The default is that nested read rather than a literal, so the "
                "sunset report cannot tell from the tree whether this one has "
                "shipped; it ships and goes away with blatNewForm.  Sibling of "
                "blatNewPageBanner, which does the same job for the results "
                "page."),
         h("sessionNewPage", "flag", "hg/hgSession/hgSession.c", default="FALSE",
-          role="gate", ticket="37996",
+          role="gate", ticket="38157",
           note="Replaces hgSession's classic C-generated 'My Sessions' page with "
                "the JavaScript-built one in hg/js/hgSession.js (a save bar plus a "
                "searchable, sortable session table with inline share/rename/"
                "overwrite/delete).  Off while the new page is being tested.  Read "
                "through cartUsualBoolean, so this is only the default: the page's "
                "banner offers a per-user opt-in/opt-out that sets sessionNewPage "
                "in the cart.  Sibling of blatNewForm; comes out once the new page "
                "replaces the old one."),
         h("sessionNewPageBanner", "flag", "hg/hgSession/hgSession.c",
-          default="follows sessionNewPage", role="gate", ticket="37996",
-          note="Whether the classic and new Sessions pages carry the banner that "
-               "links to the other one.  Defaults to whatever sessionNewPage is "
-               "set to: where the new page is enabled a user who took its go-back "
-               "link needs a way to return, and where it is disabled there is "
-               "nothing to advertise.  Set explicitly to override either way.  "
+          default="follows sessionNewPage", role="gate", ticket="38157",
+          note="Whether the classic Sessions page carries the banner advertising "
+               "the new one.  Defaults to whatever sessionNewPage is set to: where "
+               "the new page is disabled there is nothing to advertise.  Set "
+               "explicitly to override either way.  Does not gate the link the "
+               "other way: the new page always offers its way back to the classic "
+               "page, since the cart variable that got the user there sticks.  "
                "Sibling of blatNewFormBanner."),
         h("quickLiftClipToChains", "flag", "hg/lib/quickLift.c",
           default="TRUE", role="gate", verified=True, ticket="38042",
           note="Whether an item too big for the chains quickLift loaded is "
                "pulled in to what they cover rather than dropped.  Read once, "
                "in quickLiftIntervalsToBedClip, which is the display path; "
                "the details page calls quickLiftIntervalsToBed and always "
                "sees the item's true extent.  Born TRUE, so it never gated a "
                "release: like alwaysItemRgb, the off position is the way the "
                "browser behaved before the change rather than anything about "
                "the machine, so it is a gate and should go once nobody has "
                "asked for the old behaviour."),
     ],
 }