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/hgCustom/hgCustom.c src/hg/hgCustom/hgCustom.c
index 5a3b9e78654..6503c2fc046 100644
--- src/hg/hgCustom/hgCustom.c
+++ src/hg/hgCustom/hgCustom.c
@@ -149,98 +149,89 @@
 " If you do not have web-accessible data storage available, please see the\n"
 " <A TARGET=_BLANK HREF='../goldenPath/help/hgTrackHubHelp.html#Hosting'>Hosting</A> section of the Track Hub Help documentation.\n<br><br></li>"
 " </ul>\n"
 " Please note a much more efficient way to load data is to use\n"
 " <A TARGET=_BLANK HREF='../goldenPath/help/hgTrackHubHelp.html'>Track Hubs</A>, which are loaded\n" 
 " from the <A TARGET=_BLANK HREF='hgHubConnect'>Track Hubs Portal</A> found in the menu under My Data.\n"
 );
 }
 
 void addCustomForm(struct customTrack *ct, char *err, boolean warnOnly)
 /* display UI for adding custom tracks by URL or pasting data */
 {
 char *dataUrl = NULL;
 char buf[1024];
 
-boolean gotClade = FALSE;
 boolean isUpdateForm = FALSE;
 if (ct)
     {
     isUpdateForm = TRUE;
     dataUrl = ctDataUrl(ct);
     }
-else
-    /* add form needs clade for assembly menu */
-    gotClade = hGotClade();
 
 jsIncludeFile("jquery.js", NULL);
 jsIncludeFile("hgCustom.js", NULL);
 jsIncludeFile("utils.js", NULL);
 jsIncludeFile("ajax.js", NULL);
 if (cfgOptionBooleanDefault("showTutorial", TRUE))
     {
     jsIncludeFile("shepherd.min.js", NULL);
     webIncludeResourceFile("shepherd.css");
     jsIncludeFile("jquery-ui.js", NULL);
     webIncludeResourceFile("jquery-ui.css");
     jsIncludeFile("tutorialPopup.js", NULL);
     jsIncludeFile("customTrackTutorial.js", NULL);
     if (sameOk(cgiOptionalString("startCustomTutorial"), "true"))
         {
         jsInline("var startCustomTutorialOnLoad = true;");
         }
     }
 
 /* main form */
 printf("<FORM ACTION=\"%s\" METHOD=\"%s\" "
     " ENCTYPE=\"multipart/form-data\" NAME=\"mainForm\" id='mainForm'>\n",
     hgCustomName(), cartUsualString(cart, "formMethod", "POST"));
 jsOnEventById("submit", "mainForm", "$('input[name=Submit]').attr('disabled', 'disabled');");
 cartSaveSession(cart);
 
 if (!isUpdateForm)
     {
-    /* Print clade, genome and assembly  */
-    /* NOTE: this uses an additional, hidden form (orgForm), below */
-    char *onChangeDb = "document.orgForm.db.value = document.mainForm.db.options[document.mainForm.db.selectedIndex].value; document.orgForm.submit();";
-    char *onChangeOrg = "document.orgForm.org.value = document.mainForm.org.options[document.mainForm.org.selectedIndex].value; document.orgForm.db.value = 0; document.orgForm.submit();";
-    char *onChangeClade = "document.orgForm.clade.value = document.mainForm.clade.options[document.mainForm.clade.selectedIndex].value; document.orgForm.org.value = 0; document.orgForm.db.value = 0; document.orgForm.submit();";
-
+    /* Print genome search bar and selected genome label */
     puts("<TABLE id=\"genome-selection-table\" BORDER=0>\n");
-    if (gotClade)
-        {
-        puts("<TR><TD>Clade\n");
-        printCladeListHtml(hOrganism(database), "change", onChangeClade);
-        puts("&nbsp;&nbsp;&nbsp;");
-        puts("Genome\n");
-        printGenomeListForCladeHtml(database, "change", onChangeOrg);
-        }
-    else
-        {
-        puts("<TR><TD>genome\n");
-        printGenomeListHtml(database, "change", onChangeOrg);
-        }
-    puts("&nbsp;&nbsp;&nbsp;");
-    puts("Assembly\n");
-    printAssemblyListHtml(database, "change", onChangeDb);
-    char *description = hFreezeFromDb(database);
-    if ((description != NULL) && ! stringIn(database, description))
-	{
-	puts("&nbsp;&nbsp;&nbsp;");
-	printf("[%s]", trackHubSkipHubName(database));
-	}
-    puts("</TD></TR></TABLE>\n");
+    printf("<tr>\n");
+    printf("<td class='searchCell' align=center>\n");
+    // hgCustom requires this <input> be created to go along with form submission, we
+    // will change it when a genome is selected in the search bar
+    printf("<input name='db' value='%s' type='hidden'></input>\n", database);
+    jsIncludeAutoCompleteLibs();
+    char *searchBarId = "genomeSearch";
+    printGenomeSearchBar(searchBarId, "Search any species, genome or assembly name", NULL, TRUE, "Change selected genome:", NULL);
+    jsInlineF(
+        "setupGenomeSearchBar({\n"
+        "    inputId: '%s',\n"
+        "    onSelect: function(item) {\n"
+        "        document.mainForm.db.value = item.genome;\n"
+        "    }\n"
+        "});\n"
+        , searchBarId
+    );
+    printf("</td>\n");
+    printf("<td class='searchCell' align=center>\n");
+    char *dbLabel = getCurrentGenomeLabel(database);
+    printf("Current Genome: <span id='genomeLabel'>%s</span>\n", dbLabel);
+    printf("</td>\n");
+    puts("</TR></TABLE>\n");
     }
 
 /* intro text */
 puts("<P>");
 if (isUpdateForm)
     puts("Update your custom track configuration, data, and/or documentation.");
 else
     puts("Display your own data as custom annotation tracks in the browser.");
 addIntro();
 puts("<P>");
 
 /* row for error message */
 if (isNotEmpty(err))
     {
     printf("<P><B>&nbsp;&nbsp;&nbsp;&nbsp;<span style='color:%s; font-style:italic;'>"
@@ -467,43 +458,30 @@
 if (isUpdateForm)
     {
     /* hidden variables to identify track */
     cgiMakeHiddenVar(hgCtUpdatedTable, ct->tdb->track);
     char buf[512];
     char *shortLabel = htmlEncode(ct->tdb->shortLabel);
     char *longLabel = htmlEncode(ct->tdb->longLabel);
     safef(buf, sizeof buf, "track name='%s' description='%s'",
 				shortLabel, longLabel);
     char *trackLine = htmlEncode(ctOrigTrackLine(ct));
     cgiMakeHiddenVar(hgCtUpdatedTrack, trackLine ? trackLine : buf);
     freeMem(trackLine);
     freeMem(shortLabel);
     freeMem(longLabel);
     }
-else
-    {
-    /* hidden form to handle clade/genome/assembly dropdown.
-     * This is at end of page for layout reasons (preserve vertical space) */
-    puts("</FORM>");
-    printf("<FORM STYLE=\"margin-bottom:0;\" ACTION=\"%s\" METHOD=\"GET\" NAME=\"orgForm\">", hgCustomName());
-    cartSaveSession(cart);
-    if (gotClade)
-        printf("<INPUT TYPE=\"HIDDEN\" NAME=\"clade\" VALUE=\"\">\n");
-    printf("<INPUT TYPE=\"HIDDEN\" NAME=\"org\" VALUE=\"%s\">\n", organism);
-    printf("<INPUT TYPE=\"HIDDEN\" NAME=\"db\" VALUE=\"%s\">\n", database);
-    printf("<INPUT TYPE=\"HIDDEN\" NAME=\"hgct_do_add\" VALUE=\"1\">\n");
-    }
 puts("</FORM>");
 cgiDown(0.9);
 }
 
 void tableHeaderFieldStart(int columns)
 {
 /* print table column header with white text on black background */
 printf("<TD COLSPAN=%d ALIGN='CENTER' BGCOLOR='#536ED3'>", columns);
 }
 
 void tableHeaderField(char *label, char *description)
 {
 /* print table column header with white text on black background */
 puts("<TD ALIGN='CENTER' BGCOLOR='#536ED3' ");
 if (description)