fbbc2d15114548cc241e4b99dc83c0c9e79310ac
braney
  Tue Jul 28 10:20:22 2026 -0700
Docent: take track state from trackDb, add goShow, search the Convert target visibly refs #37892

Track visibility no longer comes from a table inside the renderer. docent.js used
to carry per-composite cart parameters (mane/dbSnp155/clinvar), which pinned
clinvarMain to dense for every clinvar step and quietly aliased dbSnp155 to a
subtrack it does not name. It now reads the trackDb of the server it is driving
(hubApi /list/tracks, cached a day in $TMPDIR) and derives what a step needs: the
containers above a track, and the <name>_sel checkbox that actually decides a
composite child. Nothing is pushed downward, since a container's visibility
already reaches its selected children, so a script names only its deviations from
trackDb. Scripts use real trackDb names; names trackDb does not have (hubs, custom
tracks, a quickLift target) are sent as a literal name=mode.

Two hgTracks mechanics this had to learn: a bare clinvarCnv=hide is dropped when
the container's visibility is in the same request (the cart keeps clinvarCnv_sel=1
and the row still draws), so a step naming both a composite and a child of it is
applied in two requests, container first.

New goShow verb: types a position or a GENE NAME into the position bar and lets
the page finish -- Search on hgTracks, the arrow on hgGateway. A gene name goes
through the browser's own suggestion menu, so it lands on the gene rather than the
search-results page. pick: disambiguates the menu.

convert: now finds the target the way a user does, by typing it into the Convert
page's own genome search bar and clicking the suggestion, and accepts shot: for
the Convert page itself (opened / filled / result), which no other verb can reach.

Stills of pages that are not hgTracks (an hgc detail page, an external page a link
led to) are the viewport -- the top of the page -- instead of an element shot of
the whole scrolling document, which ran to 4400px. A fading click ripple is
removed before a still: it belongs to the video, not to a figure.

Speed: FAST=1 (make FAST=1 BP1, DOCENT_FAST=1, or fast: true) drops everything
that exists only for the video -- dwells, cursor animation, dropdown theatrics,
screen recording, mp4 transcode -- taking BP1 from 64s to 24s for the same
figures. docent.mk documents make -j for parallel scenarios; the trackDb cache is
written via rename so concurrent runs cannot read a partial file, and holds the
derived index (3MB) rather than hubApi's reply (30MB). DOCENT_TIME=1 prints where
the wall clock went, DOCENT_ROWS=1 the rows hgTracks actually drew.

Trimming the dwells exposed a real race in mouseover: dismissing the previous
tooltip with a sleep and then waiting for "a tooltip is visible" was satisfied by
the stale one, so a back-to-back pinned mouseover captured the previous item's
text. It now waits for the old tooltip to be gone and for a new one whose content
differs.

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

diff --git src/hg/utils/docent/docent.mk src/hg/utils/docent/docent.mk
index 85c7a7e0537..dfd6ef68d86 100644
--- src/hg/utils/docent/docent.mk
+++ src/hg/utils/docent/docent.mk
@@ -3,47 +3,54 @@
 # A project that keeps a set of *.docent.yaml scripts includes this file and gets
 # incremental rebuilds: each ../<base>.mp4 is regenerated when its own script — or
 # docent.js itself — is newer. docent.js writes the mp4 and the named stills in one
 # run, so the mp4 stands in for both as the make target.
 #
 # In the project's Makefile:
 #
 #     DOCENT ?= $(HOME)/kent/src/hg/utils/docent/docent.js
 #     include $(dir $(DOCENT))docent.mk
 #
 # then:
 #
 #     make            # build every mp4 whose script (or docent.js) changed
 #     make AP1        # build just ../AP1.mp4 (if stale)
 #     make -B AP2     # force a rebuild
+#     make FAST=1 BP1 # figures only, no video -- roughly a third of the wall clock
+#     make -j6        # scenarios in parallel (each run gets its own browser + cart)
 #     make list       # list the base names discovered
 #     make clean      # remove generated mp4s and stills/
 #
 # Override before the include: FIGDIR (where mp4s land, default ..), PW_ENV (the
 # Playwright runtime), SCRIPTS/BASES (to build an explicit subset).
 
 DOCENT  ?= $(HOME)/kent/src/hg/utils/docent/docent.js
 SCRIPTS ?= $(wildcard *.docent.yaml)
 BASES   ?= $(SCRIPTS:.docent.yaml=)
 FIGDIR  ?= ..
 MP4S    := $(addprefix $(FIGDIR)/,$(addsuffix .mp4,$(BASES)))
 
 # Shared Playwright/Chromium install. Anywhere you have playwright + js-yaml works;
 # at UCSC this is the ~/pwrec tree.
 PW_ENV  ?= PLAYWRIGHT_BROWSERS_PATH=$(HOME)/pwrec/browsers NODE_PATH=$(HOME)/pwrec/node_modules
 
+# FAST=1 -> figures only: no dwells, no cursor animation, no screen recording, no mp4.
+# Same stills, about a third of the wall clock. Use it while iterating on figure content;
+# drop it for the final build that has to produce the videos.
+FAST_ENV = $(if $(FAST),DOCENT_FAST=1 ,)
+
 .PHONY: all list clean $(BASES)
 
 all: $(MP4S)
 
 $(FIGDIR)/%.mp4: %.docent.yaml $(DOCENT)
-	$(PW_ENV) node $(DOCENT) $<
+	$(FAST_ENV)$(PW_ENV) node $(DOCENT) $<
 
 # Convenience: `make AP1` -> build ../AP1.mp4
 $(BASES): %: $(FIGDIR)/%.mp4
 
 list:
 	@echo $(BASES)
 
 clean:
 	rm -f $(MP4S)
 	rm -rf stills