47ea57080b515e5dad5f658c58feb8944a7e7d61
chmalee
  Thu Jan 29 15:30:26 2026 -0800
Replace clade/assembly dropdowns with a search bar on most CGIs. Add a recents list to hgGateway and to the species bar and to the 'Genomes' dropdown menu. Track recently selected species in localStorage. Add toGenome and fromGenome arguemnts to hubApi/liftOver in order to find appropriate liftover assemblies, refs #36232

diff --git src/hg/lib/web.c src/hg/lib/web.c
index 0a9b9bc4562..969cd5b5c5a 100644
--- src/hg/lib/web.c
+++ src/hg/lib/web.c
@@ -904,30 +904,74 @@
 }
 
 void printBlatAssemblyListHtml(char *db)
 {
 /* Find all the assemblies that pertain to the selected genome
 Prints to stdout the HTML to render a dropdown list containing a list of the possible
 assemblies to choose from.
 
 param curDb - The assembly (the database name) to choose as selected.
 If NULL, no default selection.
  */
 struct dbDb *dbList = hGetBlatIndexedDatabases();
 printSomeAssemblyListHtml(db, dbList, NULL, NULL);
 }
 
+char *getCurrentGenomeLabel(char *db)
+/* Construct a label from dbDb (or dbDb related for an assembly hub) for the currently
+ * selected genome */
+{
+// TODO: what if 'db' is a reference to an assembly hub or genark?
+struct dbDb *info = hDbDb(db);
+if (info)
+    return cloneString(info->description);
+else
+    return cloneString(db);
+}
+
+void printGenomeSearchBar(char *id, char *placeholder, char *classStr, boolean withSearchButton, char *labelText, char *labelClassStr)
+/* Prints an input text box that can be used to search for any genome.
+ * param withSearchButton - controls if there is a button next to the bar
+ *     to manually fire the search
+ * param classStr - if desired, a custom class name or string can be used
+ *     otherwise the default styling of 'genomeSearchBarDefault' is applied via HGStyle.css
+ * param labelText - If not empty, put up a <label> for the search bar, use labelClassStr to
+ *     style it
+ * param labelClassStr - if not empty and labelText not empty, apply this class to the label
+ *
+ * There is a default class in HGStyle.css that is used
+ *
+ * The caller CGI needs to include  jquery-ui.js and utils.js to turn this into a
+ * useable search bar with autocomplete */
+{
+printf("<div class='flexContainer'>\n"); // for styling purposes
+if (isNotEmpty(labelText))
+    {
+    printf("<label for='%s' class='%s'>%s</label>", id, isNotEmpty(labelClassStr) ? labelClassStr : "genomeSearchLabelDefault", labelText);
+    }
+printf("<div class='searchBarAndButton'>\n");
+printf("<input id='%s' type='text' ", id);
+if (isNotEmpty(placeholder))
+    printf("placeholder='%s' ", placeholder);
+printf("class='%s' ", isNotEmpty(classStr) ? classStr : "genomeSearchBarDefault");
+printf("></input>\n");
+if (withSearchButton)
+    printf("<input id='%sButton' value='search' type='button'></input>", id);
+printf("</div>\n"); // the search button is grouped with the input
+printf("</div>\n");
+}
+
 static char *getDbForGenome(char *genome, struct cart *cart)
 /*
   Function to find the default database for the given Genome.
 It looks in the cart first and then, if that database's Genome matches the
 passed-in Genome, returns it. If the Genome does not match, it returns the default
 database that does match that Genome.
 
 param Genome - The Genome for which to find a database
 param cart - The cart to use to first search for a suitable database name
 return - The database matching this Genome type
 */
 {
 
 char *retDb = cartUsualString(cart, dbCgiName, NULL);
 
@@ -1696,30 +1740,33 @@
     safef(buf, sizeof(buf), "<li><a id='%s' href='%s'>%s</a></li>", contextSpecificHelpId, contextSpecificHelpLink, contextSpecificHelpLabel);
     menuStr = replaceChars(menuStr, "<!-- CONTEXT_SPECIFIC_HELP -->", buf);
     }
 
 // links to hgTracks need to use the web browser width and set the hgTracks image
 // size in pixels correctly to match the hgGateway "GO" button
 jsInline("$(\"#tools1 ul li a\").each( function (a) {\n"
 "    if (this.href && this.href.indexOf(\"hgTracks\") !== -1) {\n"
 "        var obj = this;\n"
 "        obj.onclick = function(e) {\n"
 "            var pix = calculateHgTracksWidth();\n"
 "            e.currentTarget.href += \"&pix=\" + pix;\n"
 "        }\n"
 "    }\n"
 "});\n");
+// if the user has previously searched for assemblies, add them to the "Genomes" menu heading,
+// above the "other" assemblies link
+jsInline("addRecentGenomesToMenuBar();\n");
 return menuStr;
 }
 
 void checkForGeoMirrorRedirect(struct cart *cart)
 // Implement Geo/IP based redirection.
 {
 char *thisNodeStr = geoMirrorNode();
 if (thisNodeStr)   // if geo-mirroring is enabled
     {
     char *redirectCookie = findCookieData("redirect");
     char *redirect = cgiOptionalString("redirect");
 
     // if we're not already redirected
     if (redirect == NULL && redirectCookie == NULL) 
         {