6af92621b7dba437be69c05f3ff49079ba637383
braney
Fri Sep 4 11:43:39 2026 -0700
docent: let convert: take a bare string, like every other verb
convert: hs1 left the argument as a String, so o.to was undefined and o.search
picked up String.prototype.search. The run then reported
convert: "function search() { [native code] }" matched nothing
which says nothing about what is wrong with the script. A bare string is now
the target assembly, matching hub:, addCustomTrack:, loadSession: and the rest.
quicklift: is still never implied, so convert: hs1 is a plain coordinate
convert; the README row says so now.
Found while writing the first of the regression tests for #38252, fourteen of
which open with convert:.
refs #37892
diff --git src/hg/utils/docent/README.md src/hg/utils/docent/README.md
index 87195454082..828dfe5d6fb 100644
--- src/hg/utils/docent/README.md
+++ src/hg/utils/docent/README.md
@@ -1,435 +1,435 @@
# 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
```
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** (`