MultiRegionDisplayMixin
Auto-generated from the @jbrowse/mobx-state-tree model in the source — see the developer guide for concepts. Built into JBrowse core. View source.
Per-region fetch lifecycle for LGV-based GPU displays. Installs the fetch
autoruns in afterAttach and exposes overridable hooks (fetchNeeded,
rpcProps, zoomFetchArgs, regionHasData, gateEnabled) plus
the fetchRegions / loadedRegions machinery.
rpcProps() and zoomFetchArgs() are what the display's RPC is sent, and
the foundation stamps both beside every region a fetch loads. They differ in
what a move shows: an rpcProps move supersedes the in-flight fetch and
scrims the held data, while a zoomFetchArgs move lets the held regions draw
until the refetch lands, so a value that swings with zoom goes in
zoomFetchArgs. A zoom rule that is no worker argument, such as a payload
answering only the zoom it was fetched at, is a regionHasData answer. The
mixin looks both up rather than declaring them, so a display keeps its narrow
return types through MST's .views() chain.
Each section ends with the members a composed model contributes, linked to the page that documents them.
Volatiles
| Member | Description |
|---|---|
loadedRegionsObservableMap<number, LoadedRegion> | The per-region store, keyed by displayedRegionIndex: what a fetch asked for (the span), what it was issued under (fetchInputs) and what it brought back (payload), written as one record by ctx.commitRegion.A display's own rpcDataMap was the payload column of this map held separately, and every hook that existed to keep the two in step — clearDisplaySpecificData, a regionHasData that answered rpcDataMap.has(idx), a hand-rolled prune — was that separation's cost. A display reads the payload back through regionPayloads. |
19 inherited volatiles from 3 models
From RegionTooLargeMixin: forceLoadTrack, byteEstimate, gateMeasuredViewportKey
From RenderLifecycleMixin: canvasDrawn, paintCount, currentRenderingBackend, renderTick, autorunsInstalled, renderError, offScreen
From FetchMixin: activeSignal, fetchGeneration, reloadCounter, statusWindow, error, statusMessage, statusProgress, fetchCanceled, fetchRotation
Getters
| Member | Description |
|---|---|
hostRegionHost | The containing LinearGenomeView, typed once for every display in this family — see containingHost for the cast it owns and why both foundations still declare the name. |
canvasWidthPxnumber | The CSS width of this display's on-screen canvas, in px — and the canvasWidth its renderState must carry, since the two have to agree or the bp→px mapping is scaled against a box it doesn't fill.trackWidthPx, not view.width: TrackRenderingContainer insets the rendering component by the 2px track outline under contain: strict, so a view.width-wide canvas overhangs its own container and the browser clips the overhang away. It renders almost identically, which is why MAF drifted onto view.width uncaught.A getter rather than a note on each display, because the choice was being made by copying a neighbour out of four plausible view getters — width (the viewport), this one, and totalWidthPx / totalWidthPxWithoutBorders (the content width, which the global family's heatmaps legitimately want: a different question, not a different answer). no-restricted-syntax bans the underlying read everywhere but this line, since a second spelling agrees until it doesn't.SVG export is the one exception: the export shell has no outline, so renderSvg overrides canvasWidth with the shell's own width (see LgvSvgBodyProps). |
settledSubPixelBinBpnumber | Genomic bp one cell of a per-base pass stands for at the settled zoom: subPixelBinBp off the host's debounced coarseBpPerPx, and 1 until the view initializes. A per-base encode or fetch reads this rather than the live zoom so a wheel tick does not redo every region. |
canRenderboolean | Overrides RenderLifecycleMixin's default-true hook with the LGV precondition both foundations share — see foundationCanRender. |
rendersCanvasboolean | Fills RenderLifecycleMixin's hook off fetchInert, as GlobalFetchMixin does: a display that will never fetch here shows a placeholder where its canvas would be. |
trackVisibleRegionsVisibleRegion[] | The visible blocks on the track's own assemblies, the ones this display fetches and is judged against. A view of several genomes leaves a single-genome track's other regions blank. |
viewportWithinLoadedDataboolean | true when every visible block lies within an already-fetched region — i.e. the viewport shows data we actually loaded, not the stale fringe left after a zoom-out/pan. Drives the loading overlay through the pre-refetch debounce. Spatial only, and it stays that way. Whether the data held for a block is still what a fetch would bring back is isCacheValid, which dataCurrent conjoins for the export gate. The scrim reads this getter and dataSuperseded, never isCacheValid: a phase that went loading on a moved fetchInputs would raise the overlay into every zoom. |
viewportEmptyboolean | No content block is on screen, so this display has nothing to fetch and nothing to paint — see viewportEmpty.ts for the one viewport that reaches it, how narrow that is, and why the state still has to be terminal rather than a permanent scrim. Both foundations declare it over that one expression, the same way they each declare host and paintInert. |
fetchSuspendedboolean | Overridable hook (default false), read by the fetch plan: the display is drawing something in the features' place and wants no fetch while it does. CoarseTierMixin says it while its tier stands in, so MAF never downloads the alignments its summary replaces.Not fetchInert: that one suppresses the scrim and ends the export wait, and a display saying this still has its stand-in to load. On this foundation alone, because only this family's plan reads it. |
layoutReadyboolean | Overridable hook (default false): whether a searchable feature layout currently exists. Any display defining a feature-lookup method (searchFeatureByID) must override it, so callers can tell "laid out, but off-display" from "no layout exists yet" — a distinction only the display can make. See agent-docs/reference/DISPLAY_KIT.md §"Four readiness axes". |
dataSupersededboolean | Overridable hook (default false): the held data is loaded and covers the viewport, but a fetch input has moved past it, so the data is about to be refetched. A display says so here rather than overriding dataCurrent, for the reason FetchMixin.fetchInert is a hook: an override has to restate the freshness terms and then misses the next one added.Both answers a display gives about being finished read it — the export gate through dataCurrent, the loading scrim through displayPhase. The export gate is the sharper case: awaitSvgReady samples freshness once, and an export that samples it inside this window renders the data that is about to be replaced. GWAS's LD auto-index is that case: adopting the top hit as the index SNP is an rpcProps change, so the very load that produced the top hit is what it invalidates — and SettingsInvalidate lands a tick after the write, so until it does not even staleSettingsDrawn has seen it.The window is NOT invisible on screen, which this used to say while displayPhase took a spatial-only argument: alignments' per-base wall spends the debounce plus the RPC painting a 1 px stripe every 8 px, with nothing in the key having moved yet.The input need not have settled yet. Alignments counts the debounce window ahead of its per-base bin, where the bin the data was fetched under has not moved and the clear is inevitable rather than committed. That is the half of the window an export lands in, since a reader zooms and then reaches for the menu. What may NOT go in is a change that could still be taken back: this fails hung, not stale. So state the live-vs-settled half as a value compare and leave the stamp alone. The settled half — the stamp a fetch committed under against the zoomFetchArgs a fetch now would send — is the foundation's already, through the isCacheValid term in dataCurrent, and an override restating it buys nothing: a second derivation misses the field the args gain next, latches this true, and every export of the display then waits out awaitSvgReady's backstop instead of failing. |
renderBlocksRenderBlock[] | Shared cached view for every LGV-based GPU display. A single displayedRegion may produce multiple render blocks (shared GPU buffer, different scissor clips on screen). Plugins that want to suppress rendering in certain states (e.g. no domain yet) can override this getter to return [] — the autorun lifecycle will then issue an empty-blocks render that clears the canvas. |
fetchInputsFetchInputs | What a fetch issued right now would stamp on a region: the settings tier (FetchMixin.settingsFetchInputs, the tier staleSettingsDrawn compares alone) and the zoom tier (the display's zoomFetchArgs() object). fetchRegions captures it before the RPC goes out and stamps it beside the loaded region; isCacheValid compares against it. |
regionPayloadsReadonlyMap<number, unknown> | The store's payloads, keyed by displayedRegionIndex. A display narrows this once — get rpcDataMap() { return self.regionPayloads as ReadonlyMap<number, MyResult> } — and every reader it already had goes on reading a map.MST makes a .views() getter a computed, so the Map is built once per store change and handed back by reference after that — which is what keeps installUpload's diff a reference compare per key and a frame that changed no data free of it. While something observes it: MobX suspends an unobserved computed and rebuilds on every read, so a display whose only reader is a pointer handler needs the keep-alive canvas and Manhattan both install (agent-docs/reference/DISPLAY_KIT.md §"A hit test's index needs an observer"). The render lifecycle's upload autorun is that reader for every display that draws. |
hasRegionDataboolean | A fetch has landed: at least one region's payload is in the store. The render callback's first-paint gate reads this, so an empty frame before any data cannot flip canvasDrawn; an empty but loaded region counts, and paints its empty frame. |
staleSettingsDrawnboolean | A visible block's held data was fetched under a settings or adapter key that has since moved: drawn, and wrong for the current settings, until the refetch isCacheValid already owes lands. The loading scrim's third staleness term, beside spatial coverage and dataSuperseded — the one that used to come from SettingsInvalidate emptying the coverage map, and the reason it no longer has to.False on a zoom by construction: it compares the stamp's settings tier alone, so a moved zoom tier raises no scrim. That is the whole distance from the declined fold, which compared the whole key and put the overlay 250 ms into every zoom. |
dataCurrentboolean | This family's answer to the shared freshness question every display foundation must answer (dataCurrent): the held data corresponds to what is on screen right now. Four terms — spatial coverage of every visible block, loadedRegions.size to rule out the vacuously-true empty viewport, isCacheValid per block, and the display's own dataSuperseded. Regions stream in one at a time, so a multi-region/whole-genome export waits on this getter to be complete; waiting on the first datum to arrive would export a partial set.isCacheValid belongs here and not in the scrim. Coverage answers "is the data here", never "is it what a fetch now would bring back", so a zoom that moves fetchInputs leaves every held region covered and stale at once — and an export sampling svgReady across that window painted bins the worker computed for the previous zoom. displayPhase takes dataSuperseded but NOT this term: folding a moved fetchInputs into the phase raises the loading scrim into every zoom, which is the trade this does not take.The term cannot latch, and the reason is structural rather than a case list: a block reaches fetchNeeded unless planRegionFetch finds it ungated, covered AND cache-valid, and it reads that last term tracked. The && short-circuits ahead of it drop its observables only where the block is fetched anyway, so the key move that closes this gate is the same read, in the same dependency set, that wakes the refetch reopening it. |
| loadedAssembly | The assembly the data in hand came from, once it can answer about refNames — undefined before that.Read off the first LOADED region rather than the view's displayed ones, and defined here for that reason: a display holding fetched data is asking about the assembly THAT data is on, and the view's regions can already have moved on. The initialized gate is why this returns the assembly rather than its name. getCanonicalRefName2 and refNameToIndex answer WRONGLY rather than throwing before the aliases land — identity, and a miss — so a caller that skips the gate gets a plausible answer and no signal. Returning undefined until the aliases load forces the caller to write its fallback. |
phaseViewportCurrentboolean | The loading scrim's staleness argument: what is drawn answers for what is on screen. Spatial coverage, dataSuperseded and staleSettingsDrawn; NOT isCacheValid, which is dataCurrent's and would scrim every zoom. displayPhase reads it, and so does a stand-in phase (coarseTierDisplayPhase) — one predicate, so a term added here reaches both. |
svgReadyboolean | true once an off-screen (SVG) export can safely read this display's data. Policy single-sourced in computeSvgReady; this family supplies only the freshness half, which foundationSvgReady reads as dataCurrent or the vacuous currency of viewportEmpty. Off-screen renderers gate on it via awaitSvgReady(model) instead of inlining the condition. |
paintInertboolean | Fills RenderLifecycleMixin's paintInert hook — see there for why a failed fetch has to read as finished to the consumers outside the display, and foundationPaintInert for the second such state and why both fetch families answer it through one function. Overridable, as the hook is: a display with a third inert state of its own says so here. |
paintSupersededboolean | Fills RenderLifecycleMixin's hook with staleSettingsDrawn, so painted — and data-display-drawn through it — reads pending over a canvas painted under the previous settings until the refetch lands. clearAllRpcData used to reset canvasDrawn for the same effect; the hook says it without blanking anything. |
displayPhaseDisplayPhase | The display's mutually-exclusive visual state, mapped in foundationDisplayPhase — every foundation calls it and supplies only its staleness argument, so a term added to computeActivityPhase reaches all three without being wired three times.This family's argument is phaseViewportCurrent: spatial coverage AND dataSuperseded (data a settled fetch-input change is drawing wrong right now — alignments zooming perBaseLetter from 16 bp/px to 1 stays inside the loaded region and reported ready over a wall drawn as a 1 px stripe every 8 px) AND staleSettingsDrawn.A thunk, so a suppressed or already-loading display doesn't subscribe to viewport churn. A subclass customizes this through fetchInert (FetchMixin), never by overriding the getter — see that hook. |
28 inherited getters from 3 models
From RegionTooLargeMixin: gateEnabled, byteGateAdapterConfig, configuredFetchSizeLimit, densityTooLarge, byteGateAdapterPath, adapterFetchSizeLimit, configForceLoad, gateViewportSpanBp, gateViewport, aboveForceLoadFloor, gateExempt, estimatedFetchBytes, gateMeasurementStale, gateByteLimit, gateActive, densityGateActive, tooLargeStatus, regionTooLarge, regionTooLargeReason, zoomCanReleaseGate, gateSkipsMeasuredViewport
From RenderLifecycleMixin: painted
From FetchMixin: isLoading, isLoadingOrCanceled, fetchInert, awaitingPrerequisite, awaitingDependentData, settingsFetchInputs
Methods
| Member | Description |
|---|---|
regionHasData(displayedRegionIndex: number) => boolean | Overridable hook: whether the display can draw what this region is marked loaded over, at the zoom the view has. The default answers off the store — an entry a fetch committed carries what it stored — and off the payload's zoomRange where the adapter declared one (BaseFeatureDataAdapter.getZoomRange): a BigWig tier answers a band of zooms, and the region reads as stale when the view leaves it. A payload carrying no range answers at every zoom.What survives an override is the question the store cannot answer: which of several held payloads answers. MAF caches a summary tier and a detail tier side by side under one displayedRegionIndex, so crossing the threshold inside an already-loaded region changes which map has to answer. The multi-row display's override survives for the auto-partition reconciliation (regionHasPinnedData).A view, not an action, so the reads it makes register as dependencies of FetchVisibleRegions. |
isCacheValid(displayedRegionIndex: number) => boolean | Whether the data held for a region still answers the current view. Not a hook a display fills: a display states its rule as zoomFetchArgs (what a fetch now would send the worker) and regionHasData (does what the last one stored still answer), and this compares the whole input set against the one the region was fetched under. |
2 inherited methods from one model
Actions
| Member | Description |
|---|---|
| setLoadedRegion | The raw write behind ctx.commitRegion, and not what a fetch should call. A display naming its span itself is the bug this family spent a release fixing, and the context gives a fetch no way to express it — see RegionFetchContext. Direct callers are tests staging an already-loaded display.The payload is named on every call, undefined included: this write replaces the whole record, so a re-stamp that left it off dropped the data a test had staged one line earlier, and the failure surfaced as a reader throwing three layers away. A test stages a claim with nothing behind it by saying so.An action so callers after an async boundary stay in MST strict mode. Stamps the region with the fetch key its data came back under. fetchRegions passes the key it captured before issuing the RPC; the default reads it now, which is right for a caller holding the region already and wrong for anything resuming after an await, where the viewport may have moved under the fetch. |
evictRegionStore(keep: ReadonlySet<number>) => void | The store's bound. Drops the entries a fetch can no longer be about: an index outside the view's buffered viewport, once the store is over MAX_STORED_REGIONS, oldest first.One rule for every display, where canvas hand-rolled pruneRpcDataMapToVisible (prune to the buffer on every fetch) and every other display had no bound at all beyond displayedRegions.length. That count is the contig count, so a fragmented assembly was effectively unbounded. The cap lets a pan back onto a recently-visited region draw immediately, which the prune-to-buffer rule did not. |
dropLoadedRegion(displayedRegionIndex: number) => void | Forget one region — for a display pruning what has scrolled off screen. |
clearDisplaySpecificData() => void | Overridable hook (no-op base): drop what this display holds beside the store — a second store of its own, a verdict about the viewport that was just dropped. Called by clearAllRpcData, which runs on a displayed-regions change, on a viewport move past an error or a cancel, and on reload(). |
clearSettingsBakedData() => void | Overridable hook (no-op base): drop what this display holds that is wrong under a changed setting rather than merely stale — a payload whose shape the setting decides. Called by invalidateSettings, where clearDisplaySpecificData is deliberately not: bins, reads and features fetched under the previous setting draw honestly under the scrim staleSettingsDrawn raises until the refetch lands, the way canvas has kept its features since ADR-006 and every display does since 2026-09.The variant matrix overrides it, for a structural reason: it holds one payload for every visible region and the row set is a setting, so there is no per-region replacement to wait for. |
clearAllRpcData() => void | full reset: cancels fetch, clears error, loadedRegions, display-specific data, and the canvas-drawn flag. The too-large gate is derived (a pure function of the cached estimate × viewport), so it needs no explicit clear here — the fetch autorun re-measures at the new viewport and the verdict follows. |
invalidateSettings() => void | SettingsInvalidate's reset: the half of clearAllRpcData a settings change still needs now that the settings and adapter axes are in fetchInputs. The in-flight fetch is superseded now rather than when its payload lands stamped stale, a blocking error or cancel is cleared so the plan is not blocked, and the display drops its settings-baked data. loadedRegions and the canvas-drawn flag stay: every held region already reads !isCacheValid, so the plan refetches it, and until that lands the data stays on screen under the scrim staleSettingsDrawn raises. |
reload() => void | FetchMixin.reload (error, cancel, counter) plus the full reset. Subclasses with extra teardown override and chain.An override must reach this counter, by chaining to super or by bumping it. Missing it doesn't break the retry, which the clearAllRpcData call drives; it turns the retry contract check off for that display, and the display still looks healthy. The one override in the tree chain is canvas's LinearBasicDisplay, which LinearVariantDisplay inherits. reloadReachesCounter.test.ts reads every reload() in the tree to catch the next override that misses the counter. |
fetchNeeded(_needed: IndexedRegion[]) => void | Overridable hook (no-op base): override to call one of the three helpers in fetchEachRegion.ts — fetchEachRegion (one RPC per region), fetchAllRegions (one RPC per genome, one result per region) or fetchRegionsBatched (one RPC, one payload covering all of them). |
| fetchRegions | Run a per-region fetch. The work callback calls ctx.commitRegion as it stores each region's payload, which is what marks it loaded — see RegionFetchContext for why this function no longer does that itself. Its only callers are the three helpers in fetchEachRegion.ts, which make that call for every display in the family; a display reaching past them owns both ctx.isStale() guards and the commit by hand, and none does.The fetch key is captured here, at issue, and carried into every commit — never re-read after the await. ctx.isStale() trips on a newer fetch or a cancel, not on a viewport that moved under a fetch that is still current, so a key read at commit time would stamp this data with a zoom it was not fetched at. |
afterAttach() => void | installs the fetch-lifecycle autoruns (DisplayedRegionsChange, FetchVisibleRegions, SettingsInvalidate, ClearBlockingStateOnViewportChange) |
22 inherited actions from 3 models
From RegionTooLargeMixin: clearByteEstimate, clearGateMeasurements, setForceLoadTrack, commitFetchBytes, forceLoad
From RenderLifecycleMixin: markCanvasDrawn, resetCanvasDrawn, stopRenderingBackend, renderNow, setOffScreen, setRenderError, attachRenderingBackend
From FetchMixin: setError, setStatusMessage, stopActiveFetch, openStatusStream, cancelFetch, cancelFetchByUser, beforeDestroy, beginFetch, endFetch, runFetch
Related links
- Developer guide: Pluggable elements