LinearHicDisplay
Auto-generated @jbrowse/mobx-state-tree API for the current JBrowse release —
see pluggable elements for concepts. Provided by the
hic plugin.
View source.
Example usage
A complete HicTrack config to paste into tracks. resolutionBias nudges the
auto-picked binsize (negative = finer, positive = coarser):
{
type: 'HicTrack',
trackId: 'hic',
name: 'Hi-C',
assemblyNames: ['hg38'],
adapter: { type: 'HicAdapter', uri: 'https://example.com/contacts.hic' },
displays: [
{
type: 'LinearHicDisplay',
displayId: 'hic-LinearHicDisplay',
useLogScale: true,
resolutionBias: 1,
},
],
}
Hi-C display that renders contact matrix using WebGL
The configuration slots for this model are documented on its config schema page.
Members a composed model contributes are listed here too, so these tables are the whole surface.
Properties
| Member | Description | Defined by |
|---|---|---|
typetype: types.literal('LinearHicDisplay') | LinearHicDisplay | |
configurationconfiguration: ConfigurationReference(configSchema) | LinearHicDisplay | |
idid: ElementId | BaseDisplay |
Volatiles
| Member | Description | Defined by |
|---|---|---|
rpcDatarpcData: null as HicDataResult | null | LinearHicDisplay | |
availableNormalizationsavailableNormalizations: undefined as string[] | undefined | LinearHicDisplay | |
availableResolutionsavailableResolutions: undefined as number[] | undefined | LinearHicDisplay | |
errorerror: undefined as unknown | BaseDisplay | |
statusMessagestatusMessage: undefined as string | undefined | BaseDisplay | |
statusProgressstatusProgress: undefined as number | undefined | determinate progress fraction [0,1] for the current status, or undefined when the in-flight phase is indeterminate. Set alongside statusMessage by setStatusMessage; a display that never shows a bar simply leaves it undefined. | BaseDisplay |
scrollTopscrollTop: 0 | TrackHeightMixin | |
loadedFetchSignatureloadedFetchSignature: undefined as string | undefined | fetchSignature as it stood when the held data was committed — the loaded half of this family's freshness compare. Written only by commitFetchResult and cleared only by reload, so a display cannot stamp data it did not fetch or forget to invalidate on retry; the data itself stays display-owned (arc keeps stale arcs on screen under the loading overlay, HiC keeps the stale matrix). | GlobalFetchMixin |
forceLoadTrackforceLoadTrack: false | The force-load button's track-wide approval. Volatile so it never reaches a saved session; the forceLoad config slot is the durable form. | RegionTooLargeMixin |
byteEstimatebyteEstimate: undefined as ByteEstimate | undefined | The last byte measurement: bytes, the span they were taken at, and whether zooming has been shown not to shrink them. Survives clearAllRpcData; dropped on chromosome navigation and on a tier swap. | RegionTooLargeMixin |
gateMeasuredViewportKeygateMeasuredViewportKey: undefined as string | undefined | The viewport key the gate last asked the adapter about, on either axis. Separate from byteEstimate because a density refusal measures no bytes. | RegionTooLargeMixin |
canvasDrawncanvasDrawn: false | flips true on first paint; read by test selectors to detect render | RenderLifecycleMixin |
currentRenderingBackendcurrentRenderingBackend: undefined | current backend reference, updated on context-loss recovery. Typed unknown (not generic B) on purpose: this mixin is composed by every display via a non-generic factory, so the per-display backend type B isn't known here — it's supplied at attachRenderingBackend<B> and narrowed with as B inside the autoruns. Don't "fix" the cast. | RenderLifecycleMixin |
renderTickrenderTick: 0 | counter the render autorun observes; bumped to force a re-render | RenderLifecycleMixin |
autorunsInstalledautorunsInstalled: false | guards attachRenderingBackend so the autorun pair spawns once per instance | RenderLifecycleMixin |
renderErrorrenderError: undefined | the render-backend (GPU/Canvas2D init or context-loss) error, or undefined. Single source of truth for the render-error terminal state: useRenderingBackend writes it from the canvas-init mechanism so the model — not React-local hook state — owns every terminal state. Read by displayPhase (whose renderError term outranks loading, suppressing the scrim) and by DisplayChrome (shows the retry overlay). | RenderLifecycleMixin |
activeStopTokenactiveStopToken: undefined as StopToken | undefined | stop token of the in-flight fetch, or undefined when idle | FetchMixin |
fetchGenerationfetchGeneration: 0 | bumps at every fetch end; autoruns read it to re-evaluate, and it doubles as the staleness epoch inside runFetch | FetchMixin |
reloadCounterreloadCounter: 0 | Bumped by reload() and read unconditionally by the fetch autoruns, so a user retry re-runs the body even where nothing else moved — after an error every other fetch input is unchanged. It is also the half that survives a reload() override that forgets to invalidate, which is the dead Retry button makeRetryContractCheck reports. Declared here because this is the one mixin both LGV fetch foundations compose, the same argument that put fetchInert below; the comparative family carries its own on SyntenyFetchStateMixin (ADR-054). | FetchMixin |
statusWindowstatusWindow: createStatusWindow(writeStatus(self)) | This display's status field, and the only thing that writes it: one throttle window, one slot per concurrent operation, so N parallel per-region fetches thin to one stream between them rather than N and a second operation cannot end the first one's label (ADR-081). Lent whole to createStopTokenRotation by a display that also runs a bare-autorun fetch — see StatusReporter. | FetchMixin |
fetchCanceledfetchCanceled: false | true after the user explicitly cancels a load (the loading overlay's cancel button → cancelFetchByUser). A durable, blocking state — unlike cancelFetch, it does not retrigger the fetch autoruns — so the load stays stopped until the user retries (reload) or the viewport changes. Any new fetch clears it (runFetch resets it at the start). | FetchMixin |
| fetchRotation | The latest-wins machine this mixin is a wrapper around, and not a second one: createStopTokenRotation owns token rotation, the isCurrent guard, the status slot and the supersede-versus-end rule (ADR-080, ADR-081), for every fetch in the codebase that has one. runFetch adds the observable bookkeeping a display needs on top — isLoading, error, fetchGeneration, fetchCanceled — and nothing else.It was two implementations of that machine until 2026-08-20, which is how they came to disagree about whether a completed fetch releases its token. A display's primary fetch is this wrapper; a second concurrent fetch on the same node holds a rotation of its own, which is why the primitive is the thing that exists and this is the thing built on it (ADR-054 §1). It is lent this display's statusWindow, so the fetch takes a slot on the one field rather than opening a second window over it — the whole point of StatusReporter. | FetchMixin |
Getters
| Member | Description | Defined by |
|---|---|---|
| view | LinearHicDisplay | |
resolutionBiasnumber | LinearHicDisplay | |
hasResolutionsboolean | Whether the .hic file's binsize list has arrived (it comes from the async CoreGetInfo call in afterAttach). Every resolution control — the track-menu stepper, the on-figure dropdown and its enabling checkbox — is gated on this rather than re-deriving availableResolutions?.length. | LinearHicDisplay |
awaitingPrerequisiteboolean | Retry here is two-stage: reload() wakes the info autorun and the contacts one, the contacts one runs first and declines because the header it needs has not landed, and the header arriving wakes it again through the same tracked read. So the retry contract is judged on that later run. Not fetchInert, which would be the wrong claim — HiC does want the scrim meanwhile.This is the header not having landed, which is also exactly when prepare declines: HiC's gate and its prerequisite are one condition, so every decline defers and the check can never report on this display. Deliberate, and the cost is that infoFetchFailure.test.ts is what pins HiC's retry. See FetchMixin.awaitingPrerequisite. | LinearHicDisplay |
useLogScaleboolean | LinearHicDisplay | |
useColorPercentileboolean | LinearHicDisplay | |
showResolutionControlsboolean | LinearHicDisplay | |
selectedNormalizationstring | The user's persisted normalization choice. May name a scheme the current .hic file doesn't actually offer — activeNormalization resolves that. | LinearHicDisplay |
activeNormalizationstring | The normalization to request, resolved against what the file offers (availableNormalizations). Falls back to the next-best available scheme when the selection is absent (the parser silently uses NONE otherwise). A pure getter, so opening a file that lacks the selected scheme never writes a config delta / marks the track edited — only an explicit user pick (setActiveNormalization) does.What the file could deliver is a second question this can't answer: normalization vectors are stored per (type, chr, unit, binsize) and availableNormalizations is the file-wide union, so a scheme listed here can still be missing at the current binsize. appliedNormalization below carries what actually came back. | LinearHicDisplay |
squashToHeightboolean | LinearHicDisplay | |
appliedNormalizationstring | The normalization the loaded matrix actually carries, which differs from activeNormalization whenever the file has no vectors for the requested scheme at the current binsize (KR at 5 kb but nothing at 2.5 Mb is typical). The track menu ticks this, so the radios describe the data on screen rather than the request that produced it. Falls back to the request before any data has landed.Read only by the UI. It is fetch-derived, so it must stay out of rpcProps() — see the "rpcProps() loop trap". | LinearHicDisplay |
colorScheme"fall" | "juicebox" | "viridis" | LinearHicDisplay | |
colorMaxScorenumber | Where the color ramp saturates. 0 is the "no data to scale against" sentinel; hasLegendData is the one place it's interpreted.The linear branch saturates at a twentieth of the max rather than the max itself. Contact counts are heavily skewed — a handful of very hot bins near the diagonal against a long tail near zero (see countStats.ts) — so scaling to the true max leaves everything off-diagonal at the bottom of the ramp. Log scale needs no such correction, and useColorPercentile is the principled version of the same fix. | LinearHicDisplay |
hasLegendDataboolean | Whether there's a color scale worth drawing a legend for: data loaded with a positive saturation point. The single place the colorMaxScore "0 means nothing to show" sentinel is interpreted — legend consumers read this, not the raw score. | LinearHicDisplay |
showLegendAreaboolean | Whether a legend is drawn: the setting is on AND there is a scale worth drawing one for. Read by both the on-screen overlay panel and the SVG export, so an export can't disagree with the figure it is exporting. svgLegendWidth() deliberately does not gate on this — see its note. | LinearHicDisplay |
autoResolutionIdxnumber | Index into availableResolutions that pure auto-mode would pick at the current zoom — largest binsize ≤ 2*bpPerPx, falling back to the finest binsize (idx 0) when nothing qualifies (very zoomed in).The factor 2 floors at ~0.5 bins/screen-pixel, which keeps bins visible without going sub-pixel; users who want finer can step the resolution bias down. | LinearHicDisplay |
yScalarnumber | Vertical squash of the triangle. Bidirectional fill like the LD display: dragging taller than the natural triangle height stretches to fill rather than leaving a blank band below. WithoutBorders, because the base is the axis the content occupies — the span the worker can put contacts on. totalWidthPx also counts the boundary padding blocks dynamicBlocks adds when scrolled left of genome start / past the end, which carry no data, so including them would overstate the base and leave fit-to-height short of the display. | LinearHicDisplay |
canvasWidthnumber | The box the matrix is drawn in: the canvas element's CSS width and the backing store the rendering backends resize to (renderState) have to be one number, or the drawn matrix is stretched against the box it sits in. Same name and same reason as the LD display's.totalWidthPx here and totalWidthPxWithoutBorders for the triangle's base above, which is the whole difference between the two: the canvas covers the scrolled content including the boundary padding blocks, and the apex height is set by the span the worker can put contacts on. They agree except when scrolled past an end. | LinearHicDisplay |
effectiveResolutionIdxnumber | Index actually used after applying resolutionBias, clamped to the valid range so a stale bias from a different zoom level can't index out of bounds. | LinearHicDisplay |
effectiveResolutionnumber | undefined | The actual binsize to fetch at, after auto-pick + bias. | LinearHicDisplay |
canStepResolutionFinerboolean | Whether a finer binsize exists to step to. The stepper controls read this rather than compare indices themselves, so the edges of the file's binsize list are described in one place. | LinearHicDisplay |
canStepResolutionCoarserboolean | Whether a coarser binsize exists to step to. | LinearHicDisplay |
viewSignaturestring | undefined | HiC's half of GlobalFetchMixin's freshness compare: the static-block set plus the binsize the current zoom calls for, so a pan inside the loaded blocks is a pure redraw and only a real change — a block entering, a zoom (static blocks re-snap, and the binsize may step) — refetches. Undefined until the view is measured and the .hic header has landed, which is the prerequisite gate. The normalization axis rides in through the rpcPropsCacheKey half the mixin appends.activeNormalization reading the fetched header list is safe for the reason ARCHITECTURE.md's loop-trap section gives: the contact fetch this signature keys never writes availableNormalizations, so a mismatch converges in one fetch. | LinearHicDisplay |
viewTransform{ viewScale: number; viewOffsetX: number; } | The per-frame map from the payload's pre-rotation data space (origin-relative axis bp / √2) to canvas px, read by the render state, the hit test and the SVG export so the three cannot disagree. Worker output is genomic, so this is pure live-view arithmetic — pan and zoom move it every frame with no refetch — and the one payload-derived term, originBp, folds the axis origin back in here, in double precision, which is what keeps float32 instance positions small (see calcAxisBlocks). Stale data during a refetch simply draws at its own genomic position under the live map. | LinearHicDisplay |
parentTrackAbstractTrackModel | BaseDisplay | |
RenderingComponentFC<…> | BaseDisplay | |
| DisplayBlurb | BaseDisplay | |
adapterConfigRecord<string, unknown> | BaseDisplay | |
isMinimizedboolean | Returns true if the parent track is minimized. Used to skip expensive operations like autoruns when track is not visible. | BaseDisplay |
hoveredFeatureunknown | Overridable hook (default undefined): what the pointer is currently over, for readers outside the display. LinearGenomeViewContainer publishes it to session.hovered, the view-wide "what is the user pointing at" channel a plugin can subscribe to.Declared here because a cross-display consumer can only read a name the base declares — the same reason SyntenyFetchStateMixin.fetchInert is a hook rather than a getter each display invents. The container used to read featureUnderMouse, which only the wiggle, alignments and Manhattan families spelled that way — canvas said hoveredFeature, variants hoveredGenotype — so the channel carried a hover from a third of the display types and nothing said which. It also asked only displays[0] of each track.unknown because the payload genuinely differs — a read, a wiggle bin, a SNP, a genotype cell — and session.hovered is typed to match ("can be anything; code that wants to deal with this should examine it"). Narrow it in the override. | BaseDisplay |
featureNounstring | Overridable hook (default 'feature'): the SINGULAR word for one of the things this display draws, as a menu row or a chip says it — "Hide this read", "Showing 3 variants".Declared here for the same reason as hoveredFeature above: it is read across the display boundary, by chrome that has no idea which display it is drawing for (SoloSelectionChip, alignments' group-label overlay), and a name only the base declares is a name every such consumer can rely on. Two displays declared it independently and one of those declarations WAS this default.A control keeps the generic word; content takes this one. "Variant height" reads as a different setting from "Feature height" when it is the same one, so the shared menus stay on "feature" however the display answers here, and the noun varies where it names what the user is looking at — "Showing 3 variants", "Hide this read". A display drawing something the generic word already fits is right to leave this alone. Distinct from the per-hit noun a context menu takes off the clicked item's own type ("mRNA", "gene"); that names one annotation, this names what the track holds. The hit noun falls back to this. | BaseDisplay |
featureWidgetType{ type: string; id: string; } | Overridable hook: which widget openFeatureWidget opens for one of this display's features. The default is the generic one, which is what a display drawing plain features wants and what the canvas base spelled out by hand.An override is a display whose features have a vocabulary of their own — a read, a variant, a synteny block — and the id is deliberately part of it: two displays naming one id share the drawer panel, which is the behaviour when the two are showing the same kind of thing. | BaseDisplay |
heightnumber | TrackHeightMixin | |
resizingboolean | True for the duration of a height drag on this track, whichever handle is running it. A display whose row geometry is a function of the track height restretches every row per animation frame, and can use this to sit an expensive per-frame layer out of the drag (MAF's dense per-base letter overlay is a Canvas2D pass that scales with rows x columns). The flag itself is the track's ( BaseTrackModel), so the view brackets a drag without needing the active display to have opted into this mixin. Reading it here is what makes self.resizing available to a display that did. | TrackHeightMixin |
scrollableHeightnumber | Overridable hook: how far this display's content can scroll past its viewport, in px. Infinity (the default) means "this display doesn't scroll internally" — setScrollTop then never clamps and the re-clamp autorun below is inert, so a non-scrolling display pays nothing and, crucially, never evaluates a getter that would read view geometry.A display that scrolls a canvas overrides this with max(0, contentHeight - viewportHeight), and gets the clamped setter plus the shrink autorun for free. It is the single "does it scroll, and by how much" answer: the wheel handler (useVirtualScrollWheel) and VerticalScrollbar read the same getter. | TrackHeightMixin |
hostRegionHost | The hosting view as the RegionHost contract — see containingHost for the cast it owns, why the name is host and not view, and why both foundations still declare the name over one body. | GlobalFetchMixin |
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. | GlobalFetchMixin |
canRenderboolean | Overrides RenderLifecycleMixin's default-true hook with the LGV precondition both foundations share — see foundationCanRender. | GlobalFetchMixin |
fetchSignaturestring | undefined | Signature of the fetch the current view and settings call for — the display's viewSignature plus the serialized rpcProps() axis. What runGlobalFetch gates on, captures at issue, and stamps at commit. | GlobalFetchMixin |
dataCurrentboolean | The shared freshness answer, now derived rather than a hook: data has been committed (loadedFetchSignature is only ever written beside it) and it was fetched for the current view and settings. A pan inside the loaded blocks stays current; a block entering, a tier step, a settings change or a reload() moves one side of the compare and refetches. | GlobalFetchMixin |
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. | GlobalFetchMixin |
svgReadyboolean | Policy single-sourced in computeSvgReady; this family supplies only the freshness half, which foundationSvgReady reads as dataCurrent or the vacuous currency of viewportEmpty. Note it requires the dataset to actually be current, NOT merely "not currently fetching": the fetch trigger is a debounced afterAttach autorun, so at export time isLoading can still be false with no data yet — a displayPhase !== 'loading' test would then capture an empty render. Never gates on canvasDrawn, which an off-screen export never sets. Off-screen renderers gate on it via awaitSvgReady(model). | GlobalFetchMixin |
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 computeLoadingTerm reaches all of them without being wired twice.This family's argument is the constant true, deliberately: a global display keeps the last frame up through a refetch (worker output is genomic, so the stale frame draws correctly under the live view transform), so a pan or zoom shows no scrim beyond the isLoading window. The pre-first-paint scrim it does want — the gap between mount and isLoading going true, which on HiC is the CoreGetInfo round trip its first fetch waits on — is computeLoadingTerm's shared rendersCanvas && !canvasDrawn term, not anything this family spells out.A display with no rendering backend narrows this to the backend-free DisplayStatusPhase with foundationDisplayStatusPhase, which is what arc does: it cannot reach renderError, and the narrower type is what lets DisplayStatusChrome take it with neither a cast nor a dead branch. | GlobalFetchMixin |
gateEnabledboolean | The opt-in. Overridden with a literal true by gated displays, and check-gated-adapter-budgets insists on a literal: this mixin returns early on it in an autorun and in commitFetchBytes. | RegionTooLargeMixin |
densityGateEnabledboolean | Whether the density axis applies. CanvasFeatureGateMixin contributes true beside its measurement; byte-only displays leave it. | RegionTooLargeMixin |
byteGateAdapterConfigRecord<string, unknown> | The adapter config the gate measures — the one at byteGateAdapterPath. Overridable for a display whose adapter config is synthesized rather than read off the track. | RegionTooLargeMixin |
configuredFetchSizeLimitnumber | The display's fetchSizeLimit slot, from regionTooLargeConfigSchemaFields. | RegionTooLargeMixin |
densityTooLargeboolean | The density axis's verdict; canvas overrides it. | RegionTooLargeMixin |
byteGateAdapterPathstring[] | Where on the track config the measured adapter sits. A tiered display overrides this one hook (MAF: ['adapter', 'summaryAdapter'] while showSummary), and both the measurement and the budget follow it. | RegionTooLargeMixin |
adapterFetchSizeLimitnumber | undefined | The measured adapter's own fetchSizeLimit slot, read off the live track config rather than the adapterConfig snapshot, which omits slots at their default. | RegionTooLargeMixin |
configForceLoadboolean | The declarative forceLoad slot. | RegionTooLargeMixin |
gateViewportGateViewport | undefined | What a measurement taken now would be about: the span on screen and a key for the stretch of genome it covers. Undefined until the view is measured, and the mixin's only read of the view. Captured before the fetch's round trip, never at commit. | RegionTooLargeMixin |
byteGateAdapterKeystring | Which tier the estimate is about, as a comparable string. | RegionTooLargeMixin |
aboveForceLoadFloorboolean | Whether the span on screen is at or above AUTO_FORCE_LOAD_BP, the one comparison against that constant. False on an unmeasured view. | RegionTooLargeMixin |
gateExemptboolean | Nothing may gate on either axis: the forceLoad slot or the button. | RegionTooLargeMixin |
estimatedFetchBytesnumber | undefined | The stored estimate's bytes; undefined when nothing has been measured. | RegionTooLargeMixin |
gateMeasurementStaleboolean | Whether the last measurement is about a viewport the user has since left. True before any measurement. | RegionTooLargeMixin |
gateByteLimitnumber | The byte budget: the adapter's limit, else the display's, doubled below AUTO_FORCE_LOAD_BP. Read only through resolvedByteLimit(). | RegionTooLargeMixin |
gateActiveboolean | Whether the gate may act right now, on any axis: opted in, not exempt, view measured. The view is read last, so an ungated display never touches it. | RegionTooLargeMixin |
densityGateActiveboolean | gateActive plus the density axis's own terms: the axis is on, and the span is above the floor. | RegionTooLargeMixin |
tooLargeStatusRegionTooLargeStatus | The verdict and its banner text, from the stored estimate against resolvedByteLimit() and the density axis when it may act. | RegionTooLargeMixin |
regionTooLargeboolean | RegionTooLargeMixin | |
regionTooLargeReasonstring | Banner text for the axis that tripped; empty when not too large. | RegionTooLargeMixin |
zoomCanReleaseGateboolean | Whether "zoom in to see features" is honest advice. Density always releases on zoom; bytes only if the last zoom-in moved the estimate. | RegionTooLargeMixin |
gateSkipsMeasuredViewportboolean | The skip both fetch skeletons apply: the banner is up and its measurement already describes the viewport on screen. | RegionTooLargeMixin |
rendersCanvasboolean | Overridable hook (default true): whether this display paints a canvas in its current configuration, as opposed to a deliberate static placeholder (LD with the triangle off, sequence past base resolution — both render a message where the <canvas> would go, so canvasRef is never called and canvasDrawn can never flip).Lives here, beside canvasDrawn, because every consumer of "has this display painted" needs the pair — and until 2026-08 each family declared its own copy (per-region hard-coded true, global carried the hook for LD), so a display could express the state only to whichever family it happened to compose. See painted below for the reader that was missed. | RenderLifecycleMixin |
paintedboolean | The first-paint answer every consumer outside the display should read, canvasDrawn being only the raw flag: a display that is deliberately not painting a canvas has finished, and saying otherwise is a lie that never resolves.The two rendersCanvas: false states each had three of their four consumers wired by hand — the loading scrim (rendersCanvas / fetchInert) and the SVG export (fetchInert) — while the fourth, data-display-drawn, went on publishing "false" forever off the raw flag. That attribute is what PENDING_DISPLAYS (@jbrowse/browser-test-utils) selects on, so a zoomed-out reference sequence track made every waitForDisplaysDone on the page burn its full timeout — silently, since that wait swallows its own. Same shape as fetchInert on the comparative side: the reader you forget is the one outside the display, so the display has to publish one name for it.paintInert is the third term and the same argument once more, for the state where a display would paint a canvas and never gets to — a fetch that failed before first paint. See that hook. | RenderLifecycleMixin |
isLoadingboolean | true while a fetch is active | FetchMixin |
isLoadingOrCanceledboolean | isLoading widened to cover a user-canceled load. This, not isLoading, is what a displayPhase loading term wants. cancelFetchByUser clears the stop token synchronously, so isLoading goes false the instant the user clicks Cancel — and the loading overlay that unmounts on it is carrying the Retry button, which is the only way back: the state is deliberately durable, so no autorun restarts the fetch on its own. A bare isLoading therefore reads as ready over a display that is stopped, empty and offering nothing.Arc read isLoading directly and had exactly that hole. It is a getter here so no family has to remember the second term. | FetchMixin |
fetchInertboolean | Overridable hook (default false): the states where this display deliberately never fetches, so it holds no data and none is coming. Sequence sets it past base resolution ("Zoom in to see sequence"); LD sets it with the triangle toggled off. One hook, three readers, and that is the whole point — a display that grows such a state has one thing to say rather than three, and the reader it would have forgotten is always the one outside itself: - the loading scrim ( computeLoadingTerm), which otherwise parks over the placeholder, permanently once a cancel has been clicked; - the SVG export (computeSvgReady's extraTerminal), whose awaitSvgReady is an unbounded when, so one such display hangs the whole view's export; - the dev-only retry check (makeRetryContractCheck), which would otherwise report a dead Retry on a display correctly declining to load anything.It was three hooks — loadingSuppressed, svgReadyExtraTerminal on each of the two foundations, and fetchInert on the comparative family, which had already collapsed them. Both LGV displays that override it returned one expression for all three, and one of the three was hard-coded false on the global family for a while, which is how LD came to be able to express only half its own state. Same name and same meaning as SyntenyFetchStateMixin.fetchInert now, so the retry check reads one field across all three fetch families. ADR-082.A hook rather than a displayPhase override, because overriding the getter means restating the whole loading condition — which is how sequence came to hold a verbatim copy of the other terms, one git blame away from silently missing the next one added.It lives here because this is the one mixin all three display foundations compose. Same argument, one level down, that put rendersCanvas on RenderLifecycleMixin beside canvasDrawn. | FetchMixin |
rpcPropsCacheKeystring | The RPC cache key both fetch foundations invalidate on: this display's rpcProps() payload serialized to a string. serializeRpcProps owns the why, including the silently-dead-axis corollary.Here, beside the two hooks above, for the same reason they are: it describes the display, and every foundation composes this mixin. The per-region family watches it from SettingsInvalidate and the global one from its fetch autorun's trigger list — one getter and one name, so the two cannot come to invalidate on different axes. The global side built its own local computed over the same function until 2026-08, which was the same value under a second spelling. | FetchMixin |
showLegendboolean | Whether the legend is drawn. Resolved through the promotable-slot tiers (resolveConf): an explicit track value customizes it either way, otherwise it follows the session-wide default for this display type, falling back to the slot's promotedBase. | LegendMixin |
showLegendDisplayTypeDefaultPin | The "make the current legend visibility the default for all tracks" control. Symmetric, so it promotes whichever value the track currently shows. showLegendCheckboxItem takes this as its pin. | LegendMixin |
Methods
| Member | Description | Defined by |
|---|---|---|
rpcProps() => { normalization: string; } | LinearHicDisplay | |
hitTest(mouseX: number, mouseY: number) => HicContactItem | undefined | Inverse of the view transform: takes mouse coords (canvas-relative) and returns the contact bin under the cursor, or undefined. The forward transform is viewTransform; this is its inverse so hit-testing always matches what was drawn. | LinearHicDisplay |
renderStateHicRenderState | Computed per-frame render state for the GPU backend. Read by the autorun lifecycle on every change to any tracked observable. Always resolved (a bare getter must never hand back undefined) — it's pure view/settings geometry, and "no data yet" is the render callback's gate, not a nullable state. The one data-derived field, binWidth, rides with the payload instead (see HicUploadData). | LinearHicDisplay |
svgLegendWidth() => number | Width of the SVG legend (consumed by SVGLinearGenomeView), via the shared helper — see gradientSvgLegendWidth for why it reserves on the setting alone rather than gating on hasLegendData. | LinearHicDisplay |
() => MenuItem[] | LinearHicDisplay | |
renderSvg(opts: ExportSvgDisplayOptions) => Promise<ReactNode> | LinearHicDisplay | |
| renderingProps | props passed to the renderer's React "Rendering" component. these are client-side only and never sent to the worker. includes displayModel and callbacks | BaseDisplay |
resolvedByteLimit() => number | undefined | The budget the worker enforces and the banner compares against — the one spelling of that pair. Undefined when the gate may not act. | RegionTooLargeMixin |
gateFetchState() => GateFetchState | The gate as it stands for a fetch about to be issued. Calling it is the capture, which is why it is a method. | RegionTooLargeMixin |
Actions
| Member | Description | Defined by |
|---|---|---|
setRpcData(data: HicDataResult) => void | runGlobalFetch stamps the signature this was fetched for (GlobalFetchMixin.commitFetchResult) in the same transaction. | LinearHicDisplay |
startRenderingBackend(backend: HicRenderingBackend) => void | Called by the React hook (useRenderingBackend) when the HAL resolves. Wires the backend into the mixin-owned autorun pair via attachRenderingBackend. | LinearHicDisplay |
setUseLogScale(f: boolean) => void | LinearHicDisplay | |
setUseColorPercentile(f: boolean) => void | LinearHicDisplay | |
setShowResolutionControls(f: boolean) => void | LinearHicDisplay | |
setColorScheme(f: "fall" | "juicebox" | "viridis") => void | LinearHicDisplay | |
setActiveNormalization(f: string) => void | Persist the user's explicit normalization pick. Resolution against what the file offers happens in the activeNormalization getter, so this only fires on a real user choice. | LinearHicDisplay |
setAvailableNormalizations(f: string[]) => void | Record what the .hic file offers. Resolution lives in the activeNormalization getter (which falls back off this list when the user's selectedNormalization isn't available), so this doesn't write the selection — opening a file that lacks the selected scheme never marks the track edited. | LinearHicDisplay |
setSquashToHeight(arg: boolean) => void | LinearHicDisplay | |
setAvailableResolutions(f: number[]) => void | LinearHicDisplay | |
resetResolutionBias() => void | Reset to pure auto-mode: bias 0, binsize follows zoom directly. | LinearHicDisplay |
setResolutionIdx(idx: number) => void | Lock the display to availableResolutions[idx], stored the way the config slot wants it: an offset from whatever pure auto-mode would pick at the current zoom, so a locked choice keeps shifting consistently as the user zooms rather than pinning an absolute binsize.Both resolution controls write through here. That conversion — "a bias is an index offset from the auto pick" — is one arithmetic fact, and it was stated once per control, each with its own guard against a bad index: one checked membership, the other clamped. Clamping here covers both, so a caller may hand over an out-of-range index without indexing the file's binsize list out of bounds. No-op before the binsize list arrives from CoreGetInfo. | LinearHicDisplay |
setResolution(binSize: number) => void | Lock to a specific binsize (from the overlay dropdown). No-op if the binsize isn't one the file offers. | LinearHicDisplay |
stepResolution(delta: number) => void | Step one entry finer (negative delta) or coarser (positive) from the binsize currently in effect. A step at either edge lands on the edge rather than indexing out of bounds — the menu's stepper disables there, and this keeps that from being the only thing standing between a bad index and a fetch. | LinearHicDisplay |
setStatusMessage(status?: RpcStatus | undefined) => void | BaseDisplay | |
setError(error?: unknown) => void | BaseDisplay | |
clearHoveredFeature() => void | Overridable hook (default no-op): drop whatever hoveredFeature reports. The writing twin of that getter, and what installClearHoverOnViewportChange calls.A display that STORES its hover owes an override; one that derives it from the live pointer (MAF, Hi-C, LD) owes nothing, and the default costs it nothing. Declared here so the clear can be installed for every display rather than remembered per display — forgetting it is the failure ARCHITECTURE.md's stored-hover section is about, and it used to be six closures at six call sites, which is six chances to omit one. | BaseDisplay |
reload() => void | base display reload does nothing, see specialized displays for details | BaseDisplay |
setScrollTop(scrollTop: number) => void | Clamped into [0, scrollableHeight], so no caller has to remember the bound. Unbounded for a display that leaves scrollableHeight at its Infinity default. | TrackHeightMixin |
setHeight(displayHeight: number) => number | TrackHeightMixin | |
resizeHeight(distance: number) => number | TrackHeightMixin | |
expandToContentHeight() => number | Grow the track by exactly the content it is currently hiding, so a display scrolled over a taller stack ends up showing all of it. The track's resize handle runs this on a double click.scrollableHeight is the whole measurement — it is already every scrolling display's answer to "how much is off the bottom", so no display has to supply a second one. A display that doesn't scroll internally leaves it at Infinity and gets a no-op, as does one already showing everything (0).Routed through resizeHeight rather than setHeight so grow mode's override still gets to leave grow first; going straight to the slot would let the reactive height re-derive grownHeight and the double click would appear to do nothing. | TrackHeightMixin |
commitFetchResult(commit: () => void, signature: string) => void | The commit half of runGlobalFetch: run the display's own store in the same transaction as the signature stamp, so no observer can see fresh data under a stale signature or the reverse. Being the only writer of loadedFetchSignature is what makes dataCurrent derivable — a display cannot commit without stamping. | GlobalFetchMixin |
| setByteEstimate | The bytes half of a measurement alone, for a test staging a display. Production commits through commitFetchBytes. | RegionTooLargeMixin |
clearByteEstimate() => void | Drops the estimate and the viewport stamp. forceLoadTrack survives: it is a track-wide approval. | RegionTooLargeMixin |
setForceLoadTrack(flag: boolean) => void | RegionTooLargeMixin | |
| commitFetchBytes | The byte axis of a finished fetch, called by the fetch runners with the gateFetchState() they captured at issue. Commits the per-region max; an empty batch, or an ungated display, commits nothing. | RegionTooLargeMixin |
forceLoad() => void | The banner's button: exempt the track on both axes and refetch. | RegionTooLargeMixin |
markCanvasDrawn() => void | RenderLifecycleMixin | |
resetCanvasDrawn() => void | RenderLifecycleMixin | |
stopRenderingBackend() => void | RenderLifecycleMixin | |
renderNow() => void | RenderLifecycleMixin | |
setRenderError(error: unknown) => void | set/clear the render-backend error. Called by useRenderingBackend: with the error when the canvas factory rejects (or context-loss re-init fails), and with undefined on successful (re)init and on retry. | RenderLifecycleMixin |
| attachRenderingBackend | attach a GPU/Canvas2D backend and install the upload + render autorun pair. Idempotent: re-calling swaps the backend and does not run setup again, so the callbacks and everything they close over are the first call's. | RenderLifecycleMixin |
stopActiveFetch() => void | Abort the in-flight fetch (if any) and retire its slot. The shared preamble of both cancel paths; the difference between them is only what they do to fetchCanceled / fetchGeneration afterward. | FetchMixin |
openStatusStream(isCurrent: () => boolean) => StatusStream | Open one operation's slot on the display's status field: an RPC statusCallback throttled through the display-wide window and guarded so a callback that fires after the node is torn down (RPCs resolve their status stream asynchronously) is a safe no-op, plus the clear that retires the slot when the operation ends.Every operation on the display opens one, and the two come back together because an operation that never retires goes on voting for a phase that is over. The viewport fetch ( runFetch), the clustering run and a lent createStopTokenRotation are three of them on one field; before ADR-081 each blanked the field outright and the last one to finish decided what the other two were still saying.isCurrent is required and has no "node is alive" default, because alive is not the interesting question: a superseded fetch is on a live node, and its late status repainting the overlay of the fetch that replaced it is the failure this guards. runFetch passes !isStale(), which is what every display gets for free through ctx.statusCallback; a caller outside a fetch (the clustering autorun) passes its own run's flag. Defaulting to isAlive made the loose answer the easy one and five displays took it.Declared this early only so runFetch can put one on every FetchContext. | FetchMixin |
cancelFetch() => void | cancel any in-flight fetch and bump fetchGeneration (always bumps, so callers can retrigger fetch autoruns even when nothing was in flight). This is the internal reset clearAllRpcData runs — it clears any user-cancel flag so the retrigger actually re-fetches. | FetchMixin |
cancelFetchByUser() => void | User-initiated cancel from the loading overlay. Stops the in-flight fetch and lands in a durable fetchCanceled state. Unlike cancelFetch, it does NOT bump fetchGeneration — so the fetch autoruns don't immediately restart the load. The user retries via reload (the overlay's retry button), or it clears on the next viewport change. | FetchMixin |
beforeDestroy() => void | Release an in-flight fetch's stop token on teardown. Without this, a display destroyed mid-fetch (track/view closed while loading) never signals the worker to abort the now-useless work, and its in-flight HTTP reads keep downloading. MST auto-chains lifecycle hooks, so a composing display can still define its own beforeDestroy. | FetchMixin |
endFetch(current: boolean, stopToken: StopToken) => void | The finally half of runFetch's bookkeeping, an action of its own because runFetchOnce's finally resumes on a microtask the flow does not own — a direct volatile write there is outside the action context, which is the one thing hoisting the sequence into a shared function costs. The stale branch is a superseded fetch: whoever superseded it — a newer begin, or cancel — already released this token. | FetchMixin |
runFetch(work: (ctx: FetchContext) => Promise<void>) => Promise<void> | Run a cancel-safe fetch (cancels any prior). The work callback gets a FetchContext with a stopToken to forward to the RPC and an isStale() check to short-circuit commits once the user has moved on. The MST-flow wrapper over the shared runFetchOnce sequence, and only the wrapper: the begin/clear/run/commit/error/end order, and the rules that keep a superseded run from writing back, are the same function every other fetch in the tree runs. What this adds is the observable bookkeeping a display needs — isLoading through activeStopToken, fetchGeneration, the user-cancel clear — and the flow itself, which is an action, so work's synchronous prefix runs untracked wherever a fetch autorun calls this. | FetchMixin |
setShowLegend(arg: boolean) => void | LegendMixin |