8593c40cb74ab6ee9f2318056626305d9d9544d3
braney
  Fri Apr 10 12:54:44 2026 -0700
Fix color picker from code review, refs #37329, refs #20460

- Replace Spectrum JS color picker with native HTML5 color input,
matching existing Hi-C picker
- Color saved on form submit, not immediately via AJAX
- Add checkbox to enable/disable color override; auto-checks when
user picks a color
- Gate color picker behind hg.conf showColorPicker=on (off by default)
- Only show picker for non-composite tracks that support it: bed,
bigBed, genePred, bigGenePred, wig, bigWig, rmsk, interact,
bigInteract, bigLolly, vcfTabix, vcf
- Add colorFromCart calls to interact, lolly, vcf phased, and pgSnp
drawing code
- Revert hui.js Spectrum changes from cfeb4d4 (no longer needed)
- Use htmlColorForCode()/htmlColorToRGB() from htmlColor.h instead of
hand-rolled hex parsing in colorFromCart

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 3aa37e9c255..dd23bf845e3 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -6034,45 +6034,55 @@
 {
 printf("<DIV><B>Show only transcripts with these accessions:</B> ");
 char varName[1024];
 safef(varName, sizeof(varName), "%s.nameFilter", name);
 
 char *onlyTransStr = cartUsualString(cart, varName, "");
 
 cgiMakeTextVar(varName, onlyTransStr, 60);
 printInfoIcon("Enter the primary accession of the track, so RefSeq IDs for the RefSeq track, Gencode IDs for the Gencode track, etc. Separate multiple accessions with commas.");
 puts("</DIV>\n\n");
 }
 
 void colorTrackOption(struct cart *cart, char *name, struct trackDb *tdb)
 /* color picker for overriding track color */
 {
+if (!cfgOptionBooleanDefault("showColorPicker", FALSE))
+    return;
+
 char varName[1024];
 safef(varName, sizeof(varName), "%s.colorOverride", name);
 
 char defaultColor[16];
 safef(defaultColor, sizeof(defaultColor), "#%02x%02x%02x", tdb->colorR, tdb->colorG, tdb->colorB);
 
 char *rawCartValue = cartOptionalString(cart, varName);
 boolean hasOverride = (rawCartValue != NULL && rawCartValue[0] != '\0');
 char *colorValue = hasOverride ? rawCartValue : defaultColor;
-boolean hasItemRgb = !trackDbSettingOff(tdb, "itemRgb");
 
-printf("&nbsp;<div id='colorPicker_%s'>", name);
-jsInlineF("makeHighlightPicker('%s', document.getElementById('colorPicker_%s'), '%s', '<b>Change track color: </b>&nbsp;', '%s', '%s', %s, %s);",
-        varName, name, name, colorValue, defaultColor, hasItemRgb ? "true" : "false", hasOverride ? "true" : "false");
-puts("</div>\n\n");
+char checkVar[1024];
+safef(checkVar, sizeof(checkVar), "%s.colorOverrideOn", name);
+boolean isOn = cartUsualBoolean(cart, checkVar, hasOverride);
+
+printf("<br><b>Override track color:</b> ");
+cgiMakeCheckBox(checkVar, isOn);
+printf(" <input type='color' name='%s' id='%s' value='%s' />\n",
+    varName, varName, colorValue);
+jsInlineF("document.getElementById('%s').addEventListener('input', function() {"
+    "document.querySelector('input[type=checkbox][name=\"%s\"]').checked=true;"
+    "});\n", varName, checkVar);
+puts("\n");
 }
 
 void wiggleScaleDropDownJavascript(char *name)
 /* print some js that deactivates the min/max range if autoscaling is activated */
 {
 struct dyString *dy = dyStringNew(1024);
 dyStringPrintf(dy, "  $(\"[name='%s.autoScale']\").change(function()\n", name);
 dyStringPrintf(dy, "  {\n");
 dyStringPrintf(dy, "  val= $(this).find(':selected').val(); \n");
 dyStringPrintf(dy, "  if (val!=\"use vertical viewing range setting\")\n");
 dyStringPrintf(dy, "     {\n");
 dyStringPrintf(dy, "     $(\"[name='%s.minY']\")[0].disabled=true;\n", name);
 dyStringPrintf(dy, "     $(\"[name='%s.maxY']\")[0].disabled=true;\n", name);
 dyStringPrintf(dy, "     $(\".%sAutoScaleDesc\").attr('style', 'color:grey;');\n", name);
 dyStringPrintf(dy, "     }\n");