9b762c146f7a44a3bdc783cdd202356fc163b8fb
braney
Wed Sep 16 12:43:32 2026 -0700
docent: one targetConf.js for the target, the hg.conf, the central and the account, refs #37892
docent.js and tests/preflight.js had grown a second and then a third copy of the
same lookups. That is exactly the pair that must not drift: preflight checks the
fixtures for the server the RUN will drive, so a run that resolved its target, its
hg.conf, its hgcentral or its account even slightly differently would be checked
against the wrong machine, and the mismatch would show up as a green preflight in
front of a red suite.
targetConf.js answers the four questions in order, each from the one before:
resolveTarget a `target:` (or DOCENT_TARGET) -> the .../cgi-bin URL to drive
hgConfFor that URL -> the hg.conf it reads, if the server is on this box
centralDbFor that conf -> which hgcentral it uses
loginLookup that central -> the account to sign in with
Nothing in it opens a browser or the network, which is why preflight can ask all
four in the seconds before a run.
loginLookup returns the facts plus, when the account cannot be used, ONE sentence
saying why. That sentence is the substantive half and is now written once: the
step throws it with a `login:` prefix, preflight prints it on the MISSING line.
Each caller still phrases its own success line, since one logs and the other
prints a fixture row. No password crosses that boundary to anything that prints.
248 lines net out of the two programs, 197 into the module.
docent.js is no longer a single file, and three places now say so: its own require,
the Run section of README.md, and docent.mk, whose mp4 rule gains targetConf.js as
a prerequisite -- a change there changes what a tour renders, so it has to rebuild
one. Nothing in the tree or in ~/docentTours copies docent.js; they all reference
it where it sits, with targetConf.js beside it.
Measured before and after, with no other change: preflight resolves the same
account, central and conf for genome-test, hgwdev-braney, a ts park on 48087 and
hgwbeta (which correctly has no section); tests/ is 18 of 18 with the derive
baselines matching; tests/regress preflights 14 fixtures for 67 scripts.
diff --git src/hg/utils/docent/README.md src/hg/utils/docent/README.md
index c12dc9bf3ba..2186b57f549 100644
--- src/hg/utils/docent/README.md
+++ src/hg/utils/docent/README.md
@@ -1,464 +1,471 @@
# Docent — a language for authoring guided tours of the Genome Browser
A **docent** leads a tour and explains what you are looking at. A Docent script is
that tour, written down: an ordered list of high-level verbs (`go`, `hide`,
`track`, `mouseover`, `convert`, `drag`, `shot`) that drive a real browser against a
real server.
`docent.js` renders one script into three things at once:
- a **silent mp4** of the whole tour,
- a named **PNG still** at every `shot:` marker, and
- a loadable **session file** at every `session:` marker.
So a published figure is literally a frame of the tour, and the two can never drift
apart. A session file goes further: it hands back the state itself, so a reader can open
the view the figure was taken from instead of only looking at it. The surface syntax is YAML — so ordinary editors highlight it and no one has to
learn a new parser — but the language is the verb vocabulary layered on top, not the
serialization. Scripts are named `.docent.yaml` (a bare `.docent` works too).
The verbs deliberately encode *browser mechanics* rather than selectors: `convert:`
knows that the Hide-defaults checkbox reverts when the Assembly menu reloads,
`track: {clinvar: pack}` asks trackDb which containers and checkboxes that implies, and
`mouseover:` knows that a lifted track's DOM id gains a per-run `hub__` prefix. The author writes
intent; the renderer deals with the UI.
## Run
```
PW=/hive/groups/browser/uiTest/pw
PLAYWRIGHT_BROWSERS_PATH=$PW/browsers NODE_PATH=$PW/node_modules \
node docent.js AP1.docent.yaml
```
+`docent.js` is not a single file: it requires `targetConf.js` from beside it, which
+answers where a run is pointed (`target:` and `DOCENT_TARGET`), which `hg.conf` that
+server reads, which hgcentral that names, and which account a `login:` step signs in
+with. `tests/preflight.js` requires the same module, which is the point of it being one:
+the fixture check has to resolve all four exactly the way the run will. Copy the pair, or
+run `docent.js` where it sits.
+
Needs `playwright`, `js-yaml`, and `ffmpeg`. At UCSC these live in one pinned shared
install at `/hive/groups/browser/uiTest/pw` (`browsers` for Chromium, `node_modules`
for the modules), which every browser-driving test in the tree uses; its `README.md`
records the pinned versions. Point the two variables above anywhere you have them.
Outputs, relative to the script's own directory:
- mp4 → `../.mp4` (override with `mp4:` or a second argument)
- stills → `stills//.png` (override with `stills:`)
- sessions → `sessions//.txt` (override the parent with `sessions:`)
`docent.mk` in this directory has the make rules — include it from a project that keeps
a set of scripts and it rebuilds only the ones whose source changed. See the usage
comment at the top of that file.
## Top of file (all optional)
```yaml
target: genome-test # or rr, hgwdev, hgwbeta, hgwdev-, or a full https://.../cgi-bin
db: hg38 # source assembly
position: chr7:155.8M # starting position (tracked from then on; `go:` sets it too)
reset: true # cartReset first (clean cart + fresh quickLift hub)
size: [1000, 760] # viewport
pix: 850 # browser image width
pace: 1.2 # seconds to dwell after each step
shotHold: 2.2 # extra seconds the video pauses at a shot
trackAnim: false # skip the visible dropdown gesture on `track:` (nav only)
fast: false # true = figures only: no dwells, no cursor animation, no mp4
scale: 1 # >1 = print-resolution stills (see Print resolution)
pinMouseovers: false # record every mouseover for `pinShot:` (see below)
mp4: ../myname.mp4 # override output paths if you want
stills: stills/AP1
sessions: sessions # PARENT of the session dirs; files land in //
sessionUrlBase: https://hgwdev-you.gi.ucsc.edu/~you/docent/sessions # see Sessions
```
`target:` defaults to genome-test, so a script that forgets to say where it runs will
not quietly hit someone's personal sandbox.
## Print resolution
A screen still is about 120 dpi across a journal's 7-inch column — fine on a monitor,
too coarse to print. `scale:` (or `DOCENT_SCALE=k`, or `make hires`) renders **the same
tour with k times the pixels**, which is not the same thing as enlarging the stills
afterwards: a still only ever has the pixels it was drawn with, so every layer is asked
to draw more of them.
```
make hires # every scenario at 3x -> stills.hires//
make hires SCALE=2 # 2x
make hires BASES=BP1 # one scenario
DOCENT_SCALE=3 DOCENT_STILLS=stills.hires DOCENT_FAST=1 node docent.js BP1.docent.yaml
```
What k does:
- **deviceScaleFactor: k**, with the viewport left at its 1x CSS size. The page lays out
exactly as at 1x — same line breaks, same jQuery-dialog width, same tooltip placement —
and all of it rasterizes with k times the pixels.
- **`pix` × k, `textSize` stepped up with it** (hgTracks' size ladder: 6, 8, 10, 12, 14,
18, 24, 34; 3x lands exactly on 24). The server draws a genuinely wider image, and the
bigger font keeps its layout decisions proportional — tick spacing, room for labels,
how features pack into rows. A wider image with an 8px font would be a different
picture, not a bigger one.
- **`zoom: 1/k` on the image table**, handing that wider image the 1x amount of layout
space, so one image pixel lands on one device pixel. Native resolution, no resampling.
- **The tooltip font is pinned back to its 1x size.** hgTracks takes the tooltip's
font-size from the browser text size (`window.browserTextSize` → `hg/js/utils.js`
`addMouseover`), which `textSize × k` has just tripled — and then the device pixel ratio
scales the same text a second time. Left alone, a 3x still gets tooltips 3x too big: the
popups swamp the figure and the last one pinned falls off the crop.
- **Every drawn row asks for k times its height** (`