# LinearMultiRowFeatureDisplay

Auto-generated @jbrowse/mobx-state-tree API for the current JBrowse release —
see [pluggable elements](https://jbrowse.org/jb2-staging/docs/developer_guide/) for concepts. Provided by the
`canvas` plugin.
[View source](https://github.com/GMOD/jbrowse-components/blob/main/plugins/canvas/src/LinearMultiRowFeatureDisplay/model.ts).

Multi-row interval painter (chromosome / ancestry painting). Partitions a single
feature track into stacked rows by a feature attribute and paints each feature
as a colored block on its row. GPU-rendered (WebGL/Canvas2D fallback) via the
shared per-region lifecycle. Rows are a `sources` chain (discovered →
layout-reconciled → subtree-filtered) and the left sidebar (labels +
dendrogram + reorder) is the shared `TreeSidebarMixin`.

The configuration slots for this model are documented on its
[config schema page](../../config/linearmultirowfeaturedisplay).

Members a composed model contributes are listed here too, so these tables are
the whole surface.

## Properties

<!-- prettier-ignore -->
| Member | Description | Defined by |
| --- | --- | --- |
| <span id="property-type">**type**</span><br><code>type: types.literal('LinearMultiRowFeatureDisplay')</code> |  | LinearMultiRowFeatureDisplay |
| <span id="property-configuration">**configuration**</span><br><code>configuration: ConfigurationReference(configSchema)</code> |  | LinearMultiRowFeatureDisplay |
| <span id="property-hiddencategories">**hiddenCategories**</span><br><code>hiddenCategories: types.array(types.string)</code> | Legend categories toggled off (by label). Features painted in a hidden category's color are dropped from both render paths and the hit-test. See `hiddenColors` / `toggleCategory`. | LinearMultiRowFeatureDisplay |
| <span id="property-id">**id**</span><br><code>id: ElementId</code> |  | [BaseDisplay](../basedisplay#property-id) |
| <span id="property-layout">**layout**</span><br><code>layout: types.stripDefault(types.frozen&lt;S[]&gt;(), [])</code> |  | [TreeSidebarMixin](../treesidebarmixin#property-layout) |
| <span id="property-clustertree">**clusterTree**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>clusterTree: types.stripDefault(types.maybe(types.string), unde…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>clusterTree: types.stripDefault(types.maybe(types.string), undefined)</code></pre></dialog></span> |  | [TreeSidebarMixin](../treesidebarmixin#property-clustertree) |
| <span id="property-clusterprovenance">**clusterProvenance**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>clusterProvenance: types.stripDefault( types.maybe(types.frozen…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>clusterProvenance: types.stripDefault(&#10;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;types.maybe(types.frozen&lt;ClusterProvenance&gt;()),&#10;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;undefined,&#10;&#160;&#160;&#160;&#160;&#160;&#160;)</code></pre></dialog></span> | <span data-pagefind-ignore>What `clusterTree` was computed from — the locus and the settings. Set only for a tree this app computed; a supplied phylogeny (maf's `.nh`) leaves it undefined. Persisted with the tree so it survives a session snapshot, which is the case that most needs it: a shared link otherwise hands over a dendrogram with no way to learn its locus.</span> | [TreeSidebarMixin](../treesidebarmixin#property-clusterprovenance) |
| <span id="property-treeareawidth">**treeAreaWidth**</span><br><code>treeAreaWidth: types.stripDefault(types.number, 80)</code> |  | [TreeSidebarMixin](../treesidebarmixin#property-treeareawidth) |
| <span id="property-subtreefilter">**subtreeFilter**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>subtreeFilter: types.stripDefault( types.maybe(types.array(type…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>subtreeFilter: types.stripDefault(&#10;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;types.maybe(types.array(types.string)),&#10;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;undefined,&#10;&#160;&#160;&#160;&#160;&#160;&#160;)</code></pre></dialog></span> |  | [TreeSidebarMixin](../treesidebarmixin#property-subtreefilter) |
| <span id="property-runclustering">**runClustering**</span><br><code>runClustering: types.maybe(types.boolean)</code> | <span data-pagefind-ignore>Transient declarative launch spec, the same idea as `LinearGenomeView`'s `init`: a session or config sets this true and the real clustering RPC runs once automatically, with no dialog, as soon as the display reports itself ready. `setupRunClusteringAutorun` clears it afterwards, so a saved session never re-triggers.<br><br>Lives here rather than on each display because it is the trigger for a run whose *output* — `clusterTree`, `clusterProvenance`, `layout` — is this mixin's state. Three displays declared it identically, each with its own wrapper module that existed to code-split the clustering code and, along the way, hand-wrote the same six-member duck type of the display. Splitting inside the `run` callback does the same job and loads on a run rather than on every attach. What each run actually *is* stays per display, in that callback.</span> | [TreeSidebarMixin](../treesidebarmixin#property-runclustering) |
| <span id="property-clusterregion">**clusterRegion**</span><br><code>clusterRegion: types.maybe(types.string)</code> | <span data-pagefind-ignore>Where that run reads from, as a locstring (whitespace-separated for several). Clustering is region-scoped, so running it over the visible window feeds the estimator whatever happens to be on screen; naming the locus instead lets a session cluster on the signal and then show it against its context — otherwise a zoom the user has to perform in the right order. Cleared with `runClustering`, since it is that flag's argument and a locus left standing describes a run that is not coming.</span> | [TreeSidebarMixin](../treesidebarmixin#property-clusterregion) |
| <span id="property-sortrowsby">**sortRowsBy**</span><br><code>sortRowsBy: types.maybe(types.frozen&lt;RowSortSpec&gt;())</code> | <span data-pagefind-ignore>Transient declarative launch spec, the same idea as `runClustering`: set `{refName, pos}` to order the rows once by the value each carries at that genomic column — the session-expressible form of the right-click "Sort rows by ... here". `setupRowSortAutorun` applies it once the region containing it has loaded and then clears it, so the resulting `layout` persists but a saved session never re-sorts.<br><br>Where clustering orders rows by the whole region in view and `layout` states an order outright, only this one says "rank them here", which is what lets a figure open a cohort ranked at a candidate locus with the surrounding context still on screen. What the value at the column *is* stays per display, in its `sortRows` callback.</span> | [TreeSidebarMixin](../treesidebarmixin#property-sortrowsby) |

## Volatiles

<!-- prettier-ignore -->
| Member | Description | Defined by |
| --- | --- | --- |
| <span id="volatile-rpcdatamap">**rpcDataMap**</span><br><code>rpcDataMap: regionDataMap&lt;MultiRowRegionData&gt;('rpcDataMap')</code> |  | LinearMultiRowFeatureDisplay |
| <span id="volatile-prefersoffset">**prefersOffset**</span><br><code>prefersOffset: true</code> |  | LinearMultiRowFeatureDisplay |
| <span id="volatile-hoveredmultirowfeature">**hoveredMultiRowFeature**</span><br><code>hoveredMultiRowFeature: undefined as MultiRowHit &#124; undefined</code> | The feature under the mouse, or undefined when not hovering a block. Pure hover identity — the cursor position that places the tooltip is component state, so moving inside one block doesn't invalidate this.<br><br>Named apart from the `hoveredFeature` getter it fills, because `BaseDisplay` declares that hook as a computed and MST refuses to instantiate a volatile over one. | LinearMultiRowFeatureDisplay |
| <span id="volatile-error">**error**</span><br><code>error: undefined as unknown</code> |  | [BaseDisplay](../basedisplay#volatile-error) |
| <span id="volatile-statusmessage">**statusMessage**</span><br><code>statusMessage: undefined as string &#124; undefined</code> |  | [BaseDisplay](../basedisplay#volatile-statusmessage) |
| <span id="volatile-statusprogress">**statusProgress**</span><br><code>statusProgress: undefined as number &#124; undefined</code> | <span data-pagefind-ignore>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.</span> | [BaseDisplay](../basedisplay#volatile-statusprogress) |
| <span id="volatile-scrolltop">**scrollTop**</span><br><code>scrollTop: 0</code> |  | [TrackHeightMixin](../trackheightmixin#volatile-scrolltop) |
| <span id="volatile-loadedregions">**loadedRegions**</span><br><code>loadedRegions: regionDataMap&lt;LoadedRegion&gt;('loadedRegions')</code> | <span data-pagefind-ignore>regions whose data has been fetched and committed, keyed by displayedRegionIndex; populated only after the fetch work callback returns</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#volatile-loadedregions) |
| <span id="volatile-forceloadtrack">**forceLoadTrack**</span><br><code>forceLoadTrack: false</code> | <span data-pagefind-ignore>The force-load button's track-wide approval. Volatile so it never reaches a saved session; the `forceLoad` config slot is the durable form.</span> | [RegionTooLargeMixin](../regiontoolargemixin#volatile-forceloadtrack) |
| <span id="volatile-byteestimate">**byteEstimate**</span><br><code>byteEstimate: undefined as ByteEstimate &#124; undefined</code> | <span data-pagefind-ignore>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.</span> | [RegionTooLargeMixin](../regiontoolargemixin#volatile-byteestimate) |
| <span id="volatile-gatemeasuredviewportkey">**gateMeasuredViewportKey**</span><br><code>gateMeasuredViewportKey: undefined as string &#124; undefined</code> | <span data-pagefind-ignore>The viewport key the gate last asked the adapter about, on either axis. Separate from `byteEstimate` because a density refusal measures no bytes.</span> | [RegionTooLargeMixin](../regiontoolargemixin#volatile-gatemeasuredviewportkey) |
| <span id="volatile-canvasdrawn">**canvasDrawn**</span><br><code>canvasDrawn: false</code> | <span data-pagefind-ignore>flips true on first paint; read by test selectors to detect render</span> | [RenderLifecycleMixin](../renderlifecyclemixin#volatile-canvasdrawn) |
| <span id="volatile-currentrenderingbackend">**currentRenderingBackend**</span><br><code>currentRenderingBackend: undefined</code> | <span data-pagefind-ignore>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.</span> | [RenderLifecycleMixin](../renderlifecyclemixin#volatile-currentrenderingbackend) |
| <span id="volatile-rendertick">**renderTick**</span><br><code>renderTick: 0</code> | <span data-pagefind-ignore>counter the render autorun observes; bumped to force a re-render</span> | [RenderLifecycleMixin](../renderlifecyclemixin#volatile-rendertick) |
| <span id="volatile-autorunsinstalled">**autorunsInstalled**</span><br><code>autorunsInstalled: false</code> | <span data-pagefind-ignore>guards attachRenderingBackend so the autorun pair spawns once per instance</span> | [RenderLifecycleMixin](../renderlifecyclemixin#volatile-autorunsinstalled) |
| <span id="volatile-rendererror">**renderError**</span><br><code>renderError: undefined</code> | <span data-pagefind-ignore>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).</span> | [RenderLifecycleMixin](../renderlifecyclemixin#volatile-rendererror) |
| <span id="volatile-activestoptoken">**activeStopToken**</span><br><code>activeStopToken: undefined as StopToken &#124; undefined</code> | <span data-pagefind-ignore>stop token of the in-flight fetch, or undefined when idle</span> | [FetchMixin](../fetchmixin#volatile-activestoptoken) |
| <span id="volatile-fetchgeneration">**fetchGeneration**</span><br><code>fetchGeneration: 0</code> | <span data-pagefind-ignore>bumps at every fetch end; autoruns read it to re-evaluate, and it doubles as the staleness epoch inside runFetch</span> | [FetchMixin](../fetchmixin#volatile-fetchgeneration) |
| <span id="volatile-reloadcounter">**reloadCounter**</span><br><code>reloadCounter: 0</code> | <span data-pagefind-ignore>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).</span> | [FetchMixin](../fetchmixin#volatile-reloadcounter) |
| <span id="volatile-statuswindow">**statusWindow**</span><br><code>statusWindow: createStatusWindow(writeStatus(self))</code> | <span data-pagefind-ignore>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`.</span> | [FetchMixin](../fetchmixin#volatile-statuswindow) |
| <span id="volatile-fetchcanceled">**fetchCanceled**</span><br><code>fetchCanceled: false</code> | <span data-pagefind-ignore>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).</span> | [FetchMixin](../fetchmixin#volatile-fetchcanceled) |
| <span id="volatile-fetchrotation">**fetchRotation**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>fetchRotation: createStopTokenRotation(self, { statusWindow: se…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>fetchRotation: createStopTokenRotation(self, {&#10;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;statusWindow: self.statusWindow,&#10;&#160;&#160;&#160;&#160;&#160;&#160;})</code></pre></dialog></span> | <span data-pagefind-ignore>**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.<br><br>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).<br><br>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`.</span> | [FetchMixin](../fetchmixin#volatile-fetchrotation) |
| <span id="volatile-densitystatsperregion">**densityStatsPerRegion**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>densityStatsPerRegion: regionDataMap&lt;RegionDensityStats&gt;( 'dens…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>densityStatsPerRegion: regionDataMap&lt;RegionDensityStats&gt;(&#10;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;'densityStatsPerRegion',&#10;&#160;&#160;&#160;&#160;&#160;&#160;)</code></pre></dialog></span> | <span data-pagefind-ignore>Per-region feature counts, keyed by `displayedRegionIndex`, so the verdict is a live max at the current `bpPerPx`. Cleared on navigation.</span> | [CanvasFeatureGateMixin](../canvasfeaturegatemixin#volatile-densitystatsperregion) |
| <span id="volatile-hoveredtreenode">**hoveredTreeNode**</span><br><code>hoveredTreeNode: undefined as HoveredTreeNode &#124; undefined</code> |  | [TreeSidebarMixin](../treesidebarmixin#volatile-hoveredtreenode) |
| <span id="volatile-treecanvas">**treeCanvas**</span><br><code>treeCanvas: null as HTMLCanvasElement &#124; null</code> |  | [TreeSidebarMixin](../treesidebarmixin#volatile-treecanvas) |
| <span id="volatile-mouseovercanvas">**mouseoverCanvas**</span><br><code>mouseoverCanvas: null as HTMLCanvasElement &#124; null</code> |  | [TreeSidebarMixin](../treesidebarmixin#volatile-mouseovercanvas) |
| <span id="volatile-contextmenuinfo">**contextMenuInfo**</span><br><code>contextMenuInfo: undefined as Info &#124; undefined</code> |  | [ContextMenuMixin](../contextmenumixin#volatile-contextmenuinfo) |

## Getters

<!-- prettier-ignore -->
| Member | Description | Defined by |
| --- | --- | --- |
| <span id="getter-view">**view**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>ModelInstanceTypeProps&lt;_OverrideProps&lt;_OverrideProps&lt;…&gt;, { ...;…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>ModelInstanceTypeProps&lt;_OverrideProps&lt;_OverrideProps&lt;…&gt;, { ...; }&gt;&gt; &amp; ... 25 more ... &amp; IStateTreeNode&lt;...&gt;</code></pre></dialog></span> |  | LinearMultiRowFeatureDisplay |
| <span id="getter-hoveredfeature">**hoveredFeature**</span><br><code>MultiRowHit &#124; undefined</code> | Fills `BaseDisplay`'s cross-display hover hook, which the view reads to publish `session.hovered`. | LinearMultiRowFeatureDisplay |
| <span id="getter-conf">**conf**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>ModelInstanceTypeProps&lt;Record&lt;…&gt;&gt; &amp; { setSubschema(slotName: st…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>ModelInstanceTypeProps&lt;Record&lt;…&gt;&gt; &amp; { setSubschema(slotName: string, data: Record&lt;string, unknown&gt;): any; setSlot(slotName: string, value: unknown): void; } &amp; IStateTreeNode&lt;...&gt;</code></pre></dialog></span> | config typed off the concrete schema (ConfigurationReference erases it to any); direct reads route through here to stay typed | LinearMultiRowFeatureDisplay |
| <span id="getter-densitygateenabled">**densityGateEnabled**</span><br><code>boolean</code> | Multi-row paints features into fixed lanes, so a high total feature count (e.g. a whole-chromosome haplotype painting with many segments per row) is not a per-glyph render cost — only the byte/download budget should gate it. Disable the density axis of CanvasFeatureGateMixin so the "too many features" banner never shows here. | LinearMultiRowFeatureDisplay |
| <span id="getter-showrowseparators">**showRowSeparators**</span><br><code>boolean</code> |  | LinearMultiRowFeatureDisplay |
| <span id="getter-colorrowlabels">**colorRowLabels**</span><br><code>boolean</code> | Whether the sidebar label box is tinted with the color its row is painted in — see `labelSources`. | LinearMultiRowFeatureDisplay |
| <span id="getter-partitionfield">**partitionField**</span><br><code>string</code> | The `partitionField` slot — an attribute name, or a `jexl:` expression — forwarded to the worker, which binds `feature` and resolves it per feature (`makeFeaturePartitionResolver`).<br><br>Reads the raw slot value, not `readConfObject`/`getConf`: this is a transport read, and the resolving readers evaluate a callback against whatever context the call passes, which here is none. The rmsk recipe this slot documents resolved that way to `''` — `feature.name` is `undefined` and `split` is total — and `''` shipped to the worker as an attribute name, so every feature answered `feature.get('')` and the track drew one unnamed row, with nothing thrown anywhere. Pinned end-to-end by partitionFieldTransport.test.ts. | LinearMultiRowFeatureDisplay |
| <span id="getter-lengthfield">**lengthField**</span><br><code>string</code> | Feature attribute holding a signed bp length change vs the reference. Empty = the indel-glyph pass is off. A fetch input: it decides whether the worker packs `featureDeltas` at all. | LinearMultiRowFeatureDisplay |
| <span id="getter-roworder">**rowOrder**</span><br><code>string[]</code> | Optional explicit row order from config; values listed here are placed first, remaining discovered values follow in sorted order. | LinearMultiRowFeatureDisplay |
| <span id="getter-colorconfig">**colorConfig**</span><br><code>string &#124; undefined</code> | Raw `color` slot (a CSS color or `jexl:` string, or undefined when unset), forwarded to the worker which resolves it per feature. | LinearMultiRowFeatureDisplay |
| <span id="getter-samplecolormap">**sampleColorMap**</span><br><code>Record&lt;string, string&gt;</code> | Map of partition value → color, forwarded to the worker which applies it over the per-feature `color`. | LinearMultiRowFeatureDisplay |
| <span id="getter-rowproportion">**rowProportion**</span><br><code>number</code> |  | LinearMultiRowFeatureDisplay |
| <span id="getter-rowgroups">**rowGroups**</span><br><code>RowGroup[]</code> | Regex-to-group entries tagging rows with a sidebar swatch color. Applied in `sources`, downstream of `layout`, so the derived color never lands in persisted state — a `rowGroups` edit then takes effect on a session that already has a clustered layout, instead of losing to the stale copy. | LinearMultiRowFeatureDisplay |
| <span id="getter-sourceswithoutlayout">**sourcesWithoutLayout**</span><br><code>MultiRowSource[]</code> | Rows discovered in the loaded data: the distinct partition values across all loaded regions, ordered by the config `rowOrder` then sorted. The pre-layout, pre-filter input to the arrangement dialog and to clustering. | LinearMultiRowFeatureDisplay |
| <span id="getter-useditemrgb">**usedItemRgb**</span><br><code>boolean</code> | Whether the loaded data colored itself via `itemRgb` (only possible with the `color` slot at its default). Suppresses the per-row palette, which would otherwise paint over those colors. | LinearMultiRowFeatureDisplay |
| <span id="getter-effectivepartitionfield">**effectivePartitionField**</span><br><code>string</code> | The attribute the rows are actually partitioned on: the `partitionField` slot, or — with the slot at its empty default — what the worker picked off the columns the data turned out to carry (`resolvePartitionField`, `repClass` on a RepeatMasker file). `name` until something has loaded, which is what auto falls back to anyway.<br><br>The resolved twin of the `partitionField` transport read above, which stays the raw slot because that is what the fetch has to send — resolve it there and the worker would be handed the main thread's guess at a question only the worker can answer, and the auto pick could never happen. Everything that asks "which attribute are these rows" — the menu's radio, the clustering call that has to land features in the rows the painting drew — reads this one.<br><br>Off the first loaded region rather than a vote across them: one file's columns are one file's columns, and a region that disagreed would be a different partition, not a tiebreak. | LinearMultiRowFeatureDisplay |
| <span id="getter-partitioncandidates">**partitionCandidates**</span><br><code>string[]</code> | The attribute names the loaded features carry, which is what the "Partition by..." menu offers. Unioned across regions and re-sorted, since two regions can be served by adapters that saw different optional columns.<br><br>Empty until something is loaded, which is the menu's own disabled condition — the names are discovered from the data rather than declared, the same way the rows themselves are. | LinearMultiRowFeatureDisplay |
| <span id="getter-editablesources">**editableSources**</span><br><code>MultiRowSource[]</code> | Discovered rows with the user's arrangement (reorder/relabel) applied — what the arrangement dialog edits. Not subtree-filtered. | LinearMultiRowFeatureDisplay |
| <span id="getter-sources">**sources**</span><br><code>MultiRowSource[]</code> | The display rows: `editableSources` narrowed by the active subtree filter. Render order, label order, and `rowIndexByValue` all key off this, so reordering/filtering flows through to the painting.<br><br>`rowGroups` decorates them here, and **partitions them into blocks only when no cluster tree already names this order**. Grouping and ordering are two questions about one axis, and a config carrying both used to resolve them by having `rowGroups` win and the dendrogram vanish behind `StaleTreeHint` — a silent trade of the tree for a stripe. A clustered track is exactly where the stripe is worth most: it is the axis the clustering did NOT see, so reading it across the blocks the tree found is what says whether they correspond. | LinearMultiRowFeatureDisplay |
| <span id="getter-rowindexbyvalue">**rowIndexByValue**</span><br><code>Map&lt;string, number&gt;</code> |  | LinearMultiRowFeatureDisplay |
| <span id="getter-rowcolorstringsbyindex">**rowColorStringsByIndex**</span><br><code>(string &#124; undefined)[]</code> | Per-row color (CSS) by display row — the single per-row resolver (dialog color > config `sampleColorMap` > palette-when-default). `undefined` where the row has none, and the worker-baked per-feature color paints instead. | LinearMultiRowFeatureDisplay |
| <span id="getter-rowcolorsbyindex">**rowColorsByIndex**</span><br><code>(number &#124; undefined)[]</code> | `rowColorStringsByIndex` packed for the painters. Applied at render time over the worker-baked per-feature `color` slot, so any color change repaints without a refetch. | LinearMultiRowFeatureDisplay |
| <span id="getter-labelsources">**labelSources**</span><br><code>MultiRowSource[]</code> | The rows as the sidebar draws them: `sources`, with each row's painted color carried into `labelColor` when `colorRowLabels` is on.<br><br>Off by default because the tint is not free — it spends the label box, which `rowGroups` also spends (and wins here, along with a color set in the arrangement dialog: both are something the user or the config asked for by name, where this is derived). It earns the space on a painting whose rows are palette-colored and whose labels are a wall of similar names, and costs legibility on one where the labels were doing fine, which is why it is a toggle rather than a judgment made here.<br><br>A no-op in per-feature color mode: there is no single color a row is painted in, `rowColorStringsByIndex` is `undefined` throughout, and the labels come back exactly as `sources` had them. | LinearMultiRowFeatureDisplay |
| <span id="getter-nrow">**nrow**</span><br><code>number</code> | Number of displayed rows (at least 1, so the auto-fit division is safe and the canvas mounts before data arrives). | LinearMultiRowFeatureDisplay |
| <span id="getter-fittargetheight">**fitTargetHeight**</span><br><code>number</code> | The track height that auto-fit mode divides among rows: the `height` config slot (its default, or a drag-resized value written to it). | LinearMultiRowFeatureDisplay |
| <span id="getter-colorlegend">**colorLegend**</span><br><code>LegendEntry[]</code> | Categorical color key. The explicit `legend` config slot wins when set (for color-encoded categories with no feature attribute to key on, e.g. an itemRgb ancestry painting); otherwise it's auto-derived from the loaded data as distinct `(featureName -> per-feature color)` pairs among per-feature-colored rows. Empty in per-row palette / sampleColorMap mode (where the sidebar labels are the key) and for non-categorical (unnamed / all-distinct) data. See resolveConfiguredLegend / buildColorLegend. | LinearMultiRowFeatureDisplay |
| <span id="getter-hiddencategoryset">**hiddenCategorySet**</span><br><code>ReadonlySet&lt;string&gt;</code> | `hiddenCategories` as a Set, and the single place "is this category hidden?" is answered: the on-screen and SVG-export legends (dimmed rows), `hiddenColors`, and the track menu's category checkboxes all read it. One derivation rather than four, so a change to how a label is matched can't reach some of the legends and miss the rest. | LinearMultiRowFeatureDisplay |
| <span id="getter-hiddencolors">**hiddenColors**</span><br><code>ReadonlySet&lt;number&gt;</code> | ABGR colors currently hidden via the legend's category toggles: the `colorLegend` colors whose label is in `hiddenCategories`. Both render paths and the hit-test skip features painted in one of these, so toggling a category off drops it everywhere without a refetch. `colorLegend` has one entry per distinct color (see buildColorLegend), so each toggle maps to exactly one color. | LinearMultiRowFeatureDisplay |
| <span id="getter-autorowheight">**autoRowHeight**</span><br><code>number</code> | The auto-fit row height: the display height split evenly across rows, so all rows stay visible as the row count grows.<br><br>Not floored at a pixel: a display given thousands of rows would otherwise stop fitting and grow to a pixel a row instead, which is a track thousands of pixels tall rather than the dense overview asked for. The floor lives in `rowBand`, where a sub-pixel row is widened for drawing without changing how many rows fit. | LinearMultiRowFeatureDisplay |
| <span id="getter-maxcanvasheight">**maxCanvasHeight**</span><br><code>number</code> | Ceiling on the whole row stack in CSS px, because this display sizes its canvas to its *content* and never scrolls: `height` is the canvas, so nothing downstream bounds it. What goes wrong past it, and why it is a function rather than a constant, is `maxCanvasCssPx`'s. A fixed 14px row height over a 1,987-row cohort — two clicks in the Row height menu — is 27,818 CSS px, well past it.<br><br>MAF meets the same limit with `maxRowsHeight`, but its canvas is a viewport it can scroll the overflow into. Here the cap has to land on the row height instead (below), so the rows thin out and every one of them stays on screen — which is what this display is for. | LinearMultiRowFeatureDisplay |
| <span id="getter-effectiverowheight">**effectiveRowHeight**</span><br><code>number</code> | The one override of `RowHeightMixin`'s resolved height, and the reason is structural: this display sizes its canvas to its *content* instead of scrolling a viewport, so nothing downstream bounds the row stack and the cap has to land here. `maxCanvasHeight / nrow` is that cap; past it the rows thin out and every one of them stays on screen, which is what this display is for.<br><br>The sentinel resolution and the non-positive floor stay the mixin's `resolveRowHeight` — `featureAt` and `rowBand` divide by this, so a `height` slot configured to 0 must not reach them as a 0.<br><br>The cap is not floored back up afterwards: a row below a pixel is legitimate here (see `autoRowHeight`), and `rowBand` is where a sub-pixel row is widened for drawing without changing how many of them fit. | LinearMultiRowFeatureDisplay |
| <span id="getter-rowgrouplegend">**rowGroupLegend**</span><br><code>LegendItem[]</code> | Key for the `rowGroups` stripe: one row per (group, color) pair present, in first-appearance order, in the color the sidebar swatch is painted.<br><br>**Empty unless that stripe is the only thing carrying row identity.** Above `rowLabelsCarryText` the sidebar writes each row's name beside it and a key would restate them. Below it `RowLabelsOverlay` drops to an unlabelled swatch, and 1,987 canids at 0.32 px a row are then five group colors with nothing saying what any of them is — the same hole multi-wiggle's key closed, on the display whose figure the swatch stripe was built for.<br><br>A different vocabulary from `colorLegend`, which keys the per-feature painting and drives the category toggles; these rows name rows, and are not toggleable. `legendIsReadable` refuses the degenerate cases — one group, or more groups than a reader can scan.<br><br>`showRowLabels` gates it too, because that toggle takes the stripe with it rather than only the text: `RowLabelsOverlay` renders no `SvgRowLabels` at all when it is off, and `SvgRowLabels` is where the swatch runs are drawn. Without this the labels-off dense case published a "Row groups" key naming colors that appear nowhere on the plot — the exact inverse of the case above, from the same one-line question asked about only half of what draws the stripe. | LinearMultiRowFeatureDisplay |
| <span id="getter-height">**height**</span><br><code>number</code> | Override BaseLinearDisplay.height so the track container matches the rendering canvas (numRows × effectiveRowHeight). In auto-fit mode this resolves to `fitTargetHeight`; in fixed mode it grows with the row count — this display grows to its content rather than scrolling a fixed viewport, which is why a fixed-mode drag re-pins the row height (see `setHeight`) instead of leaving it alone the way the scrolling row displays do.<br><br>Bounded by `maxCanvasHeight` through `effectiveRowHeight`, so growing to the content stops at the canvas limit rather than at nothing. A drag past it therefore returns 0 from `resizeHeight` and simply stalls. | LinearMultiRowFeatureDisplay |
| <span id="getter-hierarchy">**hierarchy**</span><br><code>ClusterHierarchyNode &#124; undefined</code> | Positioned dendrogram (when a cluster tree exists and describes the rows on screen). Leaves spaced over `height`, branches over `treeAreaWidth`.<br><br>Passes the *drawn* row names, which is the backstop for anything that reorders `sources` downstream of `layout` where `setLayout`'s invalidation cannot see it: such a reorder drops the dendrogram rather than drawing it against rows it does not name. `rowGroups` used to be the way to reach that state, and no longer is — it declines to partition while a tree describes the rows (see `sources`), so the two now compose instead of one silently costing the other. | LinearMultiRowFeatureDisplay |
| <span id="getter-sidebaroffset">**sidebarOffset**</span><br><code>number</code> | Pixel width reserved on the left for the tree (0 when no tree shows). | LinearMultiRowFeatureDisplay |
| <span id="getter-spatialindex">**spatialIndex**</span><br><code>TreeSpatialIndex &#124; undefined</code> |  | LinearMultiRowFeatureDisplay |
| <span id="getter-featurepaintinputs">**featurePaintInputs**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>Pick&lt;MultiRowRenderState, "hiddenColors" &#124; "rowColorsByIndex" &#124;…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>Pick&lt;MultiRowRenderState, "hiddenColors" &#124; "rowColorsByIndex" &#124; "rowIndexByValue"&gt;</code></pre></dialog></span> | The three inputs to "does this feature paint, and in what color" — `featurePainting`'s whole contract, and nothing else.<br><br>Split out of `renderState` because the two have different lifetimes and the difference is expensive. `renderState` also carries the canvas box and the row geometry, all four of which move on a track-height drag or a window resize; this moves only on a reorder, a recolor, a category toggle or a refetch. The consumers that answer the paint question and nothing else — the per-region GPU encode autorun and the hit test's memoized contexts — read this, so a resize no longer re-encodes every region's instance buffer (a full pass over every feature, per drag frame) to arrive at byte-identical output. | LinearMultiRowFeatureDisplay |
| <span id="getter-renderstate">**renderState**</span><br><code>MultiRowRenderState</code> | Render state passed to the GPU/Canvas2D backend each frame. | LinearMultiRowFeatureDisplay |
| <span id="getter-indelglyphregions">**indelGlyphRegions**</span><br><code>Map&lt;number, MultiRowRegionData&gt; &#124; undefined</code> | Per-region data for the indel-glyph overlay, or undefined when the `lengthField` slot is unset and there is no glyph pass.<br><br>A computed returning a plain `Map` rather than `rpcDataMap` itself: the overlay draws inside an effect, so nothing it touches there is tracked, and handing it the ObservableMap would mean a refetch never redrew the glyphs. Rebuilding here makes the read happen where MobX sees it and gives the overlay a value whose identity changes exactly when the data does. | LinearMultiRowFeatureDisplay |
| <span id="getter-drawnfeaturesbyrow">**drawnFeaturesByRow**</span><br><code>Map&lt;number, DrawnFeaturesByRow&gt;</code> | Per-region drawn features bucketed by display row, keyed by displayedRegionIndex. Built through `forEachDrawnFeature` off the same `featurePaintInputs` the painters use, so the hit test cannot answer "is this feature drawn" differently from the paint that put it there.<br><br>Memoized because the hit test needs it per *pointer frame*: `featureAt` runs on every rAF-coalesced mouse move, and building it inline meant resolving the region's whole partition list (a couple of thousand rows on a cohort painting) sixty times a second for a value that changes only when the rows, the colors or the data do.<br><br>Bucketing rather than a bare context, because the memo only removed the *setup* from the pointer frame and left the scan: the row is known before the search starts, so a hit on a 200-feature row was still walking the region's other half-million. See `findTopDrawnFeatureInRow`.<br><br>`featurePaintInputs` rather than the whole `renderState`, so that "changes only when the rows, the colors or the data do" is actually true — see there. | LinearMultiRowFeatureDisplay |
| <span id="getter-highlightedblockrect">**highlightedBlockRect**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>{ left: number; width: number; top: number; height: number; } &#124;…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>{ left: number; width: number; top: number; height: number; } &#124; undefined</code></pre></dialog></span> | Screen box of the block to mark, or undefined when there's nothing to mark. The hover drops when a right-click menu opens (else its tooltip sticks under the menu), so the menu's own feature stands in — the block a menu is acting on is exactly the one that should stay marked. | LinearMultiRowFeatureDisplay |
| <span id="getter-parenttrack">**parentTrack**</span><br><code>AbstractTrackModel</code> |  | [BaseDisplay](../basedisplay#getter-parenttrack) |
| <span id="getter-renderingcomponent">**RenderingComponent**</span><br><code>FC&lt;…&gt;</code> |  | [BaseDisplay](../basedisplay#getter-renderingcomponent) |
| <span id="getter-displayblurb">**DisplayBlurb**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>FC&lt;{ model: ModelInstanceTypeProps&lt;{ id: IOptionalIType&lt;ISimple…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>FC&lt;{ model: ModelInstanceTypeProps&lt;{ id: IOptionalIType&lt;ISimpleType&lt;string&gt;, [undefined]&gt;; type: ISimpleType&lt;string&gt;; }&gt; &amp; { ...; } &amp; { ...; } &amp; IStateTreeNode&lt;...&gt;; }&gt; &#124; null</code></pre></dialog></span> |  | [BaseDisplay](../basedisplay#getter-displayblurb) |
| <span id="getter-adapterconfig">**adapterConfig**</span><br><code>Record&lt;string, unknown&gt;</code> |  | [BaseDisplay](../basedisplay#getter-adapterconfig) |
| <span id="getter-isminimized">**isMinimized**</span><br><code>boolean</code> | <span data-pagefind-ignore>Returns true if the parent track is minimized. Used to skip expensive operations like autoruns when track is not visible.</span> | [BaseDisplay](../basedisplay#getter-isminimized) |
| <span id="getter-featurenoun">**featureNoun**</span><br><code>string</code> | <span data-pagefind-ignore>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".<br><br>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.<br><br>**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.<br><br>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.</span> | [BaseDisplay](../basedisplay#getter-featurenoun) |
| <span id="getter-featurewidgettype">**featureWidgetType**</span><br><code>{ type: string; id: string; }</code> | <span data-pagefind-ignore>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.<br><br>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.</span> | [BaseDisplay](../basedisplay#getter-featurewidgettype) |
| <span id="getter-resizing">**resizing**</span><br><code>boolean</code> | <span data-pagefind-ignore>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).<br><br>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.</span> | [TrackHeightMixin](../trackheightmixin#getter-resizing) |
| <span id="getter-scrollableheight">**scrollableHeight**</span><br><code>number</code> | <span data-pagefind-ignore>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.<br><br>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.</span> | [TrackHeightMixin](../trackheightmixin#getter-scrollableheight) |
| <span id="getter-host">**host**</span><br><code>RegionHost</code> | <span data-pagefind-ignore>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-host) |
| <span id="getter-canvaswidthpx">**canvasWidthPx**</span><br><code>number</code> | <span data-pagefind-ignore>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.<br><br>`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.<br><br>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.<br><br>SVG export is the one exception: the export shell has no outline, so `renderSvg` overrides `canvasWidth` with the shell's own width (see `LgvSvgBodyProps`).</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-canvaswidthpx) |
| <span id="getter-canrender">**canRender**</span><br><code>boolean</code> | <span data-pagefind-ignore>Overrides `RenderLifecycleMixin`'s default-true hook with the LGV precondition both foundations share — see `foundationCanRender`.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-canrender) |
| <span id="getter-viewportwithinloadeddata">**viewportWithinLoadedData**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.<br><br>**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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-viewportwithinloadeddata) |
| <span id="getter-viewportempty">**viewportEmpty**</span><br><code>boolean</code> | <span data-pagefind-ignore>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`.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-viewportempty) |
| <span id="getter-layoutready">**layoutReady**</span><br><code>boolean</code> | <span data-pagefind-ignore>Overridable hook (default false): whether a searchable feature layout currently exists. Any display defining a feature-lookup method (`searchFeatureByID`, `getFeatureById`) must override it, so callers can tell "laid out, but off-display" from "no layout exists yet" — a distinction only the display can make. See packages/display-kit/CLAUDE.md §"Four readiness axes".</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-layoutready) |
| <span id="getter-regionfetchkey">**regionFetchKey**</span><br><code>string</code> | <span data-pagefind-ignore>Overridable hook (default `''`): what a fetch issued right now would produce for a region, as a string — the display's per-region content axis. `fetchRegions` captures it before it issues the RPC and stamps it beside the loaded region; `isCacheValid` refetches a region whose stamp no longer matches. Wiggle returns `String(view.bpPerPx)` (adr-008), canvas the peptide-overlay threshold, the variant matrix its zoom in matrix mode only.<br><br>NOT an `rpcProps()` field: this invalidates one region's held data where `rpcProps` invalidates all of it, and a zoom-swinging value in the RPC payload blanks the display at the force-load floor — see REGION_TOO_LARGE.md §"How the verdict is built".<br><br>A getter, so the observables it reads register as dependencies of `FetchVisibleRegions`; MobX runs an action untracked and the autorun would keep a stale answer.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-regionfetchkey) |
| <span id="getter-datasuperseded">**dataSuperseded**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.<br><br>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.<br><br>**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.<br><br>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-datasuperseded) |
| <span id="getter-renderblocks">**renderBlocks**</span><br><code>RenderBlock[]</code> | <span data-pagefind-ignore>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-renderblocks) |
| <span id="getter-datacurrent">**dataCurrent**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.<br><br>**`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.<br><br>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-datacurrent) |
| <span id="getter-loadedassembly">**loadedAssembly**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(ModelInstanceTypeProps&lt;…&gt; &amp; { error: unknown; loadingP: Promis…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(ModelInstanceTypeProps&lt;…&gt; &amp; { error: unknown; loadingP: Promise&lt;…&gt; &#124; undefined; ... 10 more ...; refNameMismatches: Map&lt;…&gt;; } &amp; ... 13 more ... &amp; IStateTreeNode&lt;...&gt;) &#124; undefined</code></pre></dialog></span> | <span data-pagefind-ignore>The assembly the data in hand came from, once it can answer about refNames — `undefined` before that.<br><br>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.<br><br>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-loadedassembly) |
| <span id="getter-svgready">**svgReady**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-svgready) |
| <span id="getter-paintinert">**paintInert**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-paintinert) |
| <span id="getter-displayphase">**displayPhase**</span><br><code>DisplayPhase</code> | <span data-pagefind-ignore>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.<br><br>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.<br><br>A subclass customizes this through `fetchInert` (FetchMixin), never by overriding the getter — see that hook.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#getter-displayphase) |
| <span id="getter-gateenabled">**gateEnabled**</span><br><code>boolean</code> | <span data-pagefind-ignore>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`.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-gateenabled) |
| <span id="getter-bytegateadapterconfig">**byteGateAdapterConfig**</span><br><code>Record&lt;string, unknown&gt;</code> | <span data-pagefind-ignore>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.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-bytegateadapterconfig) |
| <span id="getter-configuredfetchsizelimit">**configuredFetchSizeLimit**</span><br><code>number</code> | <span data-pagefind-ignore>The display's `fetchSizeLimit` slot, from `regionTooLargeConfigSchemaFields`.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-configuredfetchsizelimit) |
| <span id="getter-densitytoolarge">**densityTooLarge**</span><br><code>boolean</code> | <span data-pagefind-ignore>The density axis's verdict; canvas overrides it.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-densitytoolarge) |
| <span id="getter-bytegateadapterpath">**byteGateAdapterPath**</span><br><code>string[]</code> | <span data-pagefind-ignore>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.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-bytegateadapterpath) |
| <span id="getter-adapterfetchsizelimit">**adapterFetchSizeLimit**</span><br><code>number &#124; undefined</code> | <span data-pagefind-ignore>The measured adapter's own `fetchSizeLimit` slot, read off the live track config rather than the `adapterConfig` snapshot, which omits slots at their default.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-adapterfetchsizelimit) |
| <span id="getter-configforceload">**configForceLoad**</span><br><code>boolean</code> | <span data-pagefind-ignore>The declarative `forceLoad` slot.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-configforceload) |
| <span id="getter-gateviewport">**gateViewport**</span><br><code>GateViewport &#124; undefined</code> | <span data-pagefind-ignore>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.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-gateviewport) |
| <span id="getter-bytegateadapterkey">**byteGateAdapterKey**</span><br><code>string</code> | <span data-pagefind-ignore>Which tier the estimate is about, as a comparable string.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-bytegateadapterkey) |
| <span id="getter-aboveforceloadfloor">**aboveForceLoadFloor**</span><br><code>boolean</code> | <span data-pagefind-ignore>Whether the span on screen is at or above `AUTO_FORCE_LOAD_BP`, the one comparison against that constant. False on an unmeasured view.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-aboveforceloadfloor) |
| <span id="getter-gateexempt">**gateExempt**</span><br><code>boolean</code> | <span data-pagefind-ignore>Nothing may gate on either axis: the `forceLoad` slot or the button.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-gateexempt) |
| <span id="getter-estimatedfetchbytes">**estimatedFetchBytes**</span><br><code>number &#124; undefined</code> | <span data-pagefind-ignore>The stored estimate's bytes; undefined when nothing has been measured.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-estimatedfetchbytes) |
| <span id="getter-gatemeasurementstale">**gateMeasurementStale**</span><br><code>boolean</code> | <span data-pagefind-ignore>Whether the last measurement is about a viewport the user has since left. True before any measurement.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-gatemeasurementstale) |
| <span id="getter-gatebytelimit">**gateByteLimit**</span><br><code>number</code> | <span data-pagefind-ignore>The byte budget: the adapter's limit, else the display's, doubled below `AUTO_FORCE_LOAD_BP`. Read only through `resolvedByteLimit()`.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-gatebytelimit) |
| <span id="getter-gateactive">**gateActive**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-gateactive) |
| <span id="getter-densitygateactive">**densityGateActive**</span><br><code>boolean</code> | <span data-pagefind-ignore>`gateActive` plus the density axis's own terms: the axis is on, and the span is above the floor.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-densitygateactive) |
| <span id="getter-toolargestatus">**tooLargeStatus**</span><br><code>RegionTooLargeStatus</code> | <span data-pagefind-ignore>The verdict and its banner text, from the stored estimate against `resolvedByteLimit()` and the density axis when it may act.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-toolargestatus) |
| <span id="getter-regiontoolarge">**regionTooLarge**</span><br><code>boolean</code> |  | [RegionTooLargeMixin](../regiontoolargemixin#getter-regiontoolarge) |
| <span id="getter-regiontoolargereason">**regionTooLargeReason**</span><br><code>string</code> | <span data-pagefind-ignore>Banner text for the axis that tripped; empty when not too large.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-regiontoolargereason) |
| <span id="getter-zoomcanreleasegate">**zoomCanReleaseGate**</span><br><code>boolean</code> | <span data-pagefind-ignore>Whether "zoom in to see features" is honest advice. Density always releases on zoom; bytes only if the last zoom-in moved the estimate.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-zoomcanreleasegate) |
| <span id="getter-gateskipsmeasuredviewport">**gateSkipsMeasuredViewport**</span><br><code>boolean</code> | <span data-pagefind-ignore>The skip both fetch skeletons apply: the banner is up and its measurement already describes the viewport on screen.</span> | [RegionTooLargeMixin](../regiontoolargemixin#getter-gateskipsmeasuredviewport) |
| <span id="getter-renderscanvas">**rendersCanvas**</span><br><code>boolean</code> | <span data-pagefind-ignore>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).<br><br>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.</span> | [RenderLifecycleMixin](../renderlifecyclemixin#getter-renderscanvas) |
| <span id="getter-painted">**painted**</span><br><code>boolean</code> | <span data-pagefind-ignore>**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.<br><br>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.<br><br>`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.</span> | [RenderLifecycleMixin](../renderlifecyclemixin#getter-painted) |
| <span id="getter-isloading">**isLoading**</span><br><code>boolean</code> | <span data-pagefind-ignore>true while a fetch is active</span> | [FetchMixin](../fetchmixin#getter-isloading) |
| <span id="getter-isloadingorcanceled">**isLoadingOrCanceled**</span><br><code>boolean</code> | <span data-pagefind-ignore>`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.<br><br>Arc read `isLoading` directly and had exactly that hole. It is a getter here so no family has to remember the second term.</span> | [FetchMixin](../fetchmixin#getter-isloadingorcanceled) |
| <span id="getter-fetchinert">**fetchInert**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.<br><br>**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:<br><br>- 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.<br><br>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.<br><br>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.<br><br>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`.</span> | [FetchMixin](../fetchmixin#getter-fetchinert) |
| <span id="getter-awaitingprerequisite">**awaitingPrerequisite**</span><br><code>boolean</code> | <span data-pagefind-ignore>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.<br><br>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.<br><br>**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`.<br><br>Not for a display deliberately not fetching at all — that is `fetchInert` above, which the loading scrim and the export read too.</span> | [FetchMixin](../fetchmixin#getter-awaitingprerequisite) |
| <span id="getter-rpcpropscachekey">**rpcPropsCacheKey**</span><br><code>string</code> | <span data-pagefind-ignore>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.<br><br>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.</span> | [FetchMixin](../fetchmixin#getter-rpcpropscachekey) |
| <span id="getter-visiblefeaturedensityperpx">**visibleFeatureDensityPerPx**</span><br><code>number</code> | <span data-pagefind-ignore>Density at the debounced `coarseBpPerPx`, so the verdict shares the layout cadence. Zero before the view is measured.</span> | [CanvasFeatureGateMixin](../canvasfeaturegatemixin#getter-visiblefeaturedensityperpx) |
| <span id="getter-maxfeaturedensity">**maxFeatureDensity**</span><br><code>number &#124; undefined</code> | <span data-pagefind-ignore>The worker's density budget; undefined when the axis may not act.</span> | [CanvasFeatureGateMixin](../canvasfeaturegatemixin#getter-maxfeaturedensity) |
| <span id="getter-showlegend">**showLegend**</span><br><code>boolean</code> | <span data-pagefind-ignore>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`.</span> | [LegendMixin](../legendmixin#getter-showlegend) |
| <span id="getter-showlegenddisplaytypedefault">**showLegendDisplayTypeDefault**</span><br><code>Pin</code> | <span data-pagefind-ignore>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`.</span> | [LegendMixin](../legendmixin#getter-showlegenddisplaytypedefault) |
| <span id="getter-rowheight">**rowHeight**</span><br><code>number</code> | <span data-pagefind-ignore>Raw per-row height setting: `0` is fit-to-display-height, any positive value is a fixed px height. The resolved value is `effectiveRowHeight` — consumers read that, never this. On the config rather than the display snapshot for the same reason `height` is: the config node outlives the display instance, so a fixed height survives unticking and reticking the track.</span> | [RowHeightMixin](../rowheightmixin#getter-rowheight) |
| <span id="getter-showtree">**showTree**</span><br><code>boolean</code> | <span data-pagefind-ignore>Whether the dendrogram sidebar is drawn.</span> | [TreeSidebarMixin](../treesidebarmixin#getter-showtree) |
| <span id="getter-showbranchlength">**showBranchLength**</span><br><code>boolean</code> | <span data-pagefind-ignore>Whether tree nodes are positioned by branch length (dendrogram) or evenly by topology (cladogram).</span> | [TreeSidebarMixin](../treesidebarmixin#getter-showbranchlength) |
| <span id="getter-showrowlabels">**showRowLabels**</span><br><code>boolean</code> | <span data-pagefind-ignore>Whether each row's name is drawn over the left of the plot.</span> | [TreeSidebarMixin](../treesidebarmixin#getter-showrowlabels) |
| <span id="getter-parsedtree">**parsedTree**</span><br><code>HierarchyNode&lt;NewickNode&gt; &#124; undefined</code> |  | [TreeSidebarMixin](../treesidebarmixin#getter-parsedtree) |
| <span id="getter-root">**root**</span><br><code>HierarchyNode&lt;NewickNode&gt; &#124; undefined</code> |  | [TreeSidebarMixin](../treesidebarmixin#getter-root) |
| <span id="getter-treehasbranchlengths">**treeHasBranchLengths**</span><br><code>boolean</code> |  | [TreeSidebarMixin](../treesidebarmixin#getter-treehasbranchlengths) |
| <span id="getter-roworderiscustom">**rowOrderIsCustom**</span><br><code>boolean</code> | <span data-pagefind-ignore>Whether the rows have been arranged away from the order they arrived in — what "Reset row order" is offered on. A written `layout` here; a display whose config seeds `layout` on load (the multi-sample variant displays' `colorBy` / `groupBy`) overrides it to compare against that seed, so the reset does not appear on a track nobody has touched.</span> | [TreeSidebarMixin](../treesidebarmixin#getter-roworderiscustom) |

## Methods

<!-- prettier-ignore -->
| Member | Description | Defined by |
| --- | --- | --- |
| <span id="method-rpcprops">**rpcProps**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>() =&gt; { partitionField: string; lengthField: string; colorConfi…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>() =&gt; { partitionField: string; lengthField: string; colorConfig: string &#124; undefined; }</code></pre></dialog></span> | Fetch-input cache keys (tier-1, via SettingsInvalidate → refetch). Color is resolved in the worker, so the raw color slot is a key. | LinearMultiRowFeatureDisplay |
| <span id="method-featureat">**featureAt**</span><br><code>(mouseX: number, mouseY: number) =&gt; MultiRowHit &#124; undefined</code> | Hit-test the feature under a display-relative pixel: row from `mouseY / rowHeight`, genomic bp from the view, then the first feature on that row whose `[start,end)` covers the bp. Returns undefined over the sidebar, off-row, out-of-bounds, or over a gap.<br><br>The sidebar bound is `treeSidebarRightEdge`, not `sidebarOffset`: the latter is where labels are *drawn* from, while the resize handle sitting in the 4px past it is the sidebar's interactive edge, and a hit under the handle would fight the drag. Same bound the wiggle family hit-tests against, and the same one the crosshair's guide stops at. | LinearMultiRowFeatureDisplay |
| <span id="method-contexttargetat">**contextTargetAt**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(mouseX: number, mouseY: number) =&gt; { refName: string; pos: num…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(mouseX: number, mouseY: number) =&gt; { refName: string; pos: number; hit: MultiRowHit &#124; undefined; } &#124; undefined</code></pre></dialog></span> | What a right-click at this display-relative pixel resolves to: the genomic position the menu's position-scoped rows act on ("Sort rows by color here"), and the feature there when the click landed on one.<br><br>Undefined wherever no menu should open, which is what the component needs in order to decide whether to `preventDefault` — over the tree sidebar, which overlays this display and owns its own menu, and in the inter-region gutter, where there is no base to name. Model-side and beside `featureAt` because it is the same question about the same pixel; spelled out in the component it re-derived `pxToBp`, the sidebar bound and the painted base that `featureAt` was about to derive again. | LinearMultiRowFeatureDisplay |
| <span id="method-regionhasdata">**regionHasData**</span><br><code>(displayedRegionIndex: number) =&gt; boolean</code> | The reader-side check of the write-side rule: `loadedRegions` is written where the payload is stored (`RegionFetchContext`), so an entry here without one in `rpcDataMap` is that rule being broken. It costs a map lookup and it decides which way the break fails — a refetch, or a viewport that reads as covered against data nobody has and never asks again.<br><br>No zoom rule beside it: the worker's output is absolute genomic uint32, so `regionFetchKey` stays at its empty default.<br><br>A view, not an action: as an action MobX untracks the `rpcDataMap` read and `FetchVisibleRegions` keeps a stale answer. | LinearMultiRowFeatureDisplay |
| <span id="method-contextmenuitems">**contextMenuItems**</span><br><code>() =&gt; MenuItem[]</code> | Items for the right-click context menu, built from the clicked position (contextMenuInfo). "Sort rows by color here" is the interactive twin of the declarative `sortRowsBy`. | LinearMultiRowFeatureDisplay |
| <span id="method-trackmenuitems">**trackMenuItems**</span><br><code>() =&gt; MenuItem[]</code> |  | LinearMultiRowFeatureDisplay |
| <span id="method-renderingprops">**renderingProps**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>() =&gt; { displayModel: ModelInstanceTypeProps&lt;{ id: IOptionalITy…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>() =&gt; { displayModel: ModelInstanceTypeProps&lt;{ id: IOptionalIType&lt;…&gt;; type: ISimpleType&lt;string&gt;; }&gt; &amp; { ...; } &amp; { ...; } &amp; { ...; } &amp; IStateTreeNode&lt;...&gt;; }</code></pre></dialog></span> | <span data-pagefind-ignore>props passed to the renderer's React "Rendering" component. these are client-side only and never sent to the worker. includes displayModel and callbacks</span> | [BaseDisplay](../basedisplay#method-renderingprops) |
| <span id="method-iscachevalid">**isCacheValid**</span><br><code>(displayedRegionIndex: number) =&gt; boolean</code> | <span data-pagefind-ignore>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#method-iscachevalid) |
| <span id="method-resolvedbytelimit">**resolvedByteLimit**</span><br><code>() =&gt; number &#124; undefined</code> | <span data-pagefind-ignore>The budget the worker enforces and the banner compares against — the one spelling of that pair. Undefined when the gate may not act.</span> | [RegionTooLargeMixin](../regiontoolargemixin#method-resolvedbytelimit) |
| <span id="method-gatefetchstate">**gateFetchState**</span><br><code>() =&gt; GateFetchState</code> | <span data-pagefind-ignore>The gate as it stands for a fetch about to be issued. Calling it is the capture, which is why it is a method.</span> | [RegionTooLargeMixin](../regiontoolargemixin#method-gatefetchstate) |
| <span id="method-observedmaxdensity">**observedMaxDensity**</span><br><code>(bpPerPx: number) =&gt; number</code> | <span data-pagefind-ignore>Highest features-per-pixel across the visible regions at `bpPerPx`.</span> | [CanvasFeatureGateMixin](../canvasfeaturegatemixin#method-observedmaxdensity) |
| <span id="method-willcleartree">**willClearTree**</span><br><code>(next: S[]) =&gt; boolean</code> |  | [TreeSidebarMixin](../treesidebarmixin#method-willcleartree) |

## Actions

<!-- prettier-ignore -->
| Member | Description | Defined by |
| --- | --- | --- |
| <span id="action-setshowrowseparators">**setShowRowSeparators**</span><br><code>(f: boolean) =&gt; void</code> |  | LinearMultiRowFeatureDisplay |
| <span id="action-setcolorrowlabels">**setColorRowLabels**</span><br><code>(f: boolean) =&gt; void</code> |  | LinearMultiRowFeatureDisplay |
| <span id="action-togglecategory">**toggleCategory**</span><br><code>(label: string) =&gt; void</code> | Show/hide a legend category by label (render-time, no refetch). | LinearMultiRowFeatureDisplay |
| <span id="action-sethiddencategories">**setHiddenCategories**</span><br><code>(labels: string[]) =&gt; void</code> |  | LinearMultiRowFeatureDisplay |
| <span id="action-setpartitionfield">**setPartitionField**</span><br><code>(field: string) =&gt; void</code> | Repartition: which feature attribute assigns a feature to a row.<br><br>A fetch input, so writing the slot refetches on its own — nothing here has to ask for one. What it DOES have to do is drop the state keyed on the old rows: `layout` names rows by value, so under a new partition its entries name rows that no longer exist, and `getSources` appends a row a layout omits rather than dropping it — the old row set would have come back beside the new one, empty, with a saved color each. Same for the hidden categories, whose labels are the old values.<br><br>`clearLayout`, not `setLayout([])`, because the subtree filter is keyed on row names too. It is otherwise independent of the tree and of the order — `filterRowsBySubtree` matches on `name` and needs neither, so `setLayout` deliberately keeps a focused clade across a reorder — but this is the one action here that renames the rows out from under it. Left set, it matched nothing and `sources` came back empty: a blank canvas with no row labels, recoverable only by finding "Clear subtree filter" in the track menu. `setPhasedMode` is the same action on the multi-sample variant displays and clears it for the same reason. | LinearMultiRowFeatureDisplay |
| <span id="action-sortrowsbyvalueat">**sortRowsByValueAt**</span><br><code>(refName: string, pos: number) =&gt; void</code> | Reorder the rows by the value each carries at (refName, pos) — the feature covering that position on each row. Reads the already-loaded region data (no refetch/RPC) and writes the new order via `layout`.<br><br>Declines with fewer than two rows to order, because the empty result is not a harmless no-op: `setLayout` clears the cluster tree whenever the row set changes, so writing it discards both the saved arrangement and the dendrogram a clustering run produced. The rows are DISCOVERED from `rpcDataMap` (see `sourcesWithoutLayout`), which empties whenever the display is panned off its data or blanked by the density gate — so "sorting" a track that is merely not loaded right now used to wipe it. `sortRowsBy`, the declarative twin, meets the same condition by waiting for the region instead (see setupRowSortAutorun); a click has nothing to wait for, so it declines.<br><br>Declines again when no loaded region covers the column, which is the gate `setupRowSortAutorun` and multi-wiggle's twin already applied and this did not: it filtered the regions by refName alone, so a position past the end of the loaded window gave every row "no value" and wrote back the order it already had. `loadedRegionIndexAt` is the one predicate all three now ask, and resolving to a single region is also what stops two loaded windows on one contig from both answering. | LinearMultiRowFeatureDisplay |
| <span id="action-sethoveredfeature">**setHoveredFeature**</span><br><code>(arg?: MultiRowHit &#124; undefined) =&gt; void</code> | Writes only when the hovered block actually changes, so a mouse moving within one block (blocks are many px wide) doesn't invalidate the observers watching this. | LinearMultiRowFeatureDisplay |
| <span id="action-selectfeaturebyid">**selectFeatureById**</span><br><code>(featureId: string, displayedRegionIndex: number) =&gt; void</code> | Re-fetch the full clicked feature by id and open it in the feature details widget. The painting ships only the slim render arrays, so the complete feature is fetched on demand (GetCanvasFeatureDetails). | LinearMultiRowFeatureDisplay |
| <span id="action-setrpcdata">**setRpcData**</span><br><code>(regionIndex: number, data: MultiRowRegionData) =&gt; void</code> |  | LinearMultiRowFeatureDisplay |
| <span id="action-cleardisplayspecificdata">**clearDisplaySpecificData**</span><br><code>() =&gt; void</code> |  | LinearMultiRowFeatureDisplay |
| <span id="action-setheight">**setHeight**</span><br><code>(newHeight: number) =&gt; number</code> | Set the track height. In auto-fit mode the rows restretch to it; in fixed mode it's distributed across the current rows as a fixed row height. | LinearMultiRowFeatureDisplay |
| <span id="action-resizeheight">**resizeHeight**</span><br><code>(distance: number) =&gt; number</code> | Drag-resize. Defers to `setHeight`, which restretches rows in auto-fit mode and re-pins the row height in fixed mode. | LinearMultiRowFeatureDisplay |
| <span id="action-setfittoheight">**setFitToHeight**</span><br><code>() =&gt; void</code> | Switch to auto-fit: seed the `height` config slot from the current content height (so toggling on doesn't jump), then `rowHeight = 0` makes `effectiveRowHeight` derive from it. | LinearMultiRowFeatureDisplay |
| <span id="action-startrenderingbackend">**startRenderingBackend**</span><br><code>(backend: MultiRowRenderingBackend) =&gt; void</code> |  | LinearMultiRowFeatureDisplay |
| <span id="action-fetchneeded">**fetchNeeded**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(needed: { region: Region; displayedRegionIndex: number; }[]) =…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(needed: { region: Region; displayedRegionIndex: number; }[]) =&gt; Promise&lt;void&gt;</code></pre></dialog></span> |  | LinearMultiRowFeatureDisplay |
| <span id="action-rendersvg">**renderSvg**</span><br><code>(opts: ExportSvgDisplayOptions) =&gt; Promise&lt;ReactNode&gt;</code> |  | LinearMultiRowFeatureDisplay |
| <span id="action-clearhoveredfeature">**clearHoveredFeature**</span><br><code>() =&gt; void</code> | Fills `BaseDisplay`'s hover-clear hook, which the fetch foundation's reaction calls on every viewport change.<br><br>The painting is a sticky canvas, so a pan or zoom fires no mousemove and no mouseleave and `hoveredFeature` keeps naming whatever used to be under the cursor. | LinearMultiRowFeatureDisplay |
| <span id="action-setstatusmessage">**setStatusMessage**</span><br><code>(status?: RpcStatus &#124; undefined) =&gt; void</code> |  | [BaseDisplay](../basedisplay#action-setstatusmessage) |
| <span id="action-seterror">**setError**</span><br><code>(error?: unknown) =&gt; void</code> |  | [BaseDisplay](../basedisplay#action-seterror) |
| <span id="action-reload">**reload**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>base display reload does nothing, see specialized displays for details</span> | [BaseDisplay](../basedisplay#action-reload) |
| <span id="action-setscrolltop">**setScrollTop**</span><br><code>(scrollTop: number) =&gt; void</code> | <span data-pagefind-ignore>Clamped into `[0, scrollableHeight]`, so no caller has to remember the bound. Unbounded for a display that leaves `scrollableHeight` at its `Infinity` default.</span> | [TrackHeightMixin](../trackheightmixin#action-setscrolltop) |
| <span id="action-expandtocontentheight">**expandToContentHeight**</span><br><code>() =&gt; number</code> | <span data-pagefind-ignore>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.<br><br>`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).<br><br>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.</span> | [TrackHeightMixin](../trackheightmixin#action-expandtocontentheight) |
| <span id="action-setloadedregion">**setLoadedRegion**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(displayedRegionIndex: number, region: Region, fetchKey?: strin…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(displayedRegionIndex: number, region: Region, fetchKey?: string) =&gt; void</code></pre></dialog></span> | <span data-pagefind-ignore>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.<br><br>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#action-setloadedregion) |
| <span id="action-droploadedregion">**dropLoadedRegion**</span><br><code>(displayedRegionIndex: number) =&gt; void</code> | <span data-pagefind-ignore>Forget one region — for a display pruning what has scrolled off screen.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#action-droploadedregion) |
| <span id="action-clearallrpcdata">**clearAllRpcData**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#action-clearallrpcdata) |
| <span id="action-fetchregions">**fetchRegions**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(needed: IndexedRegion[], work: (ctx: RegionFetchContext) =&gt; Pr…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(needed: IndexedRegion[], work: (ctx: RegionFetchContext) =&gt; Promise&lt;void&gt;) =&gt; Promise&lt;void&gt;</code></pre></dialog></span> | <span data-pagefind-ignore>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.<br><br>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.</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#action-fetchregions) |
| <span id="action-afterattach">**afterAttach**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>installs the fetch-lifecycle autoruns (DisplayedRegionsChange, FetchVisibleRegions, SettingsInvalidate, ClearBlockingStateOnViewportChange)</span> | [MultiRegionDisplayMixin](../multiregiondisplaymixin#action-afterattach) |
| <span id="action-setbyteestimate">**setByteEstimate**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(measurement: { bytes: number; viewport: GateViewport; }) =&gt; vo…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(measurement: { bytes: number; viewport: GateViewport; }) =&gt; void</code></pre></dialog></span> | <span data-pagefind-ignore>The bytes half of a measurement alone, for a test staging a display. Production commits through `commitFetchBytes`.</span> | [RegionTooLargeMixin](../regiontoolargemixin#action-setbyteestimate) |
| <span id="action-clearbyteestimate">**clearByteEstimate**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>Drops the estimate and the viewport stamp. `forceLoadTrack` survives: it is a track-wide approval.</span> | [RegionTooLargeMixin](../regiontoolargemixin#action-clearbyteestimate) |
| <span id="action-setforceloadtrack">**setForceLoadTrack**</span><br><code>(flag: boolean) =&gt; void</code> |  | [RegionTooLargeMixin](../regiontoolargemixin#action-setforceloadtrack) |
| <span id="action-commitfetchbytes">**commitFetchBytes**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(perRegionBytes: (number &#124; undefined)[], issued: GateFetchState…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(perRegionBytes: (number &#124; undefined)[], issued: GateFetchState) =&gt; void</code></pre></dialog></span> | <span data-pagefind-ignore>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.</span> | [RegionTooLargeMixin](../regiontoolargemixin#action-commitfetchbytes) |
| <span id="action-forceload">**forceLoad**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>The banner's button: exempt the track on both axes and refetch.</span> | [RegionTooLargeMixin](../regiontoolargemixin#action-forceload) |
| <span id="action-markcanvasdrawn">**markCanvasDrawn**</span><br><code>() =&gt; void</code> |  | [RenderLifecycleMixin](../renderlifecyclemixin#action-markcanvasdrawn) |
| <span id="action-resetcanvasdrawn">**resetCanvasDrawn**</span><br><code>() =&gt; void</code> |  | [RenderLifecycleMixin](../renderlifecyclemixin#action-resetcanvasdrawn) |
| <span id="action-stoprenderingbackend">**stopRenderingBackend**</span><br><code>() =&gt; void</code> |  | [RenderLifecycleMixin](../renderlifecyclemixin#action-stoprenderingbackend) |
| <span id="action-rendernow">**renderNow**</span><br><code>() =&gt; void</code> |  | [RenderLifecycleMixin](../renderlifecyclemixin#action-rendernow) |
| <span id="action-setrendererror">**setRenderError**</span><br><code>(error: unknown) =&gt; void</code> | <span data-pagefind-ignore>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.</span> | [RenderLifecycleMixin](../renderlifecyclemixin#action-setrendererror) |
| <span id="action-attachrenderingbackend">**attachRenderingBackend**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>&lt;B&gt;(backend: B, setup: () =&gt; RenderingBackendCallbacks&lt;B&gt;) =&gt; v…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>&lt;B&gt;(backend: B, setup: () =&gt; RenderingBackendCallbacks&lt;B&gt;) =&gt; void</code></pre></dialog></span> | <span data-pagefind-ignore>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.</span> | [RenderLifecycleMixin](../renderlifecyclemixin#action-attachrenderingbackend) |
| <span id="action-stopactivefetch">**stopActiveFetch**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>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.</span> | [FetchMixin](../fetchmixin#action-stopactivefetch) |
| <span id="action-openstatusstream">**openStatusStream**</span><br><code>(isCurrent: () =&gt; boolean) =&gt; StatusStream</code> | <span data-pagefind-ignore>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.<br><br>**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.<br><br>`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.<br><br>Declared this early only so `runFetch` can put one on every `FetchContext`.</span> | [FetchMixin](../fetchmixin#action-openstatusstream) |
| <span id="action-cancelfetch">**cancelFetch**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>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.</span> | [FetchMixin](../fetchmixin#action-cancelfetch) |
| <span id="action-cancelfetchbyuser">**cancelFetchByUser**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>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.</span> | [FetchMixin](../fetchmixin#action-cancelfetchbyuser) |
| <span id="action-beforedestroy">**beforeDestroy**</span><br><code>() =&gt; void</code> | <span data-pagefind-ignore>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.</span> | [FetchMixin](../fetchmixin#action-beforedestroy) |
| <span id="action-endfetch">**endFetch**</span><br><code>(current: boolean, stopToken: StopToken) =&gt; void</code> | <span data-pagefind-ignore>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.</span> | [FetchMixin](../fetchmixin#action-endfetch) |
| <span id="action-runfetch">**runFetch**</span><br><code>(work: (ctx: FetchContext) =&gt; Promise&lt;void&gt;) =&gt; Promise&lt;void&gt;</code> | <span data-pagefind-ignore>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.<br><br>**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.</span> | [FetchMixin](../fetchmixin#action-runfetch) |
| <span id="action-setdensitystats">**setDensityStats**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(displayedRegionIndex: number, stats: RegionDensityStats) =&gt; vo…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(displayedRegionIndex: number, stats: RegionDensityStats) =&gt; void</code></pre></dialog></span> |  | [CanvasFeatureGateMixin](../canvasfeaturegatemixin#action-setdensitystats) |
| <span id="action-cleargatemeasurements">**clearGateMeasurements**</span><br><code>() =&gt; void</code> |  | [CanvasFeatureGateMixin](../canvasfeaturegatemixin#action-cleargatemeasurements) |
| <span id="action-commitgatemeasurements">**commitGateMeasurements**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(measurements: RegionGateMeasurement[], issued: GateFetchState)…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(measurements: RegionGateMeasurement[], issued: GateFetchState) =&gt; void</code></pre></dialog></span> | <span data-pagefind-ignore>Commit a batch of per-region fetch results on the density axis, judged by the tier captured at issue. The byte axis is `commitFetchBytes`.</span> | [CanvasFeatureGateMixin](../canvasfeaturegatemixin#action-commitgatemeasurements) |
| <span id="action-setshowlegend">**setShowLegend**</span><br><code>(arg: boolean) =&gt; void</code> |  | [LegendMixin](../legendmixin#action-setshowlegend) |
| <span id="action-setrowheight">**setRowHeight**</span><br><code>(n: number) =&gt; void</code> | <span data-pagefind-ignore>Pin a px row height. `0` is the fit sentinel, but enter fit mode through `setFitToHeight` instead — displays whose `height` getter is content-derived have to re-seed the slot on the way in, and that is what the action is for.</span> | [RowHeightMixin](../rowheightmixin#action-setrowheight) |
| <span id="action-setshowtree">**setShowTree**</span><br><code>(arg: boolean) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setshowtree) |
| <span id="action-setshowbranchlength">**setShowBranchLength**</span><br><code>(arg: boolean) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setshowbranchlength) |
| <span id="action-setshowrowlabels">**setShowRowLabels**</span><br><code>(arg: boolean) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setshowrowlabels) |
| <span id="action-setlayout">**setLayout**</span><br><code>(layout: S[]) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setlayout) |
| <span id="action-clearlayout">**clearLayout**</span><br><code>() =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-clearlayout) |
| <span id="action-setclustertree">**setClusterTree**</span><br><code>(tree?: string &#124; undefined) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setclustertree) |
| <span id="action-setlayoutandclustertree">**setLayoutAndClusterTree**</span><br><span class="cell-more"><button type="button" class="cell-more-trigger"><code>(layout: S[], tree?: string &#124; undefined, provenance?: ClusterPr…</code></button><dialog class="cell-dialog"><form method="dialog"><button class="cell-dialog-close" aria-label="Close">✕</button></form><pre><code>(layout: S[], tree?: string &#124; undefined, provenance?: ClusterProvenance &#124; undefined) =&gt; void</code></pre></dialog></span> |  | [TreeSidebarMixin](../treesidebarmixin#action-setlayoutandclustertree) |
| <span id="action-settreeareawidth">**setTreeAreaWidth**</span><br><code>(width: number) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-settreeareawidth) |
| <span id="action-setsubtreefilter">**setSubtreeFilter**</span><br><code>(names?: string[] &#124; undefined) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setsubtreefilter) |
| <span id="action-setrunclustering">**setRunClustering**</span><br><code>(arg?: boolean &#124; undefined) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setrunclustering) |
| <span id="action-setclusterregion">**setClusterRegion**</span><br><code>(arg?: string &#124; undefined) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setclusterregion) |
| <span id="action-setsortrowsby">**setSortRowsBy**</span><br><code>(arg?: RowSortSpec &#124; undefined) =&gt; void</code> | <span data-pagefind-ignore>Trigger (or clear) a one-shot declarative row sort; consumed and reset by `setupRowSortAutorun`. A display's right-click item calls its own sort directly (instant, the data is already loaded); this is the session-level entry point.</span> | [TreeSidebarMixin](../treesidebarmixin#action-setsortrowsby) |
| <span id="action-sethoveredtreenode">**setHoveredTreeNode**</span><br><code>(node?: HoveredTreeNode &#124; undefined) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-sethoveredtreenode) |
| <span id="action-settreecanvasref">**setTreeCanvasRef**</span><br><code>(ref: HTMLCanvasElement &#124; null) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-settreecanvasref) |
| <span id="action-setmouseovercanvasref">**setMouseoverCanvasRef**</span><br><code>(ref: HTMLCanvasElement &#124; null) =&gt; void</code> |  | [TreeSidebarMixin](../treesidebarmixin#action-setmouseovercanvasref) |
| <span id="action-opencontextmenu">**openContextMenu**</span><br><code>(info: Info) =&gt; void</code> |  | [ContextMenuMixin](../contextmenumixin#action-opencontextmenu) |
| <span id="action-closecontextmenu">**closeContextMenu**</span><br><code>() =&gt; void</code> |  | [ContextMenuMixin](../contextmenumixin#action-closecontextmenu) |

