630eb30bc3695afcf73a19be6e3bc9a2829b365f
chmalee
  Wed May 13 13:04:37 2026 -0700
Make myVariants items bed12+ rather than bed9+, refs #33808

diff --git src/hg/hgc/myVariantsClick.c src/hg/hgc/myVariantsClick.c
index 8ed8f6e5749..3df5f1c2dca 100644
--- src/hg/hgc/myVariantsClick.c
+++ src/hg/hgc/myVariantsClick.c
@@ -139,39 +139,91 @@
         printf("<B>Description:</B> ");
         printInfoIcon("Longer notes or comments about this annotation. Displayed on this details page.");
         printf("<BR>\n");
         cgiMakeTextArea(varName, item->description, 8, 80);
         printf("<BR>\n");
 
         /* Non-editable chromosome. */
         htmlPrintf("<B>Chromosome:</B> %s<BR>\n", item->chrom);
 
         /* Editable start and end. */
         int chromSize = hChromSize(database, item->chrom);
         char chromSizeString[16];
         safef(chromSizeString, sizeof(chromSizeString), "%d", chromSize);
         printf("<B>Start:</B> ");
         safef(varName, sizeof(varName), "%s_%s", trackName, "chromStart");
-        cgiMakeIntVarInRange(varName, item->chromStart+1, NULL, 80, "1", chromSizeString);
-        printInfoIcon("1-based start position on the chromosome.");
+        cgiMakeIntVarInRange(varName, item->chromStart, NULL, 80, "0", chromSizeString);
+        printInfoIcon("0-based start position on the chromosome.");
         printf("<BR>\n");
         printf("<B>End:</B> ");
         safef(varName, sizeof(varName), "%s_%s", trackName, "chromEnd");
         cgiMakeIntVarInRange(varName, item->chromEnd, NULL, 80, "1", chromSizeString);
-        printInfoIcon("1-based end position on the chromosome (inclusive).");
+        printInfoIcon("0-based half-open end position on the chromosome.");
         printf("<BR>\n");
 
