6743564c0d16d85e588d5a9d80097e4690b91666
max
  Tue Sep 29 15:37:43 2026 -0700
hgTracks: tell the user when a track download is incomplete or still being prepared, and put the GenBank size limit under hg.conf

Four things in the "Download Current Track Data" dialog, all of them about a
download that quietly does the wrong thing.

The api stops at a limit on how many items it will return and says so with
maxItemsLimit in the reply, which the dialog ignored. Worse, a truncated reply
carries two extra top level fields, maxItemsLimit and dataDownloadUrl, and the
CSV/TSV converter took every top level field it did not recognise for a track:
the string one was iterated one character per row, and the conversion threw
before writing anything. A truncated CSV or TSV download therefore produced no
file and no message at all. A track's value is always the array of its rows, so
that is now the test for what is a track, rather than a list of field names that
the api will keep outgrowing. All formats now say plainly that the file is
incomplete, and the GenBank file carries the same warning in its COMMENT block,
where it outlives the dialog.

Nothing showed that anything was happening between the click and the browser's
download, which is one second for two tracks and four for twenty, on a 20 kb
region. The Download button now goes disabled with a line beside it while the
file is prepared. It is in the button pane rather than the dialog body because
the body scrolls once the track list is long.

The GenBank region limit drops from 100 Mbp to 25 Mbp. 50 Mbp of chr1 with 24
tracks answers with 340 MB of track json and 50 MB of sequence, which the web
browser parses, copies into the file text and copies again into the Blob, so the
tab needs several times the region in memory. The limit is now the hg.conf
setting maxGenbankRegion, registered in hgConfCatalog.py as a knob: the ceiling
belongs to the machine and its users. It is read only when showGenbankDownload
is on, and a value that is not a positive number falls back to the default
rather than aborting the CGI.

refs #38433

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 8bf9456ffae..38c1190060c 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -9133,31 +9133,42 @@
         // Hand the JS dialog a login URL that returns to this hgTracks page.
         char *retUrl = wikiLinkEncodeReturnUrl(cartSessionId(cart), "hgTracks", "");
         char *loginUrl = wikiLinkUserLoginUrlReturning(cartSessionId(cart), retUrl);
         jsInlineF("var myVariantsLoginUrl = \"%s\";\n", loginUrl);
         freez(&retUrl);
         freez(&loginUrl);
         }
     }
 
 // put the track download interface behind hg.conf control
 if (cfgOptionBooleanDefault("showDownloadUi", TRUE))
     jsInline("var showDownloadButton = true;\n");
 
 // remove the hg.conf option once this feature is released
 if (cfgOptionBooleanDefault("showGenbankDownload", FALSE))
+    {
     jsInline("var showGenbankDownload = true;\n");
+    // How much sequence the javascript will turn into a GenBank file, in bases.  The
+    // file holds the whole region and is built in the web browser's memory, so the
+    // ceiling belongs to the machine and its users rather than to the code.  Printed
+    // as a number, and a value that is not a positive one falls back to the default,
+    // so a typo in hg.conf cannot write broken javascript or refuse every region.
+    long long maxGbRegion = atoll(cfgOptionDefault("maxGenbankRegion", "25000000"));
+    if (maxGbRegion <= 0)
+        maxGbRegion = 25000000;
+    jsInlineF("var maxGenbankRegion = %lld;\n", maxGbRegion);
+    }
 
 // remove the hg.conf option once this feature is released
 if (cfgOptionBooleanDefault("showIgv", FALSE))
     {
     puts(" <button id='hgtIgv' type='button' "
             "title='Add an IGV.js window below the UCSC Browser, to open files from "
             "your local harddisk or server' >Add IGV Tracks</button>");
     //jsInline("document.getElementById('hgtIgv').addEventListener('click', onIgvClick);");
     }
 
 }
 
 static void printAliases(char *nativeName, char *displayName)
 /* Print out the alternative names for this sequence next to the position. */
 {