94f9b53d3db05371699a8fae11407acf0b30e02f
braney
  Wed Jul 8 16:22:22 2026 -0700
Fix three base-position ruler click issues: show "1 base" instead of "0 bases" at single-base zoom, make single clicks recenter symmetrically instead of drifting one base left, and stop a click on the ruler track's grey side-label strip from hijacking the config popup to do a zoom. refs #27113

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 31a8467ac00..24ea18e429b 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -481,32 +481,37 @@
                     break;
                 }
             }
         }
         return {chromStart : virtStart, chromEnd : virtEnd};
     },
 
     selectionPixelsToBases: function (img, selection)
     {   // Convert selection x1/x2 coordinates to chromStart/chromEnd.
         return genomePos.pixelsToBases(img, selection.x1, selection.x2,
                                         hgTracks.winStart, hgTracks.winEnd, true);
     },
 
     update: function (img, selection, singleClick)
     {
+        // For a drag-select keep the half-base offset (addHalfBp) so an edge that
+        // ends just before a base does not count that base.  For a single click turn
+        // it off so both edges resolve to the same base under the pointer: with it on,
+        // at single-base zoom a left click drifted one base left while a right click
+        // could not move at all.
         var pos = genomePos.pixelsToBases(img, selection.x1, selection.x2,
-                                            hgTracks.winStart, hgTracks.winEnd, true);
+                                            hgTracks.winStart, hgTracks.winEnd, !singleClick);
         // singleClick is true when the mouse hasn't moved (or has only moved a small amount).
         if (singleClick) {
             var center = (pos.chromStart + pos.chromEnd)/2;
             pos.chromStart = Math.floor(center - hgTracks.newWinWidth/2);
             pos.chromEnd = pos.chromStart + hgTracks.newWinWidth;
             // clip
             if (pos.chromStart < hgTracks.chromStart)
                 pos.chromStart = hgTracks.chromStart; // usually  1
             if (pos.chromEnd > hgTracks.chromEnd)
                 pos.chromEnd = hgTracks.chromEnd; // usually virt chrom size
 
             // save current position so that that it may be restored after highlight or cancel.
             genomePos.original = genomePos.getOriginalPos();
             genomePos.originalSize = $('#size').text().replace(/,/g, ""); // strip out any commas
 
@@ -2410,30 +2415,40 @@
                     else
                         $(this).hide();
                     $('body').css('cursor', ''); // Occasionally wait cursor got left behind
                     $("#hlColorPicker").spectrum("hide");
                 }
         });
         $(dragSelectDialog).dialog('open');
         
     },
 
     selectEnd: function (img, selection, event)
     {
         var now = new Date();
         var doIt = false;
         var rulerClicked = selection.y1 <= hgTracks.rulerClickHeight; // = drag on base position track (no shift)
+        // A single click on the grey side-label/config strip has its x clamped into the
+        // data area by imgAreaSelect, so without this test it looks like a ruler click and
+        // performs the zoom instead of opening the base-position track's config popup (#27113).
+        // Use the raw mouse x (getXLimits gives the data-area bounds) to spot a label-strip
+        // click and let it fall through to the normal config handler.
+        if (rulerClicked && selection.event) {
+            var dataXLimits = genomePos.getXLimits($(img), 0);
+            if (selection.event.pageX < dataXLimits[0] || selection.event.pageX > dataXLimits[1])
+                rulerClicked = false;
+        }
         if (dragSelect.originalCursor)
             jQuery('body').css('cursor', dragSelect.originalCursor);
         if (dragSelect.escPressed)
             return false;
         // ignore releases outside of the image rectangle (allowing a 10 pixel slop)
         if (genomePos.check(img, selection)) {
             // ignore single clicks that aren't in the top of the image
             // (this happens b/c the clickClipHeight test in dragSelect.selectStart
             // doesn't occur when the user single clicks).
             doIt = (dragSelect.startTime !== null || rulerClicked);
         }
 
         if (doIt) {
             // dragSelect.startTime is null if mouse has never been moved
             var singleClick = (  (selection.x2 === selection.x1)