+        /* Blocks (BED12). Hidden inputs are kept in sync by the widget;
+         * updateBlocksFields in hgTracks reads them on submit. */
+        char vBC[128], vBS[128], vBT[128], vCS[128], vCE[128];
+        safef(vBC, sizeof(vBC), "%s_blockCount", trackName);
+        safef(vBS, sizeof(vBS), "%s_blockSizes", trackName);
+        safef(vBT, sizeof(vBT), "%s_chromStarts", trackName);
+        safef(vCS, sizeof(vCS), "%s_chromStart", trackName);
+        safef(vCE, sizeof(vCE), "%s_chromEnd", trackName);
+        struct dyString *sizesCsv = dyStringNew(64);
+        struct dyString *startsCsv = dyStringNew(64);
+        int bi;
+        for (bi = 0; bi < item->blockCount; bi++)
+            {
+            if (bi > 0)
+                {
+                dyStringAppendC(sizesCsv, ',');
+                dyStringAppendC(startsCsv, ',');
+                }
+            dyStringPrintf(sizesCsv, "%d", item->blockSizes[bi]);
+            dyStringPrintf(startsCsv, "%d", item->chromStarts[bi]);
+            }
+        char countStr[32];
+        safef(countStr, sizeof(countStr), "%u", item->blockCount);
+        printf("<B>Blocks:</B> ");
+        printInfoIcon("BED12-style blocks. Leave empty to store a single full-span block.");
+        printf("<BR>\n");
+        cgiMakeHiddenVarWithIdExtra(vBC, vBC, countStr, NULL);
+        cgiMakeHiddenVarWithIdExtra(vBS, vBS, dyStringContents(sizesCsv), NULL);
+        cgiMakeHiddenVarWithIdExtra(vBT, vBT, dyStringContents(startsCsv), NULL);
+        printf("<div id=\"myVariantsBlocksUi\" style=\"margin:4px 0 12px 0;\"></div>\n");
+        jsIncludeFile("myVariantsBlocks.js", NULL);
+        jsInlineF(
+            "$(function(){\n"
+            "  if (typeof myVariantsBlocks === 'undefined') { return; }\n"
+            "  var sizesEl = document.getElementById('%s');\n"
+            "  var startsEl = document.getElementById('%s');\n"
+            "  var sizes = sizesEl.value ? sizesEl.value.split(',').map(Number) : [];\n"
+            "  var starts = startsEl.value ? startsEl.value.split(',').map(Number) : [];\n"
+            "  myVariantsBlocks.mount('myVariantsBlocksUi', {\n"
+            "    initialSizes: sizes,\n"
+            "    initialStarts: starts,\n"
+            "    getStart: function(){ return parseInt(document.getElementById('%s').value, 10); },\n"
+            "    getEnd:   function(){ return parseInt(document.getElementById('%s').value, 10); },\n"
+            "    hiddenCountInput:  document.getElementById('%s'),\n"
+            "    hiddenSizesInput:  sizesEl,\n"
+            "    hiddenStartsInput: startsEl\n"
+            "  });\n"
+            "});\n",
+            vBS, vBT, vCS, vCE, vBC);
+        dyStringFree(&sizesCsv);
+        dyStringFree(&startsCsv);
+
         /* Edit the color */
         safef(varName, sizeof(varName), "%s_%s", trackName, "itemRgb");
         char colorHex[8];
         safef(colorHex, sizeof(colorHex), "#%06X", item->itemRgb);
         hPrintf("<label for=\"%s\"><b>Color:</b></label> ", varName);
         hPrintf("<input type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\">\n",
             varName, varName, colorHex);
         jsInlineF(
             "$(function() {"
                 "$(document.getElementById('%s')).spectrum({"
                     "preferredFormat: 'hex',"
                     "showInput: true,"
                     "showPalette: true,"
                     "hideAfterPaletteSelect: true"
                 "});"
@@ -285,32 +337,46 @@
             cgiMakeButton(varName, "Delete");
             printf(" ");
             }
         safef(varName, sizeof(varName), "%s_%s", trackName, "cancel");
         cgiMakeButton(varName, "Cancel");
         printf("</FORM>\n");
         }
     else
         {
         /* Read-only display for shared items without write permission.
          * htmlPrintf escapes %s by default; that prevents stored XSS via
          * owner-controlled fields. */
         htmlPrintf("<B>Label:</B> %s<BR>\n", item->name);
         htmlPrintf("<B>Description:</B><BR>\n%s<BR>\n", item->description);
         htmlPrintf("<B>Chromosome:</B> %s<BR>\n", item->chrom);
-        printf("<B>Start:</B> %d<BR>\n", item->chromStart + 1);
+        printf("<B>Start:</B> %d<BR>\n", item->chromStart);
         printf("<B>End:</B> %d<BR>\n", item->chromEnd);
+        if (item->blockCount > 1)
+            {
+            printf("<B>Blocks:</B> ");
+            int roBi;
+            for (roBi = 0; roBi < item->blockCount; roBi++)
+                {
+                if (roBi > 0)
+                    printf(", ");
+                int relStart = item->chromStarts[roBi];
+                int relEnd = relStart + item->blockSizes[roBi];
+                printf("%d-%d (%dbp)", relStart, relEnd, item->blockSizes[roBi]);
+                }
+            printf("<BR>\n");
+            }
         char colorHex[8];
         safef(colorHex, sizeof(colorHex), "#%06X", item->itemRgb);
         printf("<B>Color:</B> <span style='background:%s; padding:2px 12px'>&nbsp;</span> %s<BR>\n",
             colorHex, colorHex);
         if (isNotEmpty(item->ref))
             htmlPrintf("<B>Ref:</B> %s<BR>\n", item->ref);
         if (isNotEmpty(item->alt))
             htmlPrintf("<B>Alt:</B> %s<BR>\n", item->alt);
         if (isNotEmpty(item->project))
             htmlPrintf("<B>Project:</B> %s<BR>\n", item->project);
         if (isNotEmpty(item->mouseover))
             htmlPrintf("<B>Mouseover:</B> %s<BR>\n", item->mouseover);
 
         /* Custom fields read-only */
             {