LinearBasicDisplay
Auto-generated @jbrowse/mobx-state-tree API for the current JBrowse release —
see pluggable elements for concepts. Provided by the
canvas plugin.
View source.
Example usage
A complete FeatureTrack config (e.g. genes from a GFF3) to paste into
tracks. displayMode sets the feature height preset (normal, compact, or
superCompact), or collapsed for a single-row overview:
{
type: 'FeatureTrack',
trackId: 'genes',
name: 'Genes',
assemblyNames: ['hg38'],
adapter: {
type: 'Gff3TabixAdapter',
uri: 'https://example.com/genes.gff3.gz',
},
displays: [
{
type: 'LinearBasicDisplay',
displayId: 'genes-LinearBasicDisplay',
height: 200,
displayMode: 'compact',
},
],
}
GPU-accelerated feature display with gene-specific UI on top of the shared
canvas base display (LinearCanvasBaseDisplay). This is the GPU stack — despite
the name it does NOT extend BaseLinearDisplay (the legacy block stack). See
display stacks.
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 |
|---|---|---|
configurationconfiguration: ConfigurationReference(configSchema) | LinearCanvasBaseDisplay | |
jexlFiltersSettingjexlFiltersSetting: types.maybe(types.array(types.string)) | Runtime "Filter by..." override. When set (even to an empty list) it replaces the jexlFilters config slot; when undefined the config default applies. Stored as already-jexl:-prefixed expressions (runtime convention), unlike the deferred-evaluation config slot. | LinearCanvasBaseDisplay |
| pinnedFeatureIds | Feature ids the user pinned to the top of the layout via the feature right-click menu. Pinned features are inserted first into the greedy row-packer, so they hold the topmost rows in their bp range across zoom re-packs (see packPreparedRef in layout.ts). stripDefault so a display with nothing pinned omits the empty array from its snapshot. Persisted by uniqueId, which resolves back to the same feature after a plain reload of the same remote file: every adapter id is adp-<configHash> (idMaker over the config) plus a file byte offset (tabix/BigBed) or a deterministic full-file parse index (plain GFF3/BED/VCF). Caveat: NOT robust to editing a file read by a plain (non-tabix) adapter (the indices shift), nor to local blob files (their handleId changes each session — but a blob can't reload its data across refresh anyway). Same basis for solo/hiddenFeatureIds. | LinearCanvasBaseDisplay |
| soloFeatureIds | "Show only these features": the collected set the user builds by ctrl+clicking features (or via the right-click menu). Only isolates the view once soloApplied is true — before that it's a highlighted selection that hides nothing, so the candidates stay clickable. Persistent so a view can be opened pre-focused declaratively (e.g. collapse-introns seeds it in the new view's snapshot). stripDefault so an unfocused display omits the empty array from its snapshot. | LinearCanvasBaseDisplay |
soloAppliedsoloApplied: types.stripDefault(types.boolean, false) | Whether the collected soloFeatureIds set is actually isolating the view (worker drops non-members). Decoupled from collection so building a multi-feature set doesn't hide the features mid-build. | LinearCanvasBaseDisplay |
| "Hide this feature" exclusion set (inverse of solo): the worker drops these from layout/drawing. Applies immediately per feature — no collect-then-apply. Persistent like the solo set, so a hidden feature stays hidden across reload/session save. stripDefault so a display with nothing hidden omits the empty array from its snapshot. | LinearCanvasBaseDisplay | |
| expandedGeneIds | Genes the user opened from the isoform badge on their own label: these draw every isoform whatever geneGlyphMode or the fit ladder's isoform rung would otherwise collapse them to. A per-GENE override of a track-wide setting, so the reader can open the one gene they are reading without turning every other gene on screen into a stack.The collapse is the worker's decision, so this is an RPC cache key (see rpcProps) and a click refetches the visible regions — the same contract solo/hidden already have, and for the same reason. Persistent and by uniqueId, on the same basis as solo/hidden/pinnedFeatureIds; stripDefault so a display with nothing opened omits the empty array from its snapshot. | LinearCanvasBaseDisplay |
| featureHighlights | Declarative feature highlights, typically seeded by a text search (highlight the gene you searched for). Each entry pins a feature by its span+name signature rather than its uniqueId — a search result carries no uniqueId to persist (unlike solo/hidden/pinned, which come from a click on a rendered feature and so DO have a reload-stable id) — and is resolved against rendered features on the main thread. stripDefault so a display with no highlights omits it from snapshot. | LinearCanvasBaseDisplay |
idid: ElementId | BaseDisplay | |
typetype: types.string | BaseDisplay |
Volatiles
| Member | Description | Defined by |
|---|---|---|
geneGlyphNoticeDismissedgeneGlyphNoticeDismissed: false | LinearBasicDisplay | |
rpcDataMaprpcDataMap: regionDataMap<LoadedFeatureData>('rpcDataMap') | LinearCanvasBaseDisplay | |
featureIdUnderMousefeatureIdUnderMouse: null as string | null | LinearCanvasBaseDisplay | |
subfeatureIdUnderMousesubfeatureIdUnderMouse: null as string | null | LinearCanvasBaseDisplay | |
mouseoverExtraInformationmouseoverExtraInformation: undefined as string[] | undefined | the hover tooltip's rows, each rendered as its own element — see hoverTooltipRows for why this is a list and not one HTML string | LinearCanvasBaseDisplay |
| sequenceHoverPosition | genomic base currently hovered in a feature sequence dialog opened from this display, read by the LGV crosshair overlay | LinearCanvasBaseDisplay |
contextMenuInfo: undefined as FeatureContextMenuInfo | undefined | LinearCanvasBaseDisplay | |
incrementalLayoutincrementalLayout: createIncrementalLayout() | LinearCanvasBaseDisplay | |
incrementalLayoutLabelsOnlyincrementalLayoutLabelsOnly: createIncrementalLayout() | LinearCanvasBaseDisplay | |
incrementalLayoutBodiesOnlyincrementalLayoutBodiesOnly: createIncrementalLayout() | LinearCanvasBaseDisplay | |
| incrementalLayoutDecimated | LinearCanvasBaseDisplay | |
| incrementalLayoutIsoforms | LinearCanvasBaseDisplay | |
morphFromTopsmorphFromTops: undefined as Map<string, number> | undefined | LinearCanvasBaseDisplay | |
morphProgressmorphProgress: 1 | LinearCanvasBaseDisplay | |
morphStartMsmorphStartMs: 0 | LinearCanvasBaseDisplay | |
morphFromMaxYmorphFromMaxY: 0 | LinearCanvasBaseDisplay | |
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 | |
loadedRegionsloadedRegions: regionDataMap<LoadedRegion>('loadedRegions') | regions whose data has been fetched and committed, keyed by displayedRegionIndex; populated only after the fetch work callback returns | MultiRegionDisplayMixin |
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 |
| densityStatsPerRegion | Per-region feature counts, keyed by displayedRegionIndex, so the verdict is a live max at the current bpPerPx. Cleared on navigation. | CanvasFeatureGateMixin |
Getters
| Member | Description | Defined by |
|---|---|---|
subfeatureLabels"below" | "none" | "overlay" | LinearBasicDisplay | |
geneGlyphMode"all" | "auto" | "longestCoding" | LinearBasicDisplay | |
showOnlyGenesboolean | LinearBasicDisplay | |
displayDirectionalChevronsboolean | LinearBasicDisplay | |
effectiveGeneGlyphMode"all" | "auto" | "longestCoding" | LinearBasicDisplay | |
showGeneGlyphNoticeboolean | LinearBasicDisplay | |
geneGlyphIsoformPicksIsoformPicks | What picked the transcript each collapsed gene in the loaded view is showing, summed over its regions: the chip names the commonest rule (RefSeq Select) instead of only saying that transcripts are hidden. | LinearBasicDisplay |
geneGlyphTrimmedGenesReadonlyMap<string, IsoformTrim> | LinearBasicDisplay | |
geneGlyphIsoformCapnumber | undefined | The isoform count the fit ladder trimmed to, or undefined when nothing on screen was trimmed. Read off the solve that did the trimming (fitStage.maxIsoforms) rather than off anything merely being hidden: a region fetched under longestCoding reports every multi-isoform gene as collapsed and the ladder never touched it. | LinearBasicDisplay |
geneGlyphCollapsedboolean | LinearBasicDisplay | |
isGeneLikeboolean | whether the right-clicked feature is a gene, transcript or RNA | LinearBasicDisplay |
geneGlyphNotice{…} | undefined | This display's answer to the base's isoform-collapse chrome hook (see geneGlyphNotice on the canvas base): absent unless the loaded data has a multi-isoform gene, so switching modes is meaningful. Its own block, after the actions it hands over, so self carries them. | LinearBasicDisplay |
colorLegendLegendItem[] | This display's answer to the base's colorLegend chrome hook, from the legend config slot. A jexl: color expression is a lookup table whose keys are readable only in the config, so the drawn feature carries the color and nothing carries its meaning; declaring the vocabulary is the only place that can come from. Empty slot draws nothing, so a track that declares no key is unaffected.Not auto-derived: the color a feature is painted has no name attached to it, and guessing one from a feature field would name whichever field happened to correlate. | LinearBasicDisplay |
| conf | the config typed off the concrete schema; ConfigurationReference erases self.configuration to any, so direct reads route through this to stay typed (same move as BaseAdapter<CONF>). | LinearCanvasBaseDisplay |
renderState{ scrollY: number; canvasWidth: number; canvasHeight: number; } | LinearCanvasBaseDisplay | |
labelScrollBucketnumber | LinearCanvasBaseDisplay | |
maxHeightnumber | LinearCanvasBaseDisplay | |
displayMode"collapsed" | "compact" | "normal" | "superCompact" | LinearCanvasBaseDisplay | |
labelFontSizenumber | LinearCanvasBaseDisplay | |
showLabelsMode"auto" | "description" | "name" | "nameAndDescription" | "none" | LinearCanvasBaseDisplay | |
showLabelsboolean | LinearCanvasBaseDisplay | |
showDescriptionsboolean | LinearCanvasBaseDisplay | |
showOutlineboolean | LinearCanvasBaseDisplay | |
featureColorany | LinearCanvasBaseDisplay | |
utrColorstring | LinearCanvasBaseDisplay | |
colorByMode"attribute" | "solid" | "strand" | LinearCanvasBaseDisplay | |
colorByAttributestring | LinearCanvasBaseDisplay | |
effectiveShowDescriptionsboolean | LinearCanvasBaseDisplay | |
selectedFeatureIdstring | undefined | LinearCanvasBaseDisplay | |
colorByCDSboolean | LinearCanvasBaseDisplay | |
showAminoAcidsboolean | LinearCanvasBaseDisplay | |
reversedRegionsSet<number> | LinearCanvasBaseDisplay | |
pinnedFeatureIdSetReadonlySet<string> | LinearCanvasBaseDisplay | |
expandedGeneIdSetReadonlySet<string> | LinearCanvasBaseDisplay | |
soloFeatureIdSetReadonlySet<string> | LinearCanvasBaseDisplay | |
number | LinearCanvasBaseDisplay | |
soloFeatureCountnumber | LinearCanvasBaseDisplay | |
pinnedFeatureCountnumber | LinearCanvasBaseDisplay | |
canonicalFeatureHighlightsFeatureHighlight[] | LinearCanvasBaseDisplay | |
resolvedHighlightsResolvedHighlights | LinearCanvasBaseDisplay | |
highlightedFeatureIdSetReadonlySet<string> | LinearCanvasBaseDisplay | |
layoutPinnedFeatureIdSetReadonlySet<string> | LinearCanvasBaseDisplay | |
featureHighlightCountnumber | LinearCanvasBaseDisplay | |
| layoutInputs | Layout inputs shared by the base layout and every fit-escalation layout, minus the per-config label/description reservation flags. One source so the candidate layouts can't drift on bpPerPx / orientation / display mode / pins / opened genes.expandedGeneIds belongs here and not on the rungs that trim, even though only they consult it: an expanded gene arrives carrying collapsedIsoformCount, so EVERY rung's pack trims it back to what the mode collapsed it to, and a rung that inherits the layout inputs without the exemption re-collapses the gene the user just opened. The three trimming rungs each added it for themselves; full and labels did not, which in grow — where full is the only rung — left no rung below to recover on.Each region's ref key is NOT here: it rides on the region itself, which is what the layout groups by (see LayoutRegionData). | LinearCanvasBaseDisplay |
layoutReadyboolean | Whether features can be laid out: data is fetched, in-bounds, and the view is measured. The shared readiness guard for every layout getter — an empty stack until then, so the GPU upload autorun has nothing to push and view-geometry getters aren't read before the view is measured. | LinearCanvasBaseDisplay |
onScreenFeatureIdsReadonlySet<string> | undefined | The features whose bp span touches the viewport. Why that is not the whole packed stack — and the matching rules — live with the pure featureIdsTouchingBlocks in layout.ts; this getter is the reactive half, deciding when to ask.Read off coarseDynamicBlocks (500ms debounced), like the layout's coarseBpPerPx, so a pan re-measures once it settles instead of breathing the whole stack every frame. Undefined until the view has coarse blocks, which every consumer reads as "measure the whole stack".Two things measure over it, for the same reason: the fit ladder ( fitMeasureFeatureIds) and the scroll extent (scrollExtentMaxY). | LinearCanvasBaseDisplay |
fitMeasureFeatureIdsReadonlySet<string> | undefined | The features fit mode measures its stack against: the on-screen set while the fit is running, undefined otherwise (which measures the whole stack). | LinearCanvasBaseDisplay |
decimatedBaseInputsLabelRoomFactorFreeInputs | The decimated rung's layout inputs minus the whitespace factor. Typed without labelRoomFactor so the solve's shared preparation provably can't depend on it (see createContentHeightProbe). | LinearCanvasBaseDisplay |
decimatedHeightProbe(labelRoomFactor: number) => number | Measures the decimated rung's stack height at any whitespace factor, against the features the ladder measures its rungs with — so the factor the solve picks is judged on the same stack the rung is then kept or rejected on.A getter, not a call inside the solve, because the preparation it holds (per-kind label widths, the two neighbor-room sorts — about a fifth of a layout) depends on the data and the layout inputs but NOT on the track height. Dragging the resize handle re-solves every frame; caching it here keeps those frames to the bisection's packs alone. | LinearCanvasBaseDisplay |
isoformsBaseInputsIsoformCountFreeInputs | The isoforms rung's layout inputs minus the count itself, typed without it so the solve's shared preparation provably cannot depend on it. Same reservation as labels — names kept, descriptions dropped — because the whole point of the rung is that names survive the trim. | LinearCanvasBaseDisplay |
isoformsHeightProbe(maxIsoforms: number) => number | Measures the isoforms rung's stack height at any isoform count, against the features the ladder measures its rungs with.A getter for the reason decimatedHeightProbe is one: the preparation it holds depends on the data and the layout inputs but NOT on the track height, and dragging the resize handle re-solves every frame. | LinearCanvasBaseDisplay |
maxIsoformsOnScreennumber | The most isoforms any gene ON SCREEN has — the top of the solve's bracket, and the count above which a trim can take nothing away. | LinearCanvasBaseDisplay |
fitIsoformCountnumber | undefined | The isoform count the isoforms rung commits at: the largest whose names-kept stack fits fitTargetHeight, so the most transcripts are kept without giving up a name. Undefined when nothing is worth trimming; 1 when even one transcript per gene overflows, which the decimated and bodies rungs below then inherit.Never in grow, whose height is its own content's — trimming there would shrink the track it was measured against. | LinearCanvasBaseDisplay |
fitIsoformsSolvedMap<number, FeatureDataResult> | The isoforms stack: every gene trimmed to fitIsoformCount transcripts, names intact. Falls back to the labels stack when there is nothing to trim. | LinearCanvasBaseDisplay |
baseLaidOutDataMapMap<number, FeatureDataResult> | Full reservation (names + descriptions): rendered at fit stage full and in non-fit modes, and the first stack fitStage probes. | LinearCanvasBaseDisplay |
fitLabelsOnlyLayoutMap<number, FeatureDataResult> | Names reserved, descriptions dropped — the labels stage's stack. With descriptions already off (config, or the auto density gate) this rung's reservation is the base one, so reuse that stack by reference rather than packing a byte-identical copy into a second memo. | LinearCanvasBaseDisplay |
fitDecimatedFactornumber | undefined | The whitespace factor the decimated rung commits at: the smallest one whose packed stack fits fitTargetHeight, so the most names are kept. Undefined when there is nothing to decimate (names off) or when even the most aggressive factor overflows. | LinearCanvasBaseDisplay |
fitDecimatedSolvedMap<number, FeatureDataResult> | The decimated stack: names kept only on features with at least fitDecimatedFactor × their label width in neighbour whitespace (plus pinned/highlighted, always). Filling the height with as many non-overlapping names as fit, rather than snapping between a few fixed rungs, is what this rung is for; it decimates by isolation, not by any notion of feature importance. Falls back to the labels stack when there is nothing to decimate or no factor fits. | LinearCanvasBaseDisplay |
fitBodiesOnlyLayoutMap<number, FeatureDataResult> | LinearCanvasBaseDisplay | |
fitSmallestBoxPxnumber | The unscaled height (px) of the shortest box on screen that the layout actually DRAWS — a UTR at its 0.65 fraction, a transcript rect inside a gene, a plain variant box — which is the one a uniform squeeze takes below a visible size first, and so the basis for the squeeze floor below. 0 when nothing is drawn, which makes that floor a no-op. A drawn box, not a feature's laid-out extent, and the distinction is the whole floor: a gene's extent is every stacked transcript plus its label rows, so a floor built on it promised 2px boxes while letting each transcript render at a third of a pixel. See minDrawnBoxHeight.Measured off the layout, never off the featureHeight config slot. The slot is a per-feature jexl callback slot (contextVariable: ['feature']), so reading it here — with no feature in scope — evaluates the callback against nothing and throws, taking the whole fit layout down with it. And even where it holds a plain number it names the plain-rect glyph's row height, which is not what a UTR or an isoform inside a gene is drawn at.Reads the full rung specifically because it is the stack the ladder always materializes, so it costs nothing extra. Box HEIGHTS don't vary across rungs (only the label reservation does), but the set of boxes counted can: minDrawnBoxHeight skips a feature the packer left unplaced, and bodies — the only rung a squeeze ever runs on — packs tighter and so places features full pushed past the row limit. On a stack deep enough to truncate at full, the floor is therefore measured over a subset and can allow a squeeze slightly past the MIN_FIT_BOX_PX promise. Reading it off bodies instead would be circular — that layout is chosen using this scale.Narrowed to fitMeasureFeatureIds, the same on-screen set every rung is measured over. | LinearCanvasBaseDisplay |
fitMinScalenumber | Floor on the fit squeeze: the smallest vertical scale that still leaves every drawn box at least MIN_FIT_BOX_PX tall. When boxes would pack tighter than this the squeeze stops here and the surplus scrolls instead of vanishing. squeezeFloorScale answers both degenerate cases (nothing drawn, or boxes already at the minimum) as 1 — no squeeze available — so there is nothing to clamp or zero-check here. | LinearCanvasBaseDisplay |
fitMaxScalenumber | Ceiling on the fit grow: the largest vertical scale before a feature body exceeds the height it would have outside fit mode. A sparse stack grows to fill the track only until its bodies reach that height, so fit never makes a feature taller than the display normally draws it. In normal display mode the laid-out body already is that height, pinning the scale at 1 (no grow, surplus stays whitespace); a compact mode may grow back up to — but not past — it. That works out to exactly 1 / multiplier, with no body height read at all: the grow target is the unmultiplied height and the laid-out body is that height times the mode's multiplier, so it cancels whatever it was per feature and the ceiling is purely the display mode's compact ratio (1 in normal mode → no grow). Unlike the squeeze floor, which has to know the shortest actual box (see fitSmallestBoxPx), this bound is uniform. | LinearCanvasBaseDisplay |
fitStageFitStage | The resolved fit outcome — which reservation level survived, its unscaled layout, and the vertical scale to fill the track — bundled so the three can never disagree. The ladder keeps the least reduction whose unscaled stack fits the track height: full (names + descriptions), else labels (drop descriptions), else decimated at a whitespace factor solved to the height (fitDecimatedSolved — keeps as many non-overlapping names as fit, filling the space continuously), else bodies (drop names too, pack tight) when even the tightest decimation overflows. The kept rung is then scaled to fill the track: grown up to fitMaxScale when it fits with room to spare, but never past the normal feature height — so in normal display mode grow is pinned at 1 and spare space stays whitespace, while a compact mode may enlarge back up to normal; or — only at the last bodies rung — squeezed down to fitMinScale and scrolled if even that overflows. Non-fit modes stay at full, scale 1. Read off the unscaled candidate heights so it can't feed back on its own scale. The ladder walk + scale math live in resolveFitLadder.Every rung is measured over fitMeasureFeatureIds — on screen in fit mode, everything otherwise — so the rung that survives and the squeeze it gets are decided by the stack in view, not by the half-viewport of buffered features packed on either side of it. | LinearCanvasBaseDisplay |
fitScalenumber | Uniform vertical scale for fit mode; 1 unless the resolved stack is being grown to fill the track (> 1) or the bodies stack squeezed to fit (< 1). | LinearCanvasBaseDisplay |
laidOutDataMapReadonlyMap<number, FeatureDataResult> | What every consumer (hit test, GPU upload, React render) reads: the resolved fit layout, cloned and scaled only when grown or squeezed. A fit stack shorter than the track stays top-anchored at y=0 (the surplus is bottom whitespace), so a relayout — an isoform collapse, a filter — packs back up against the top instead of jumping to a re-centered offset. Returned by reference off the untransformed path (scale 1) so the incremental-layout upload diff and Y-morph idle check stay intact. | LinearCanvasBaseDisplay |
renderedShowDescriptionsboolean | Descriptions are painted at the full stage, and at the isoforms one where a fixed-height track reached it — that ladder is full → isoforms and gives up transcripts rather than labels, so the rung packs the descriptions the settings asked for and this has to agree. Fit mode only reaches isoforms after labels dropped them. Every render-time consumer — label draw and the highlight/hit/SVG label-width reservation — reads this so a box never reserves width for a description it won't draw. | LinearCanvasBaseDisplay |
renderedShowLabelsboolean | Names are painted at every stage short of bodies (and whenever fit is off), where the packer reserved row height + overhang for the names it kept so they never overlap — including the decimated stage, whose per-feature pruning happens inside the layout (dropped names are removed from floatingLabelsData), not via this flag. At the bodies stage nothing is reserved, so all names are hidden rather than drawn on top of the boxes. Every render-time consumer reads this so hidden names reserve nothing. | LinearCanvasBaseDisplay |
renderedShowSubfeatureLabelsboolean | A subfeature label (a transcript name under its gene) is a worker-baked config choice rather than a fit rung — showLabels/showDescriptions govern only the feature's OWN two lines, and the packer reserves this label's row and overhang unconditionally to match. So it survives every rung the two flags above drop, including bodies.What it does NOT survive is the squeeze. The rows it was reserved in are spent in bodyHeightPx and scaled with everything else, while the text draws at the mode's own font size — so at scale 0.3 a gene's transcript names are painted over rows a third as tall as the text, on top of each other and of the boxes. Below 1 they are hidden instead; at 1 (every non-squeezed rung, and all of fixed/grow) nothing changed. | LinearCanvasBaseDisplay |
fitDropsFitDrops | What the ladder took from the labels the settings reserved, and how far it squeezed — the one derivation both user-facing notes read. | LinearCanvasBaseDisplay |
fitNotestring | undefined | The track-sizing control's account of what fit mode gave up, or undefined when nothing. The ladder drops labels silently and the "Labels" radio keeps saying they are on, so without this a user has no way to tell a track with no descriptions from one whose descriptions fit mode hid. | LinearCanvasBaseDisplay |
labelsFitHintstring | undefined | The note on the selected "Labels" radio while the ladder is not honouring it (see inertLabelHint). | LinearCanvasBaseDisplay |
morphEasednumber | LinearCanvasBaseDisplay | |
renderDataMapReadonlyMap<number, FeatureDataResult> | LinearCanvasBaseDisplay | |
settledMaxYnumber | LinearCanvasBaseDisplay | |
maxYnumber | LinearCanvasBaseDisplay | |
scrollExtentMaxYnumber | LinearCanvasBaseDisplay | |
hasOverflowboolean | LinearCanvasBaseDisplay | |
truncatedFeatureCountnumber | LinearCanvasBaseDisplay | |
contentHeightnumber | LinearCanvasBaseDisplay | |
scrollContentHeightnumber | LinearCanvasBaseDisplay | |
scrollableHeightnumber | LinearCanvasBaseDisplay | |
growTargetHeightnumber | LinearCanvasBaseDisplay | |
featureIdIndexMap<string, FlatbushItem> | LinearCanvasBaseDisplay | |
subfeatureIdIndexMap<string, SubfeatureInfo> | LinearCanvasBaseDisplay | |
hoveredFeatureFlatbushItem | null | LinearCanvasBaseDisplay | |
hoveredSubfeatureSubfeatureInfo | null | LinearCanvasBaseDisplay | |
featureItemMapMap<string, FeatureItemEntry> | LinearCanvasBaseDisplay | |
flatbushIndexesMap<number, FlatbushRegionIndexes> | LinearCanvasBaseDisplay | |
regionFetchKeystring | LinearCanvasBaseDisplay | |
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 |
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 |
heightMode"fit" | "fixed" | "grow" | The resolved track-height strategy (fixed/grow/fit). Promotable sentinel slot: resolveConf walks the customized-track -> session-default -> fixed cascade and never returns the inherit sentinel. | HeightModeMixin |
fitTargetHeightnumber | The drag-resizable track height as stored in the config slot — the fit target the fit/grow layout scales or packs content into. Read there instead of the reactive height getter to break the grow-mode cycle (height->grownHeight->layout->height). Equals height in fixed/fit. | HeightModeMixin |
growMaxHeightnumber | Ceiling grow mode sizes the track to, in px (content past it scrolls). Lives here rather than as a constant so a track whose whole point is a deep pileup can raise it; both displays that own a grownHeight read this, so the two can't diverge. | HeightModeMixin |
autoHeightboolean | grow mode as a boolean, derived from the unified heightMode slot. | HeightModeMixin |
fitHeightToDisplayboolean | fit mode as a boolean, derived from the unified heightMode slot. | HeightModeMixin |
grownHeightnumber | Target track height for grow: what the content wants, capped so a deep stack doesn't grow the track to thousands of px (the remainder scrolls). What installGrowExitBake bakes into the slot on exit. | HeightModeMixin |
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 |
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. | MultiRegionDisplayMixin |
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). | MultiRegionDisplayMixin |
canRenderboolean | Overrides RenderLifecycleMixin's default-true hook with the LGV precondition both foundations share — see foundationCanRender. | MultiRegionDisplayMixin |
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 alone: a phase that went loading on a moved regionFetchKey would raise the overlay into every zoom. | MultiRegionDisplayMixin |
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. | MultiRegionDisplayMixin |
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 cleared and 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.On screen this window is invisible (the clear lands a tick later and the loading scrim covers it), which is exactly why it needs saying: awaitSvgReady samples freshness once, and an export that samples it inside this window renders the data that is about to be discarded — or, once the clear lands mid-render, nothing at all. GWAS's LD auto-index is the 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.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 key strings alone. The settled half — the stamp a fetch committed under against the key a fetch now would use — is the foundation's already, through the isCacheValid term in dataCurrent, and an override restating it buys nothing: a second derivation of the key's vocabulary reads "16|fine" against a live "16" the day the key grows an axis, latches this true, and every export of the display then waits out awaitSvgReady's backstop instead of failing. | MultiRegionDisplayMixin |
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. | MultiRegionDisplayMixin |
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 this (not "the first datum arrived") is what keeps a multi-region/whole-genome export complete.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 regionFetchKey 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 still reads viewportWithinLoadedData alone: folding staleness into the phase raises the loading scrim into every zoom, which is the trade REJECTED_IDEAS.md "Folding content staleness into displayPhase" turned down and 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. | MultiRegionDisplayMixin |
| loadedAssembly | The assembly the data in hand came from, once it can answer about refNames — undefined before that.Off the first LOADED region rather than the view's displayed ones, which is the distinction that makes it belong here: 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. Handing back undefined until it can answer is what makes the caller write its fallback. | MultiRegionDisplayMixin |
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. | MultiRegionDisplayMixin |
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. | MultiRegionDisplayMixin |
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 three without being wired three times.This family's argument is spatial: loading also covers stale data (viewport past loaded) still on screen through the pre-refetch debounce. 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. | MultiRegionDisplayMixin |
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 |
awaitingPrerequisiteboolean | Overridable hook (default false), read only by the dev-only retry check (makeRetryContractCheck): "this run declined because a prerequisite fetch in another autorun has not landed, and its arrival wakes this one again". It defers the retry verdict to that later run rather than waiving it, so a display cannot spend its retry on a decline it called preliminary.Two displays say it, one per fetch foundation, which is why it lives beside fetchInert rather than on either: HiC's contacts fetch declines until CoreGetInfo lands, and MultiSampleVariantBaseModel's fetchNeeded declines until sourcesBase does. Both have a reload() that wakes the prerequisite's autorun as well as their own.It has to be strictly narrower than the gate it explains. One that restates the gate's negation makes every decline a deferred one, so no run is ever judged and the display has silently opted out — an exemption by another name. HiC is in that shape deliberately, because its gate and its prerequisite are one condition; what covers its retry instead is LinearHicDisplay/infoFetchFailure.test.ts.Not for a display deliberately not fetching at all — that is fetchInert above, which the loading scrim and the export read too. | 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 |
visibleFeatureDensityPerPxnumber | Density at the debounced coarseBpPerPx, so the verdict shares the layout cadence. Zero before the view is measured. | CanvasFeatureGateMixin |
maxFeatureDensitynumber | undefined | The worker's density budget; undefined when the axis may not act. | CanvasFeatureGateMixin |
Methods
| Member | Description | Defined by |
|---|---|---|
rpcProps() => {…} | LinearBasicDisplay | |
featureNarrowings() => { showOnlyGenes: { count: number; clear: () => void; }; } | LinearBasicDisplay | |
() => MenuItem[] | LinearBasicDisplay | |
() => MenuItem[] | LinearBasicDisplay | |
| LinearBasicDisplay | ||
() => MenuItem[] | LinearBasicDisplay | |
configuredFilters() => string[] | What the jexlFilters config slot alone declares, jexl:-prefixed.In its own block ahead of activeFilters / featureFilterCount so both reach it through self: featureFilterCount is super-captured by subclasses and called unbound, so a same-block this is undefined there. | LinearCanvasBaseDisplay |
activeFilters() => string[] | The filters actually applied, as jexl:-prefixed expressions — see activeJexlFilters, which is the shared two-tier resolution. | LinearCanvasBaseDisplay |
gpuProps() => { colorTable: Uint32Array<ArrayBuffer>; } | What the main-thread encode needs beyond a region's own data: the packed color for every theme class the worker emitted. The theme deliberately does NOT appear in rpcProps() above. It used to, so worker-baked CDS-frame and connector colors could follow it — and every field of that payload is an RPC cache key, so a light/dark toggle or a config theme edit re-downloaded and re-parsed every visible region of every canvas feature track. The worker now emits a class where it used to bake a theme color (colorClasses.ts) and this resolves it, so the same toggle is a re-encode of what is already loaded.session.palette, not session.theme: this crosses no boundary that needs MUI, and a getter rather than a pushed volatile so the SVG export and the RPC — neither of which has a component — see a real palette (ARCHITECTURE.md, "Theme-derived render inputs are session getters"). | LinearCanvasBaseDisplay |
| fitLayoutAt | One fit-escalation candidate: the stack packed with the given label/description reservation, via that config's own memo instance so each keeps stable references across renders. Empty until initialized/in-bounds, so the GPU upload autorun has nothing to push. | LinearCanvasBaseDisplay |
decimatedLayoutInputs(labelRoomFactor: number) => LayoutInputs | Layout inputs for the decimated rung at one whitespace factor. Every probe and the committed layout go through this single builder, so the stack the solve measures cannot differ from the stack it commits by a forgotten field. | LinearCanvasBaseDisplay |
solveLabelRoomFactor(trackHeight: number) => number | undefined | The whitespace factor the decimated rung commits at: the smallest one whose packed stack fits trackHeight (smallest = most names kept), or undefined when even the most aggressive decimation overflows. The bisection lives in solveLabelRoomFactor (fitLadder.ts), next to the ladder walk it serves. | LinearCanvasBaseDisplay |
morphOffsetFor(featureId: string) => number | LinearCanvasBaseDisplay | |
getFeatureById(featureId: string) => FlatbushItem | undefined | LinearCanvasBaseDisplay | |
| searchFeatureByID | LinearCanvasBaseDisplay | |
| renderSvg | LinearCanvasBaseDisplay | |
featureMarks() => Reversibles | Reversible state that MARKS features rather than hiding them — the highlight boxes and the pins holding features at the top of the layout. Same declaration shape as the narrowings above and the same undo rows, but deliberately a separate list: neither hides anything, so neither belongs in the "Filter by... (n)" count or under "Clear all filters". They need the rows for the same reason the narrowings do. Both outlive the navigation that created them and neither is reachable from the feature itself once the user has panned away — and a pin is worse than a highlight, because nothing on screen marks a pinned feature at all. | LinearCanvasBaseDisplay |
featureFilterCount() => number | How many independent things are narrowing what the display shows — the "(n)" in "Filter by... (n)", and the gate on "Clear all filters". Derived, so it cannot drift from the list it counts. | LinearCanvasBaseDisplay |
regionHasData(displayedRegionIndex: number) => boolean | LinearCanvasBaseDisplay | |
() => MenuItem[] | Flattened "Show..." submenu: all checkbox toggles first, then the radio groups (each under its own subHeader). Composed from the two extension points above so subclasses inject toggles/groups in place without rebuilding trackMenuItems from scratch. | LinearCanvasBaseDisplay |
() => MenuItem[] | The "Color by..." radio choices (solid/strand/attribute). Split out so subclasses can reuse them while assembling their own color menu. | LinearCanvasBaseDisplay |
() => MenuItem[] | Color-related track menu entries: a single "Color by..." entry whose "Solid color..." choice opens the solid+UTR color picker. A subclass changing the choices overrides colorBySubMenuItems (variants swaps in its consequence-impact and SV-type presets); this wrapper reads that back off self, so it is not the seam to override. | LinearCanvasBaseDisplay |
() => MenuItem[] | One "Feature height" menu with two independent radio groups: the size presets and, under a "Track sizing" subheader, how the track responds when there are more features than fit. | LinearCanvasBaseDisplay |
| 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 |
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 regionFetchKey (what a fetch now would produce) and regionHasData (did the last one store anything), and this compares the key against the one the region was fetched under. A subclass that changes what it fetches spells the change in the key, and one that forgets gets a redundant fetch rather than a cached answer for a zoom the data was never fetched at. | MultiRegionDisplayMixin |
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 |
observedMaxDensity(bpPerPx: number) => number | Highest features-per-pixel across the visible regions at bpPerPx. | CanvasFeatureGateMixin |
Actions
| Member | Description | Defined by |
|---|---|---|
setSubfeatureLabels(value: "below" | "none" | "overlay") => void | LinearBasicDisplay | |
setGeneGlyphMode(value: "all" | "auto" | "longestCoding") => void | LinearBasicDisplay | |
dismissGeneGlyphNotice() => void | LinearBasicDisplay | |
setShowOnlyGenes(value: boolean) => void | LinearBasicDisplay | |
setDisplayDirectionalChevrons(value: boolean) => void | LinearBasicDisplay | |
beginYMorph(fromTops: Map<string, number>, fromMaxY: number) => void | LinearCanvasBaseDisplay | |
setMorphProgress(t: number) => void | LinearCanvasBaseDisplay | |
endYMorph() => void | LinearCanvasBaseDisplay | |
| setRpcData | LinearCanvasBaseDisplay | |
pruneRpcDataMapToVisible(visibleDisplayedRegionIndices: Set<number>) => void | LinearCanvasBaseDisplay | |
startRenderingBackend(backend: CanvasFeatureRenderingBackend) => void | LinearCanvasBaseDisplay | |
togglePinnedFeature(featureId: string) => void | LinearCanvasBaseDisplay | |
clearPinnedFeatures() => void | LinearCanvasBaseDisplay | |
toggleExpandedGene(featureId: string) => void | Open or re-collapse one gene's isoforms, from the badge on its own label. Nothing else has to change: the trim reports what it WOULD hide for a gene in the set as well as for one out of it (see IsoformTrimPlan.expandedHidden), so the badge that opened a gene is the badge that closes it again. | LinearCanvasBaseDisplay |
clearExpandedGenes() => void | Re-collapse every gene opened from a badge. | LinearCanvasBaseDisplay |
toggleSoloFeature(featureId: string) => void | LinearCanvasBaseDisplay | |
applySolo() => void | LinearCanvasBaseDisplay | |
soloFeature(featureId: string) => void | LinearCanvasBaseDisplay | |
clearSolo() => void | LinearCanvasBaseDisplay | |
hideFeature(featureId: string) => void | LinearCanvasBaseDisplay | |
() => void | LinearCanvasBaseDisplay | |
| setHover | LinearCanvasBaseDisplay | |
clearHover() => void | LinearCanvasBaseDisplay | |
(info: FeatureContextMenuInfo) => void | LinearCanvasBaseDisplay | |
() => void | LinearCanvasBaseDisplay | |
setFeatureHighlights(highlights: FeatureHighlight[]) => void | LinearCanvasBaseDisplay | |
addFeatureHighlightForItem(target: HighlightTarget, refName: string) => void | LinearCanvasBaseDisplay | |
removeFeatureHighlightsForId(featureId: string) => void | LinearCanvasBaseDisplay | |
clearFeatureHighlights() => void | LinearCanvasBaseDisplay | |
selectFeature(feature: Feature) => void | Open the feature-details widget. The adapter's header metadata (VCF INFO/FORMAT descriptions, etc.) is fetched first and passed as descriptions so the widget can label attribute rows and — for the variant widget — resolve the ANN/CSQ column names; without it that table renders headerless. CoreGetMetadata returns null for adapters that expose none, so this is a no-op for those tracks. | LinearCanvasBaseDisplay |
clearSelection() => void | LinearCanvasBaseDisplay | |
| setShowLabels | LinearCanvasBaseDisplay | |
setJexlFilters(filters?: string[] | undefined) => void | Sets the runtime filter override (already-jexl:-prefixed expressions). Pass undefined to clear it and fall back to the config jexlFilters slot. | LinearCanvasBaseDisplay |
setShowOutline(value: boolean) => void | LinearCanvasBaseDisplay | |
setFeatureColor(color?: string | undefined) => void | LinearCanvasBaseDisplay | |
setUtrColor(color?: string | undefined) => void | LinearCanvasBaseDisplay | |
setSequenceHoverPosition(pos: SequenceHoverPosition | undefined) => void | LinearCanvasBaseDisplay | |
| setDisplayMode | LinearCanvasBaseDisplay | |
openSetColorDialog(showUtrColor?: any) => void | LinearCanvasBaseDisplay | |
openColorByAttributeDialog() => void | LinearCanvasBaseDisplay | |
openFilterDialog() => void | LinearCanvasBaseDisplay | |
| fetchFullFeature | LinearCanvasBaseDisplay | |
clearAllFeatureFilters() => void | Reverse every narrowing. Derived from the same list featureFilterCount counts, so a subclass that adds one gets both halves at once and the menu cannot offer a recovery that doesn't recover. | LinearCanvasBaseDisplay |
| selectFeatureById | LinearCanvasBaseDisplay | |
reload() => void | Clears the loaded regions and fetches straight away, rather than waiting out FetchVisibleRegions' 600ms debounce as the rest of the family does — Retry and Force load are both clicks, and this is the display the user is most often clicking on. | LinearCanvasBaseDisplay |
| fetchNeeded | LinearCanvasBaseDisplay | |
clearHoveredFeature() => void | Fills BaseDisplay's hover-clear hook, which the fetch foundation's reaction calls on every viewport change.The painting is a sticky canvas, so a pan or zoom under a stationary cursor fires no mousemove and no mouseleave, and the highlight box keeps naming whatever used to be under it. | LinearCanvasBaseDisplay |
setStatusMessage(status?: RpcStatus | undefined) => void | BaseDisplay | |
setError(error?: unknown) => void | 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 |
setHeightMode(mode: "fit" | "fixed" | "grow") => void | Set the track-height strategy by writing the unified heightMode slot; the modes are mutually exclusive by construction. Entering a non-fixed mode drops a leftover scroll offset that the reconfigured height contradicts — neither fit nor grow generally scrolls, and a sticky canvas left at an out-of-range offset paints clipped or blank with no DOM scroll event to resync it. Displays with more transient state to reset super-capture this. | HeightModeMixin |
setShowLegend(arg: boolean) => void | LegendMixin | |
| setLoadedRegion | The raw write behind ctx.commitRegion, and not what a fetch should call: a display naming its own span is the bug this family spent a release on, and going through the context is what makes that inexpressible — see RegionFetchContext. Direct callers are tests staging an already-loaded display.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. | MultiRegionDisplayMixin |
dropLoadedRegion(displayedRegionIndex: number) => void | Forget one region — for a display pruning what has scrolled off screen. | MultiRegionDisplayMixin |
clearDisplaySpecificData() => void | no-op base — subclasses override to clear rpcDataMap etc. | MultiRegionDisplayMixin |
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. | MultiRegionDisplayMixin |
| 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. | MultiRegionDisplayMixin |
afterAttach() => void | installs the fetch-lifecycle autoruns (DisplayedRegionsChange, FetchVisibleRegions, SettingsInvalidate, ClearBlockingStateOnViewportChange) | MultiRegionDisplayMixin |
| 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 |
| setDensityStats | CanvasFeatureGateMixin | |
clearGateMeasurements() => void | CanvasFeatureGateMixin | |
| commitGateMeasurements | Commit a batch of per-region fetch results on the density axis, judged by the tier captured at issue. The byte axis is commitFetchBytes. | CanvasFeatureGateMixin |
Related links
- Guide: URL query parameter API