jbrowse-plugin-graphgenomeviewer 4.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/LinearGraphDisplay-7POYIRIB.js +2 -0
- package/dist/chunks/{LinearGraphDisplay-P7U6NCZF.js.map → LinearGraphDisplay-7POYIRIB.js.map} +2 -2
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +2 -2
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +3 -3
- package/package.json +1 -1
- package/src/LinearGraphDisplay/LinearGraphDisplay.test.ts +42 -0
- package/src/LinearGraphDisplay/components/LinearGraphDisplay.tsx +4 -0
- package/src/LinearGraphDisplay/model.ts +228 -187
- package/src/version.ts +1 -1
- package/dist/chunks/LinearGraphDisplay-P7U6NCZF.js +0 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["global-externals:@jbrowse/core/Plugin", "global-externals:@jbrowse/core/pluggableElementTypes/AdapterType", "global-externals:@jbrowse/core/configuration", "global-externals:@jbrowse/core/data_adapters/dataAdapterCache", "global-externals:@jbrowse/core/pluggableElementTypes", "global-externals:@jbrowse/core/pluggableElementTypes/ViewType", "global-externals:@jbrowse/core/util/withLaunchInput", "global-externals:@jbrowse/core/pluggableElementTypes/models", "global-externals:@jbrowse/render-core/RenderLifecycleMixin", "global-externals:mobx", "global-externals:@jbrowse/core/pluggableElementTypes/DisplayType", "global-externals:@jbrowse/display-kit/trackHeightConfigSchemaFields", "global-externals:@jbrowse/display-kit/TrackHeightMixin", "../src/index.ts", "../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/BubbleChart.mjs", "../src/GbzBaseSyntenyAdapter/index.ts", "../src/GbzBaseSyntenyAdapter/configSchema.ts", "../src/GetSubgraph.ts", "../src/GraphAddTrackWorkflow/index.ts", "../src/GraphComputeLayout.ts", "../src/loadBandage.ts", "../src/GraphGenomeView/index.ts", "../src/GraphGenomeView/model.ts", "../src/GraphGenomeView/bubbles/bubbleHalos.ts", "../src/GraphGenomeView/bubbles/bubblesFromGraph.ts", "../src/GraphGenomeView/bubbles/popBubble.ts", "../src/GraphGenomeView/genes/geneFeatures.ts", "../src/GraphGenomeView/genes/genePins.ts", "../src/gfa-core/gfaParser.ts", "../src/GraphGenomeView/gfa/gfaConverter.ts", "../src/GraphGenomeView/host.ts", "../src/GraphGenomeView/laneRamp.ts", "../src/GraphGenomeView/layout/orientToReference.ts", "../src/GraphGenomeView/layout/referenceSeeds.ts", "../src/GraphGenomeView/repeats/repeatFeatures.ts", "../src/GraphGenomeView/walkHighlight.ts", "../src/hoverSync/lgvHover.ts", "../src/launchFromGraph/launchFromGraph.ts", "../src/launchFromGraph/linearViewTarget.ts", "../src/launchFromGraph/launchTracks.ts", "../src/launchFromGraph/syntenyTracks.ts", "../src/launchSubgraph/subgraphTracks.ts", "../src/LaunchGraphGenomeView.ts", "../src/LinearGraphDisplay/index.ts", "../src/LinearGraphDisplay/configSchema.ts", "../src/LinearGraphDisplay/model.ts", "../src/MinigraphBubbleAdapter/index.ts", "../src/MinigraphBubbleAdapter/configSchema.ts", "../src/RgfaTabixAdapter/index.ts", "../src/RgfaTabixAdapter/configSchema.ts", "../src/hoverSync/GraphNodeHighlight.tsx", "../src/hoverSync/graphViewHighlights.ts", "../src/hoverSync/index.tsx", "../src/pluginName.ts", "../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["module.exports = JBrowseExports[\"@jbrowse/core/Plugin\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/AdapterType\"];", "module.exports = JBrowseExports[\"@jbrowse/core/configuration\"];", "module.exports = JBrowseExports[\"@jbrowse/core/data_adapters/dataAdapterCache\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/ViewType\"];", "module.exports = JBrowseExports[\"@jbrowse/core/util/withLaunchInput\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/models\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/RenderLifecycleMixin\"];", "module.exports = JBrowseExports[\"mobx\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/DisplayType\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/trackHeightConfigSchemaFields\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/TrackHeightMixin\"];", "import Plugin from '@jbrowse/core/Plugin'\nimport { isAbstractMenuManager } from '@jbrowse/core/util'\nimport BubbleChartIcon from '@mui/icons-material/BubbleChart'\n\nimport GbzBaseSyntenyAdapterF from './GbzBaseSyntenyAdapter/index'\nimport GetSubgraph from './GetSubgraph'\nimport GraphAddTrackWorkflowF from './GraphAddTrackWorkflow/index'\nimport GraphComputeLayout from './GraphComputeLayout'\nimport GraphGenomeViewF from './GraphGenomeView/index'\nimport LaunchGraphGenomeViewF from './LaunchGraphGenomeView'\nimport LinearGraphDisplayF from './LinearGraphDisplay/index'\nimport MinigraphBubbleAdapterF from './MinigraphBubbleAdapter/index'\nimport RgfaTabixAdapterF from './RgfaTabixAdapter/index'\nimport GraphHoverSyncF from './hoverSync/index'\nimport { PLUGIN_NAME } from './pluginName'\nimport { version } from './version'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\n\nexport default class GraphGenomeViewPlugin extends Plugin {\n name = PLUGIN_NAME\n version = version\n\n install(pluginManager: PluginManager) {\n GraphGenomeViewF(pluginManager)\n LinearGraphDisplayF(pluginManager)\n LaunchGraphGenomeViewF(pluginManager)\n GraphHoverSyncF(pluginManager)\n RgfaTabixAdapterF(pluginManager)\n MinigraphBubbleAdapterF(pluginManager)\n GbzBaseSyntenyAdapterF(pluginManager)\n GraphAddTrackWorkflowF(pluginManager)\n pluginManager.addRpcMethod(() => new GraphComputeLayout(pluginManager))\n pluginManager.addRpcMethod(() => new GetSubgraph(pluginManager))\n }\n\n // A throw from configure() takes the whole session to its error page, and a\n // menu entry is not worth that: hubs 1.0.9 did exactly this to every\n // jbrowse.org/ucsc launch by appending to a menu a released host defines as\n // a function. The view is still reachable without the entry.\n configure(pluginManager: PluginManager) {\n try {\n if (isAbstractMenuManager(pluginManager.rootModel)) {\n pluginManager.rootModel.appendToSubMenu(['Add'], {\n label: 'Graph genome view',\n icon: BubbleChartIcon,\n onClick: (session: AbstractSessionModel) => {\n session.addView('GraphGenomeView', {})\n },\n })\n }\n } catch (e) {\n console.warn('[GraphGenomeView] could not add the Add menu entry', e)\n }\n }\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon([/*#__PURE__*/_jsx(\"circle\", {\n cx: \"7.2\",\n cy: \"14.4\",\n r: \"3.2\"\n}, \"0\"), /*#__PURE__*/_jsx(\"circle\", {\n cx: \"14.8\",\n cy: \"18\",\n r: \"2\"\n}, \"1\"), /*#__PURE__*/_jsx(\"circle\", {\n cx: \"15.2\",\n cy: \"8.8\",\n r: \"4.8\"\n}, \"2\")], 'BubbleChart');", "import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'\n\nimport configSchema from './configSchema.ts'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function GbzBaseSyntenyAdapterF(pluginManager: PluginManager) {\n pluginManager.addAdapterType(\n () =>\n new AdapterType({\n name: 'GbzBaseSyntenyAdapter',\n displayName: 'gbz-base pangenome adapter',\n configSchema,\n // The same opened database serves the graph view's subgraph, so the\n // launch menu offers this track too (launchSubgraph/subgraphTracks).\n // headerLanes: getHeader declares every haplotype as a lane, which is\n // what makes MultiWaySyntenyDisplay read the header of an untiered\n // adapter and offer its lane picker over the whole graph.\n // lanePairsOnAnchor: a window of the anchor answers any two lanes'\n // alignment to each other, so the display fetches each adjacent pair\n // rather than composing it through the reference\n adapterCapabilities: [\n 'getSubgraph',\n 'headerLanes',\n 'lanePairsOnAnchor',\n ],\n adapterMetadata: {\n category: 'Synteny adapters',\n },\n getAdapterClass: () =>\n import('./GbzBaseSyntenyAdapter.ts').then(r => r.default),\n }),\n )\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config GbzBaseSyntenyAdapter\n * #trackType SyntenyTrack\n * #fileFormat synteny | gbz-base pangenome database | Haplotype alignments read from the graph at query time, no PAF conversion\n * Serves a pangenome graph stored as a gbz-base SQLite database (`.gbz.db`) as\n * haplotype-versus-reference alignments, the shape `MultiWaySyntenyDisplay`\n * draws as one lane per haplotype. A window on the reference is located on the\n * graph's reference path, every haplotype's walk through the nodes it covers\n * is recovered and named, and each haplotype becomes one alignment record with\n * a CIGAR, so the graph is read through HTTP range requests with no offline\n * conversion. The database has to carry the `HaplotypeSamples` and\n * `HaplotypeLengths` side tables that `gbz-haplotype-index` (shipped with\n * `@gmod/gbz-base`) writes into an upstream-built database; without them the\n * graph cannot say which haplotype a walk belongs to.\n *\n * Only the fine tier exists: a whole-chromosome view stays on a PIF's coarse\n * tier. A query on a haplotype lane answers nothing, so the multi-way display\n * composes its lane-to-lane links through the reference.\n *\n * #example\n * ```js\n * {\n * type: 'GbzBaseSyntenyAdapter',\n * uri: 'hprc-v2.gbz.db',\n * assemblyNames: ['hg38'],\n * assemblyNameToPanSN: { hg38: 'GRCh38#0' },\n * }\n * ```\n */\nconst GbzBaseSyntenyAdapter = ConfigurationSchema(\n 'GbzBaseSyntenyAdapter',\n {\n /**\n * #slot\n * The first entry is the anchor: the JBrowse assembly the graph's reference\n * sample is loaded as. Further entries are haplotypes loaded as JBrowse\n * assemblies under their PanSN prefix (`HG002#1`); one loaded under another\n * name only needs its `assemblyNameToPanSN` entry. A haplotype named in\n * neither is still a lane, labelled by its PanSN prefix.\n */\n assemblyNames: {\n type: 'stringArray',\n defaultValue: [],\n },\n /**\n * #slot\n * The `.gbz.db` written by `gbz-base construct` and augmented by\n * `gbz-haplotype-index`; read by HTTP range requests, so it can be large.\n */\n gbzDbLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.gbz.db',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n * A companion database written by `gbz-haplotype-index --output`, holding\n * the `HaplotypeSamples` and `HaplotypeLengths` tables for a graph database\n * that does not carry them itself, such as the one HPRC publishes. One\n * built with anchor rows (the tool's default) lets a fetch for a chosen\n * set of lanes walk only those haplotypes. Empty means the graph database\n * carries the tables.\n */\n haplotypeIndexLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n * How the graph view's subgraph is extended past the nodes the reference\n * window touches. `contained` adds every top-level snarl with both\n * boundary nodes in the window, which is what brings back the bubbles a\n * reference-only walk leaves out; a haplotype still breaks into a new W\n * line wherever it leaves the window's nodes (a 60 kb C4 window on the\n * HPRC graph: 2,703 nodes, 8,083 walks for 464 haplotypes). `overlapping`\n * also follows snarls that leave the window, so every haplotype is one\n * walk (464), at the price of every node in those snarls (4,184 there,\n * 24,547 across the KIV-2 repeat). `none` leaves only the bp `context`.\n */\n subgraphSnarls: {\n type: 'stringEnum',\n model: types.enumeration('SnarlOutput', [\n 'none',\n 'contained',\n 'overlapping',\n ]),\n defaultValue: 'contained',\n },\n /**\n * #slot\n * The graph sample the anchor window is located on. Empty means the sample\n * the anchor's PanSN prefix names, or the database's one reference sample\n * (`gbwt_reference_samples`) when the anchor names none; a database with\n * several reference samples then needs this set.\n */\n referenceSample: {\n type: 'string',\n defaultValue: '',\n },\n /**\n * #slot\n * Maps a JBrowse assembly name to its PanSN prefix in the graph, sample or\n * haplotype level (`{ hg38: 'GRCh38#0', 'HG002.1': 'HG002#1' }`). Defaults\n * to identity.\n */\n assemblyNameToPanSN: {\n type: 'frozen',\n defaultValue: {},\n },\n /**\n * #slot\n * Graph context around the reference walk, in bp, on top of\n * `subgraphSnarls`. Since `@gmod/gbz-base` joins the pieces of a walk that\n * leaves the window's nodes and comes back (the private stretch becomes the\n * record's insertion, the skipped reference its deletion), a lane's record\n * count is one per haplotype at any context; what context trades is nodes\n * read against pieces to identify and join. A window inside a snarl far\n * larger than itself (MHC class II on the HPRC graph, 90 kb) is 1.1M pieces\n * at 0 and 464 walks at 1000, three times faster; a window whose private\n * stretches are short bubbles costs about the same either way. For the\n * graph view's cut it is simply how far past the window the cut extends.\n */\n context: {\n type: 'number',\n defaultValue: 1000,\n advanced: true,\n },\n /**\n * #slot\n * The most graph nodes one window may load before the query fails rather\n * than reading a whole chromosome into the worker.\n */\n nodeLimit: {\n type: 'number',\n defaultValue: 100000,\n advanced: true,\n },\n },\n {\n explicitlyTyped: true,\n\n /**\n * #preProcessSnapshot\n *\n *\n * preprocessor to allow minimal config:\n * ```json\n * {\n * \"type\": \"GbzBaseSyntenyAdapter\",\n * \"uri\": \"graph.gbz.db\",\n * \"assemblyNames\": [\"hg38\"]\n * }\n * ```\n */\n preProcessSnapshot: snap => {\n return snap.uri\n ? {\n ...snap,\n gbzDbLocation: {\n uri: snap.uri,\n baseUri: snap.baseUri,\n },\n }\n : snap\n },\n },\n)\n\nexport type GbzBaseSyntenyAdapterConfig = Instance<typeof GbzBaseSyntenyAdapter>\n\nexport default GbzBaseSyntenyAdapter\n", "import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'\nimport { RpcMethodTypeWithRenameRegion } from '@jbrowse/core/pluggableElementTypes'\n\nimport type { RpcExecuteArgs } from '@jbrowse/core/rpc/RpcRegistry'\nimport type { Region } from '@jbrowse/core/util'\n\n// No `sessionId`, `signal` or `statusCallback` here: those belong to the\n// CALL, not to this payload, and core's `EntriesDeclaringCallLevelFields` fails\n// the build for a registry entry that declares one. `execute` still receives\n// them, because `RpcExecuteArgs` intersects `RpcCallContext` in.\n// `hops` is how far past the window a cut follows links, which only the rGFA\n// cut reads; the GBZ cut has its own bp `context` slot. `haplotypes` is the\n// set the cut is for, lane assembly names or PanSN prefixes, which only the GBZ\n// cut reads: it keeps those walks and the nodes they visit. Undefined or empty\n// is every haplotype. `tier` picks which of an rGFA track's two index pairs the\n// cut reads, the adapter's own or its `coarse` one; fine when absent.\nexport type SubgraphTier = 'fine' | 'coarse'\n\nexport interface SubgraphCutOptions {\n hops?: number\n haplotypes?: string[]\n tier?: SubgraphTier\n}\n\n// What an adapter's cut is handed: the payload, plus the call's own signal so\n// a cut the view has moved on from stops reading.\nexport interface SubgraphAdapterOptions extends SubgraphCutOptions {\n signal?: AbortSignal\n}\n\nexport interface GetSubgraphArgs {\n adapterConfig: Record<string, unknown>\n region: Region\n opts?: SubgraphCutOptions\n}\n\ndeclare module '@jbrowse/core/rpc/RpcRegistry' {\n interface RpcRegistry {\n GetSubgraph: {\n args: GetSubgraphArgs\n return: string\n }\n }\n}\n\n// Adapters that can cut a local subgraph out of a graph file implement this:\n// RgfaTabixAdapter and GbzBaseSyntenyAdapter today.\ninterface SubgraphAdapter {\n getSubgraph(region: Region, opts?: SubgraphAdapterOptions): Promise<string>\n}\n\nfunction isSubgraphAdapter(adapter: object): adapter is SubgraphAdapter {\n return (\n 'getSubgraph' in adapter &&\n typeof (adapter as SubgraphAdapter).getSubgraph === 'function'\n )\n}\n\n// RpcMethodTypeWithRenameRegion, not the plain RpcMethodType: the base class is\n// what maps `region.refName` from the assembly's spelling onto the adapter's own\n// before the call crosses to the worker. Without it a launch on an hg38 whose\n// contigs are bare (`6`, which is what every GRCh38 FASTA on jbrowse.org uses)\n// asked RgfaTabixAdapter for `GRCh38#0#6`, matched nothing, and opened a view\n// reading \"0 nodes, 0 edges\" with no error. The segments track drew the whole\n// time, because CoreGetFeatures renames and this did not, so the graph looked\n// broken while its own track looked fine. `assemblyNameToPanSN` covers the\n// sample half of a PanSN name only; the contig half is refName aliasing, which\n// the assembly already knows and this now consults.\nexport default class GetSubgraph extends RpcMethodTypeWithRenameRegion<'GetSubgraph'> {\n name = 'GetSubgraph' as const\n\n // Parameterized by the registry key, not left at the default `string`: a bare\n // RpcMethodTypeWithRenameRegion resolves RpcExecuteArgs to `unknown`, so\n // `execute` type-checks against nothing at all. `invoke` has already run\n // `deserializeArguments` by the time this is called, so it does not.\n async execute(args: RpcExecuteArgs<'GetSubgraph'>) {\n // `signal` arrives on every call and has to be named to be forwarded:\n // unnamed, a cut the view had replaced went on querying to the end, one\n // tabix read per off-reference segment per hop.\n const { adapterConfig, region, sessionId, opts, signal } = args\n\n const { dataAdapter } = await getAdapter(\n this.pluginManager,\n sessionId,\n adapterConfig,\n )\n if (isSubgraphAdapter(dataAdapter)) {\n return dataAdapter.getSubgraph(region, { ...opts, signal })\n }\n // An empty result is how the view and the launch menu detect \"this track\n // can't do subgraphs\" \u2014 see GraphGenomeView.loadFromTabixSubgraph.\n console.warn(\n `[GetSubgraph] ${dataAdapter.constructor.name} does not implement getSubgraph`,\n )\n return ''\n }\n}\n", "import { lazy } from 'react'\n\nimport { AddTrackWorkflowType } from '@jbrowse/core/pluggableElementTypes'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nconst GraphAddTrackWidget = lazy(() => import('./AddTrackWorkflow'))\n\nexport default function GraphAddTrackWorkflowF(pluginManager: PluginManager) {\n pluginManager.addAddTrackWorkflowType(\n () =>\n new AddTrackWorkflowType({\n name: 'Pangenome graph track',\n displayName: 'Add pangenome graph track',\n ReactComponent: GraphAddTrackWidget,\n stateModel: types.model({}),\n }),\n )\n}\n", "import { RpcMethodType } from '@jbrowse/core/pluggableElementTypes'\n\nimport loadBandage from './loadBandage'\n\nimport type { LayoutNode } from './GraphGenomeView/layout/referenceSeeds'\nimport type { Graph, LayoutResult } from './GraphGenomeView/types'\nimport type { RpcExecuteArgs } from '@jbrowse/core/rpc/RpcRegistry'\n\n// `sessionId` and `statusCallback` are the CALL's, not this payload's \u2014 core's\n// `EntriesDeclaringCallLevelFields` fails the build for a registry entry that\n// declares either, on the grounds that a field one method owns is a field the\n// other forty cannot be passed. `execute` still receives both, through\n// `RpcExecuteArgs`'s intersection with `RpcCallContext`.\nexport interface GraphComputeLayoutArgs {\n // a node may carry an `x`/`y` seed, which the engine reads as where its\n // chain starts (referenceSeeds.ts)\n graph: { nodes: LayoutNode[]; edges: Graph['edges'] }\n options: Record<string, unknown>\n}\n\ndeclare module '@jbrowse/core/rpc/RpcRegistry' {\n interface RpcRegistry {\n GraphComputeLayout: {\n args: GraphComputeLayoutArgs\n return: { result: LayoutResult; duration: number }\n }\n }\n}\n\nexport default class GraphComputeLayout extends RpcMethodType<'GraphComputeLayout'> {\n name = 'GraphComputeLayout' as const\n\n async execute(args: RpcExecuteArgs<'GraphComputeLayout'>) {\n const { graph, options, statusCallback } = args\n\n statusCallback?.('Loading layout engine')\n const module = await loadBandage()\n\n statusCallback?.('Computing layout')\n const startTime = performance.now()\n const result = module.computeLayout(graph, options)\n const duration = performance.now() - startTime\n\n return { result, duration }\n }\n}\n", "export type { BandageModule } from './bandage/bandage-layout'\n\nimport type { BandageModule } from './bandage/bandage-layout'\n\n// The Bandage engine is a ~425kb self-contained ES module (Emscripten\n// -sSINGLE_FILE=1, wasm inlined as base64). A plain dynamic import lets the\n// bundler emit it as a sibling chunk that the browser resolves relative to this\n// module's own url via import.meta.url \u2014 so it downloads only when a\n// force-directed layout is requested, and works identically on the main thread\n// and in the RPC worker with no url plumbing.\nlet cached: Promise<BandageModule> | undefined\n\nexport default function loadBandage() {\n cached ??= import('./bandage/bandage-layout')\n .then(mod => mod.default())\n .catch((e: unknown) => {\n // let a later attempt retry rather than caching the failure forever\n cached = undefined\n throw e\n })\n return cached\n}\n", "import { lazy } from 'react'\n\nimport ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'\nimport { defineLaunchKeys } from '@jbrowse/core/util/withLaunchInput'\n\nimport modelFactory from './model'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\n// This view has NO launch keys, and saying so is what turns the check on.\n//\n// A launch key is a spec field the launcher interprets rather than assigns \u2014 an\n// LGV's `assembly` and `loc`, resolved into `displayedRegions`. Every field of\n// a graph launch is a plain persisted prop instead (see\n// LaunchGraphGenomeView), so the registration is empty; what it buys is\n// `ViewType.acceptedKeys`, which is `undefined` until a view declares its\n// launch vocabulary, and which `loadSessionSpec` needs to report\n// `ignored unknown key(s)`.\n//\n// Without it a session spec's typo in a GraphGenomeView entry is silent: the\n// key lands in the snapshot, MST drops it, and the view opens on the import\n// form with nothing said. That surface is written by hand with no compiler\n// behind it \u2014 `sessionSpec()` takes a bare `object` \u2014 and the docs build two\n// dozen graph figures through it, `loadedRegion` and `loadedTrackId` among the\n// fields a misspelling would silently cost.\n//\n// Ignored by a host that predates `acceptedKeys`: its ViewType constructor\n// reads the options it knows and this is not one of them.\nconst graphLaunchKeys = defineLaunchKeys<object>()({})\n\nexport default function GraphGenomeViewF(pluginManager: PluginManager) {\n pluginManager.addViewType(() => {\n return new ViewType({\n name: 'GraphGenomeView',\n displayName: 'Graph genome view',\n stateModel: modelFactory(),\n launchKeys: graphLaunchKeys,\n ReactComponent: lazy(() => import('./components/GraphGenomeView')),\n })\n })\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'\nimport { pushLaunchViewMenuItem } from '@jbrowse/core/ui'\nimport {\n getContainingView,\n getSession,\n isSessionModelWithWidgets,\n statusMessageText,\n} from '@jbrowse/core/util'\nimport { openLocation } from '@jbrowse/core/util/io'\nimport { addDisposer, flow, isAlive, types } from '@jbrowse/mobx-state-tree'\nimport { RenderLifecycleMixin } from '@jbrowse/render-core/RenderLifecycleMixin'\nimport { getDpr } from '@jbrowse/render-core/canvas2dUtils'\nimport { autorun, reaction, untracked } from 'mobx'\n\nimport { backboneNodes, backboneSpan, isBackbone } from './anchoredNodes'\nimport { BUBBLE_SPREAD_VALUES, spreadFor } from './bubbleSpreads'\nimport { bubbleHalos } from './bubbles/bubbleHalos'\nimport { bubblesFromGraph } from './bubbles/bubblesFromGraph'\nimport {\n BUBBLE_KIND_NAMES,\n bubbleSegmentIds,\n classifyBubble,\n} from './bubbles/classifyBubble'\nimport { bubbleSubgraph } from './bubbles/popBubble'\nimport { COLOR_SCHEME_VALUES } from './colorSchemes'\nimport { deletionEdges } from './deletionEdges'\nimport {\n GENE_ADAPTER_TYPES,\n geneModelsFrom,\n pickGeneTrack,\n} from './genes/geneFeatures'\nimport { genePins } from './genes/genePins'\nimport { convertGFAToGraph } from './gfa/gfaConverter'\nimport { cutHolds, hostCut, hostFrame, hostWindow, isLinearHost } from './host'\nimport {\n paintSourceLane,\n referencePositionColor,\n sourceLaneDisplay,\n} from './laneRamp'\nimport { drawnNodeLength, layoutScaling } from './layout/drawnScale'\nimport { mergeRuns, splitRuns } from './layout/mergeRuns'\nimport { orientToReference } from './layout/orientToReference'\nimport { seededNodes } from './layout/referenceSeeds'\nimport { ROW_HEIGHT_PX } from './layout/rowSpacing'\nimport { walkRowsExtent } from './layout/walkRowLayout'\nimport { walkRows } from './layout/walkRows'\nimport {\n LAYOUT_MODE_VALUES,\n layoutModeByValue,\n modeUsesLayoutEngine,\n} from './layoutModes'\nimport {\n NODE_WIDTH_VALUES,\n maxNodeWidthPx,\n meanDepth,\n nodeWidthPx,\n} from './nodeWidths'\nimport { anchorFromPaths, anchorGraph } from './pathAnchoring'\nimport { pathColorsLegible, pathLegend } from './pathColors'\nimport { buildNeighbors, nodeReferenceSpan } from './referenceSpan'\nimport { buildGeometry, computeReferenceRamp } from './renderer/GeometryBuilder'\nimport {\n REPEAT_ADAPTER_TYPES,\n pickRepeatTrack,\n repeatArraysFrom,\n} from './repeats/repeatFeatures'\nimport { withCalls } from './repeats/walkCalls'\nimport { walkHighlight } from './walkHighlight'\nimport { parseGFA } from '../gfa-core/index'\nimport {\n hoverInRegion,\n nodeForLgvHover,\n readLgvHover,\n} from '../hoverSync/lgvHover'\nimport {\n contributingAssemblies,\n locLabel,\n nodeOwnLocation,\n resolveContributors,\n resolveLocationAssembly,\n} from '../launchFromGraph/contributors'\nimport { graphLaunchMenuItems } from '../launchFromGraph/graphMenuItems'\nimport {\n highlightInLinearView,\n launchSyntenyView,\n paddedLocation,\n showInLinearView,\n withReferenceRegion,\n} from '../launchFromGraph/launchFromGraph'\nimport { launchTracks } from '../launchFromGraph/launchTracks'\nimport { linearViewTarget, withRows } from '../launchFromGraph/linearViewTarget'\nimport { launchableSyntenyTracks } from '../launchFromGraph/syntenyTracks'\nimport {\n graphReferenceAssembly,\n offReferenceProblem,\n} from '../launchSubgraph/subgraphTracks'\n\nimport type { BubbleSpread } from './bubbleSpreads'\nimport type { ColorScheme, ResolvedColorScheme } from './colorSchemes'\nimport type { GeneModel } from './genes/geneFeatures'\nimport type { HostWindow, LinearHost } from './host'\nimport type { LayoutScaling } from './layout/drawnScale'\nimport type { LayoutModeValue } from './layoutModes'\nimport type { NodeWidth } from './nodeWidths'\nimport type { Renderer } from './renderer/types'\nimport type { RepeatArray } from './repeats/repeatFeatures'\nimport type { Graph, GraphNode, LayoutResult } from './types'\nimport type { SubgraphCutOptions, SubgraphTier } from '../GetSubgraph'\nimport type { NodeInk } from './util/hitDetection'\nimport type { MinigraphBubble } from '../MinigraphBubbleAdapter/bubbleLine'\nimport type { AxisScale } from './util/geometry'\nimport type { GraphLocation } from '../launchFromGraph/contributors'\nimport type { SubgraphRegion } from '../launchSubgraph/launchSubgraphView'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Feature } from '@jbrowse/core/util'\nimport type { FileLocation } from '@jbrowse/core/util/types'\n\n// Ceiling on the pane, and what it falls back to before there is a layout to\n// size against. A roughly square drawing (FMMM) hits this and keeps the\n// scrollable pane it has always had.\nconst MAX_CANVAS_HEIGHT = 600\n// Floor, so a window holding only backbone \u2014 one row, no height at all \u2014 still\n// leaves room to hover a node and read its tooltip.\nconst MIN_CANVAS_HEIGHT = 160\nconst VARIANT_MAP_HEIGHT = 340\n\nconst SEGMENTS_SUFFIX = '.segs.bed.gz'\n\n// The rGFA index prefix an adapter config names, from either spelling the\n// config schema accepts: the `uri` shorthand or an explicit segments location.\nfunction bubblePrefix(adapterConfig: Record<string, unknown>) {\n if (typeof adapterConfig.uri === 'string') {\n return adapterConfig.uri\n }\n const segments = adapterConfig.segmentsLocation as\n { uri?: string } | undefined\n const uri = segments?.uri\n return typeof uri === 'string' && uri.endsWith(SEGMENTS_SUFFIX)\n ? uri.slice(0, -SEGMENTS_SUFFIX.length)\n : undefined\n}\n// Gap between the drawing and the edge of the pane, on all four sides.\nconst FIT_PADDING = 40\nconst HOVER_BRIGHTEN = 1.4\nconst SELECT_BRIGHTEN = 1.6\nconst VIEWPORT_DEBOUNCE_MS = 150\nconst VIEWPORT_PANES_BUILT = 1\n\n// MobX tracks every observable read while a computed or autorun runs, so\n// passing values here registers them as dependencies without otherwise using\n// them.\nfunction dependOn(..._values: unknown[]) {}\n\n// Hard size cap for the single-mode graph view. Past it the view declines with a\n// \"zoom in\" message rather than switching to a degraded rendering mode \u2014 one mode\n// only, since the large-region case is a linear synteny view, not a graph.\n// (The `adr-027` citation this used to carry was stale: that number was reused\n// for wheel-input semantics after the large-mode ADR was removed in 9d8102f0b5.)\n//\n// This bounds the *fetch*, and it is the only cap that can be applied before one\n// happens. It is a poor proxy for cost, because cost tracks node count, so the\n// number is chosen against the density of the graphs it can actually guard: a\n// region is fetched through `getSubgraph`, and the rGFA cut is what this number\n// was chosen against (the GBZ cut is base-level and has its own `nodeLimit`,\n// which trips long before 5 Mb). Both measured rGFAs are sparse, and at 5 Mb\n// both land at or under the ~2k nodes that redraw in under 10 ms (see agent-docs/GRAPH_SCALE_AND_LOD.md):\n//\n// HPRC MC GRCh38 ~7,000 bp/segment whole 4.9 Mb MHC 1,173 nodes\n// ecoli minigraph 3,078 bp/segment whole 4.6 Mb genome 2,415 nodes\n//\n// Fetch cost does not scale with the window either: against the hosted HPRC\n// index a 4.9 Mb links query and a 100 kb one both cost ~1.3 s, being dominated\n// by HTTP setup. FMMM is the slowest consumer at this size and stays tolerable,\n// ~0.1-3 s for ~1k nodes depending on layout quality (bandage-layout-js), and it\n// runs off the main thread.\n//\n// A dense graph is what this cannot protect against, because bp per node varies\n// by orders of magnitude between graph types \u2014 a base-level pggb graph runs ~17\n// bp/node, where 5 Mb would be hundreds of thousands. Such a graph has no region\n// query to reach this check with today, and the node budget below is the cap that\n// catches it if one ever does.\nexport const MAX_GRAPH_REGION_BP = 5_000_000\n\n// The cap in a message, in the unit that reads: `5 Mb`, not `5000 kb`.\nexport function formatSpanBp(bp: number) {\n return bp >= 1_000_000\n ? `${+(bp / 1_000_000).toFixed(1)} Mb`\n : `${Math.round(bp / 1000)} kb`\n}\n\n// What the view will actually draw, checked once the graph is parsed and so\n// applying to a whole-file import as much as to a subgraph fetch. Measured on\n// bubble-chain graphs: ~1-2k nodes redraws in under 10 ms, 10k takes ~43 ms of\n// geometry and ~125k canvas draw calls per frame (single-digit fps while\n// panning), and 100k needs 632 ms and 75 MB of vertex buffers. So this is set\n// where the tab stops being usable rather than where it stops being smooth, and\n// it is a view prop rather than a constant so a session can raise it \u2014 the same\n// escape hatch strangepg gives with `-T N`.\nexport const DEFAULT_MAX_GRAPH_NODES = 20_000\n\n// The floor exists to keep a scale positive and finite, not to express a useful\n// zoom level, so it has to clear the smallest scale a real layout asks for. In\n// the reference-anchored layouts world units are bp: fitting a chromosome-scale\n// rGFA (250 Mbp) into ~720 px needs ~3e-6, and the old 0.001 floor clamped that\n// to 7x too wide \u2014 zoomToFit could not fit a whole-file import at all.\nconst MIN_ZOOM = 1e-6\nconst MAX_ZOOM = 100\n\nfunction clampZoom(zoom: number) {\n return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom))\n}\n\n// The zoom as a pair of axis scales. One expression, read by all three getters\n// below: they sit in one `.views()` block and so cannot reach each other through\n// `self`, which had each of them restating the `pixelRows ? 1 : scale` rule that\n// AxisScale exists to keep in one place.\nfunction axisScaleOf(scale: number, pixelRows: boolean): AxisScale {\n // pixelRows rides along rather than being derived downstream: the deletion\n // bow is capped in a row layout and not in the isotropic one, and `scaleY !==\n // scaleX` coincides at one zoom level. See AxisScale.\n return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows }\n}\n\ntype ViewportOwner = 'fit' | 'user' | 'host'\n\ninterface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\n// What the pane shows, in layout units.\nfunction viewportOf(model: {\n translateX: number\n translateY: number\n width: number\n scaleX: number\n scaleY: number\n canvasHeight: number\n}): Bounds {\n return {\n minX: -model.translateX / model.scaleX,\n minY: -model.translateY / model.scaleY,\n maxX: (model.width - model.translateX) / model.scaleX,\n maxY: (model.canvasHeight - model.translateY) / model.scaleY,\n }\n}\n\n// The window a geometry build covers: the pane plus a whole pane on every\n// side, so a pan has that far to go before the drawing runs out and a rebuild\n// is due. Drawing three panes' worth is cheap now that a redraw is a few\n// batched strokes; what it buys is that an ordinary pan never blanks its\n// margins for the debounce and never rebuilds at all.\nfunction padded(v: Bounds, panes: number): Bounds {\n const w = (v.maxX - v.minX) * panes\n const h = (v.maxY - v.minY) * panes\n return {\n minX: v.minX - w,\n minY: v.minY - h,\n maxX: v.maxX + w,\n maxY: v.maxY + h,\n }\n}\n\n// Centre the leftover, but only when there IS leftover. A row layout fits on x\n// alone, so its drawing is routinely taller than the pane \u2014 that is the case\n// rowSpacing.ts means by \"reached by panning\" \u2014 and centring an overflow splits\n// the loss across both ends: at 41 rows the top row landed 100 px above the\n// pane. The top row is the reference backbone, i.e. the axis the whole layout\n// exists to put under the linear view, so it is the one row that must not be\n// the first thing to go. Pinned at the padding it opens where a fitting drawing\n// opens, and the rows past the ceiling are below, which is the direction a\n// reader already scrolls a track in.\nfunction fittedTranslateY(\n bounds: { minY: number; h: number },\n usableHeight: number,\n yScale: number,\n) {\n const leftoverY = usableHeight - bounds.h * yScale\n return FIT_PADDING - bounds.minY * yScale + Math.max(0, leftoverY) / 2\n}\n\nfunction contains(outer: Bounds, inner: Bounds) {\n return (\n inner.minX >= outer.minX &&\n inner.maxX <= outer.maxX &&\n inner.minY >= outer.minY &&\n inner.maxY <= outer.maxY\n )\n}\n\n// Force layouts already computed for a graph, keyed by the view props the\n// engine reads. Every layout mode is local and costs a millisecond except this\n// one, which is seconds: measured on the committed engine, a 1,201-node bubble\n// chain is 0.6 s proportional, 4.3 s at 'Wide bubbles' and 3.4 s at the highest\n// quality.\n//\n// Worth keeping because a session revisits the same layout. The two drawings\n// answer different questions and the README's own figure is one subgraph in\n// both, so Anchored <-> Force is a round trip a reader makes repeatedly \u2014 and\n// every control in the settings dialog calls `recomputeLayout` on change\n// whether or not the current mode reads what it changed, which made picking a\n// reference path in force mode a multi-second re-run of an identical layout.\n// Both are hits.\n//\n// Entries are the live position objects rather than copies, which a node drag\n// mutates in place (see moveNode), so a layout comes back arranged the way it\n// was left rather than snapping back.\n//\n// Weak on the graph so nothing has to invalidate it: a graph is rebuilt by\n// every load and dropped by `clearGraph`, and its layouts go with it.\nconst forceLayouts = new WeakMap<Graph, Map<string, LayoutResult>>()\n\n// Enough to hold the settings a comparison moves between; past that the oldest\n// goes, since a big graph's positions are megabytes.\nconst FORCE_LAYOUT_CACHE_SIZE = 4\n\nfunction forceLayoutsOf(graph: Graph) {\n let cache = forceLayouts.get(graph)\n if (!cache) {\n cache = new Map()\n forceLayouts.set(graph, cache)\n }\n return cache\n}\n\n// Re-anchoring builds a new Graph, and the force layout of it is the same\n// drawing: `anchorFromPaths` rewrites each node's `stable` and `samples` and\n// touches neither the ids, the lengths, the depths nor the edges, which are all\n// the engine reads. So the layouts follow the graph they were computed for\n// rather than being thrown away with it.\nfunction inheritForceLayouts(from: Graph, to: Graph) {\n const cache = forceLayouts.get(from)\n if (cache && from !== to) {\n forceLayouts.set(to, cache)\n }\n}\n\nfunction remember(\n cache: Map<string, LayoutResult>,\n key: string,\n result: LayoutResult,\n) {\n if (cache.size >= FORCE_LAYOUT_CACHE_SIZE) {\n cache.delete(cache.keys().next().value!)\n }\n cache.set(key, result)\n}\n\nexport default function stateModelFactory() {\n return types\n .compose(\n 'GraphGenomeView',\n BaseViewModel,\n RenderLifecycleMixin(),\n types.model({\n type: types.literal('GraphGenomeView'),\n // FMMM's iteration budget, 0-4, the same scale Bandage's own settings\n // dialog exposes: 3+1 iterations at 0, 15+10 at 1, 30+20 at 2, 60+40 at\n // 3, 120+60 at 4 (BandageNG layout/graphlayoutworker.cpp).\n //\n // 2 because that is what the engine we vendor ships as its default\n // (`graphLayoutQuality = IntSetting(2, 0, 4)`, program/settings.cpp).\n // This was 1 for no recorded reason, i.e. one step BELOW upstream, and a\n // first sight of any graph is the default: review kept asking whether\n // the drawing could be iterated more (\"are you sure you can't iterate it\n // more times for better layout?\") of figures that were simply on a lower\n // setting than the tool they come from. Cost at the sizes this view\n // draws is milliseconds, and the header states the layout time so the\n // difference is visible rather than asserted.\n layoutQuality: types.optional(types.number, 2),\n linearLayout: types.optional(types.boolean, false),\n // Which drawing to use; the modes and their fallbacks are described in\n // LAYOUT_MODES. Default 'force' is the Bandage FMMM drawing, i.e. what\n // someone who has seen a pangenome graph before expects to see.\n //\n // The default used to be 'auto', the reference-anchored layout, on the\n // argument that lining the graph's x axis up with a linear view above it\n // is what this view can do and Bandage cannot. Docs review disagreed\n // twice, on figure after figure \u2014 \"the linear backbone is just\n // confusing\", \"default to showing the bandage graphs over linear\n // backbone in almost all cases\" \u2014 and the reason is that the anchored\n // drawing looks like a track rather than a graph: a bubble collapses onto\n // the reference axis, so the alternative alleles read as short bars\n // hanging under a line rather than as two routes through the same locus.\n // The anchored modes stay one dropdown click away, and are what the\n // hover-sync and one-row-per-strain figures select deliberately.\n layoutMode: types.optional(\n types.enumeration(LAYOUT_MODE_VALUES),\n 'force',\n ),\n // 'auto' rather than a colour, resolved by `effectiveColorScheme`. See\n // COLOR_SCHEMES for why the default is not 'uniform' any more. A session\n // that names a scheme keeps it \u2014 this only decides what an unstated one\n // opens as.\n colorScheme: types.optional(\n types.enumeration(COLOR_SCHEME_VALUES),\n 'auto',\n ),\n // How far the force layout opens a bubble, which on a variation graph is\n // the difference between a legible drawing and a rope. See\n // BUBBLE_SPREADS; no effect on the reference-anchored layouts, which\n // place a node from its coordinates rather than from a force sim.\n bubbleSpread: types.optional(\n types.enumeration(BUBBLE_SPREAD_VALUES),\n 'auto',\n ),\n // Node thickness by depth, Bandage's own device; see NODE_WIDTHS.\n nodeWidth: types.optional(\n types.enumeration(NODE_WIDTH_VALUES),\n 'depth',\n ),\n // Whether the node layouts draw each bubble as a halo along its nodes\n // with a label that opens it. The variant map draws glyphs instead.\n showBubbles: types.optional(types.boolean, true),\n showDeletionEdges: types.optional(types.boolean, false),\n // The session's genes drawn onto the backbone: exons along the nodes\n // that carry them, names pinned at their midpoints. See genes/.\n showGenes: types.optional(types.boolean, true),\n // Which track the genes come from; empty picks the assembly's\n // annotation track (pickGeneTrack).\n geneTrackId: types.optional(types.string, ''),\n // Which track the walk rows read tandem repeat arrays from; empty\n // picks a track whose name says repeats (pickRepeatTrack).\n repeatTrackId: types.optional(types.string, ''),\n // The array the walk rows measure between and tile by, as\n // RepeatArray.key; empty measures the whole window untiled.\n repeatKey: types.optional(types.string, ''),\n // Samples whose walks the walk rows show, by the name before the\n // haplotype number; undefined shows every walk the cut holds.\n walkRowSamples: types.maybe(types.frozen<string[]>()),\n // One walk, by its path name, lifted out of the drawing: its nodes and\n // links keep their ink and the rest fades. Empty lifts none.\n highlightedPath: types.optional(types.string, ''),\n // Which of a general GFA's paths the anchored layouts put on x. A path\n // GFA's names are arbitrary and none of them is marked as the\n // reference, so this is a choice; empty means \"infer\", which is the\n // assembly a subgraph was cut against, and the first path in the file\n // for a whole-file import. No effect on an rGFA, whose segments carry\n // their own coordinates. See pathAnchoring.ts.\n referencePath: types.optional(types.string, ''),\n // Whether the toolbar spells out `fetch 12371ms \u00B7 layout 4ms \u00B7 geom 9ms`.\n // Off by default because it was in the toolbar of every published graph\n // figure, where a machine's fetch time is noise a reader has to skip\n // past. The numbers themselves are always on the element as `data-*`\n // attributes, which is what browser tests assert against, so hiding the\n // text costs no coverage.\n showPerf: types.optional(types.boolean, false),\n contigThickness: types.optional(types.number, 6),\n connectorThickness: types.optional(types.number, 2),\n darkMode: types.optional(types.boolean, false),\n scale: types.optional(types.number, 1),\n translateX: types.optional(types.number, 0),\n translateY: types.optional(types.number, 0),\n drawPaths: types.optional(types.boolean, false),\n // Ceiling on the drawing pane, in css px, for a session or a figure that\n // wants a shorter one than the drawing asks for. Unset means the built-in\n // MAX_CANVAS_HEIGHT, which is what every existing session gets.\n //\n // Why this is a knob rather than a smaller default: the pane is as tall\n // as the drawing's own aspect ratio (see canvasHeight), which is right \u2014\n // a graph that is twice as tall as it is wide should not be squeezed\n // into a strip. But one very long node in a cut of short ones makes that\n // aspect ratio all arc: the HPRC CHM13 figure draws a 142 kb allele\n // among sub-kb backbone segments, pins the 600 px ceiling and spends\n // most of it on the loop, with the chain squashed along the bottom edge.\n // Lowering the ceiling there scales the whole drawing down, which is\n // exactly the trade that figure wants and the wrong default for a graph\n // whose height is carrying information.\n paneHeight: types.maybe(types.number),\n // Raise to draw a bigger graph than the default budget allows; see\n // DEFAULT_MAX_GRAPH_NODES for what the numbers cost.\n maxGraphNodes: types.optional(types.number, DEFAULT_MAX_GRAPH_NODES),\n // The bp ceiling on a cut, which is a PROXY for node count and only a\n // good one at fine granularity: on a base-level index 5 Mb is already\n // thousands of segments, so refusing by span is refusing by cost. A\n // level-of-detail tier breaks the proxy \u2014 a whole 249 Mb human\n // chromosome is 474 nodes off the hosted HPRC tier \u2014 so a session that\n // knows which granularity it is pointed at can raise this. The real\n // backstop stays `maxGraphNodes`, which counts what actually came back.\n //\n // The launch MENUS keep the constant deliberately (launchSubgraphView):\n // a user rubber-banding 249 Mb over a fine index should still be told\n // no, because nothing there has said the track is coarse.\n maxRegionBp: types.optional(types.number, MAX_GRAPH_REGION_BP),\n loadedTrackId: types.optional(types.string, ''),\n loadedRegion: types.maybe(\n types.frozen<{\n refName: string\n assemblyName: string\n start: number\n end: number\n }>(),\n ),\n // The haplotypes the cut is for, lane assembly names or PanSN\n // prefixes, beside the region so a re-cut asks for the same set;\n // undefined is every haplotype. Only the GBZ cut reads it.\n subgraphHaplotypes: types.maybe(types.frozen<string[]>()),\n // How far the cut follows links past the segments the region's own\n // links name, defaulting to one hop because at 0 the drawing is wrong\n // rather than merely sparse. A detour that leaves the backbone before\n // the window and rejoins after it has its interior indexed only under\n // its own stable sequence, which no query on the reference reaches, so an\n // arm that bypasses 21 kb of reference arrives as a 43 bp fragment: the\n // one bubble draws as two unrelated small insertions, and nothing on\n // screen says they are the same event. One hop closes those, and that is\n // the whole of what a reader means by seeing the local graph.\n //\n // The cost is queries rather than nodes, and a hop only follows alleles\n // (offReference in RgfaTabixAdapter, which is gfabase's stop-at-cutpoints\n // rule read off the rank tag), so it is bounded by the off-reference\n // segments the cut already reached and it does not walk the backbone out\n // of the window. Measured: the E. coli paa locus goes 14 segments at 0 to\n // 17 at 1 and stays at 17 at 2, so the cut closes and stays closed.\n // HPRC's amylase window goes 63 to 78 to 92, because there the alleles\n // have alleles; wall-clock is flat across all three, the remote index\n // dominating. 0 stays available for a graph where even that is too much.\n //\n // Still not a graph-aware cut: a complete one needs the bubble\n // decomposition (`gfatools view -R`, whose bubble rows state their own\n // member segments) rather than a frontier, which is the follow-up noted\n // in the adapter.\n subgraphContext: types.optional(types.number, 1),\n // Whole-GFA source loaded on attach \u2014 lets a GraphGenomeView be\n // instantiated declaratively from a session/config snapshot.\n gfaLocation: types.maybe(types.frozen<FileLocation>()),\n // The reference span the reference-position ramp runs over. A graph cut\n // from a track already states one as `loadedRegion`; a file-loaded graph\n // has no region at all, and this is how its snapshot can still put a\n // linear track beside it on the same ramp \u2014 both painted from one pair\n // of numbers rather than from the file's own measured extent.\n colorDomain: types.maybe(\n types.frozen<{ start: number; end: number }>(),\n ),\n // The linear view this graph was launched from, so a hovered node can\n // draw its reference span there and vice versa. Written by the launch\n // menu; see hoverSync/.\n connectedViewId: types.maybe(types.string),\n // Whether the cut came from the source track's `coarse` pair\n // (RgfaTabixAdapter), so a restored session re-makes the cut it saved.\n // No bp cap applies to that pair; maxGraphNodes counts what came back.\n coarseCut: types.optional(types.boolean, false),\n }),\n )\n .volatile(() => ({\n graph: undefined as Graph | undefined,\n layoutResult: undefined as LayoutResult | undefined,\n // The bubble index rows over the cut window, when the source track has\n // one beside its segments. Undefined for a graph with no index, which the\n // variant map draws from `derivedBubbles` instead.\n indexBubbles: undefined as MinigraphBubble[] | undefined,\n // the genes over the cut window, read once per cut from the gene track\n geneFeatures: undefined as GeneModel[] | undefined,\n // the tandem repeat arrays over the cut window, from the repeat track\n repeatArrays: undefined as RepeatArray[] | undefined,\n // The graphs the open bubble was popped out of, outermost first, each\n // with what closing back to it restores without a refetch. A stack so a\n // popped superbubble can be mapped and popped again.\n popStack: [] as {\n graph: Graph\n layoutMode: LayoutModeValue\n label: string\n indexBubbles: MinigraphBubble[] | undefined\n }[],\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n error: undefined as unknown,\n isLoading: false,\n loadCanceled: false,\n statusMessage: '',\n hoveredNode: null as string | null,\n hoveredEdge: null as number | null,\n selectedNode: null as string | null,\n viewportDirty: 0,\n // Bumped when the layout's positions are mutated IN PLACE, which only a\n // node drag does. Separate from `viewportDirty` because that one also\n // fires on pan and zoom, and the things keyed off this \u2014 the two hit\n // indexes and the label placement's per-layout caches \u2014 do not depend on\n // the transform at all. Rebuilding a 12k-edge hit index because the user\n // panned cost ~14 ms of the first mousemove after every gesture.\n positionsVersion: 0,\n // Bumped per upload, so the hover autorun re-states its highlights\n // against the batch the renderer now holds.\n geometryVersion: 0,\n // The zoom and the window the current batch was built for; a pan that\n // stays inside it needs no rebuild.\n builtViewport: undefined as { scale: number; bounds: Bounds } | undefined,\n draggingNode: null as string | null,\n // Dragging the background rather than a node. Lives here beside\n // draggingNode instead of in a component ref+state pair, so the two\n // mutually exclusive drag modes are one piece of state read from one place.\n isPanning: false,\n // Who places the view. 'fit' refits it to every new layout, 'user' leaves\n // it where a gesture or a restored session put it, and 'host' takes x\n // from the linear view the pane sits in. Not derived from\n // `isDefaultViewport`, which zoomToFit itself invalidates on its first\n // run \u2014 that made the fit fire once against not-yet-measured dimensions\n // and then never re-fit.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n viewportOwner: 'fit' as ViewportOwner,\n // the pane height a host holds, so a re-cut with more rows does not\n // move the pane while the linear view above it is being dragged\n hostPaneHeight: undefined as number | undefined,\n cutNote: undefined as string | undefined,\n recuts: 0,\n viewportDirtyTimer: undefined as\n ReturnType<typeof setTimeout> | undefined,\n // 0 is never a live rAF handle, so it doubles as \"nothing pending\"\n positionsDirtyFrame: 0,\n // Performance instrumentation, surfaced in GraphStats for browser tests\n // to assert against budgets. `fetchMs` is the GetSubgraph RPC round-trip,\n // `layoutMs` is the Bandage FMMM compute time reported by the\n // GraphComputeLayout RPC, `geometryMs` is the main-thread buildGeometry\n // pass and `geometryStrokeCount` the node strokes it produced.\n lastFetchMs: undefined as number | undefined,\n lastLayoutMs: undefined as number | undefined,\n lastGeometryMs: undefined as number | undefined,\n lastGeometryStrokeCount: undefined as number | undefined,\n }))\n .views(self => ({\n get nodeById() {\n if (self.graph) {\n const m = new Map<string, GraphNode>()\n for (const n of self.graph.nodes) {\n m.set(n.id, n)\n }\n return m\n }\n return undefined\n },\n // bp per node id, which is all the label overlay needs of a GraphNode.\n // Held here rather than rebuilt in the component: that render runs on\n // every pan, and the map is a fact about the graph (measured 6.9 ms an\n // allocation on a 30k-node cut).\n get nodeLengths() {\n const m = new Map<string, number>()\n for (const n of self.graph?.nodes ?? []) {\n m.set(n.id, n.length)\n }\n return m\n },\n get nodeCount() {\n return self.graph?.nodes.length ?? 0\n },\n get edgeCount() {\n return self.graph?.edges.length ?? 0\n },\n get pathCount() {\n return self.graph?.paths?.length ?? 0\n },\n get hasGraph() {\n return self.graph !== undefined\n },\n // The app-wide readiness contract (AppReadyMarker, @jbrowse/capture)\n // reads this: a declared source still fetching, or a graph whose\n // geometry has not been built yet.\n get showLoading() {\n return (\n self.error === undefined &&\n (self.isLoading ||\n ((self.graph !== undefined ||\n !!(self.loadedTrackId && self.loadedRegion) ||\n !!self.gfaLocation) &&\n self.lastGeometryStrokeCount === undefined))\n )\n },\n get canRetryLoad() {\n return !!(self.loadedTrackId && self.loadedRegion) || !!self.gfaLocation\n },\n // Only before anything is drawn: past that point a reload is superseded\n // by the next setting change, and stopping one midway would leave a new\n // graph under the previous graph's layout.\n get canCancelLoad() {\n return self.isLoading && !self.layoutResult\n },\n // Whether the drawing on screen is the FMMM engine's. False before there\n // is a graph, since nothing is drawn by anything yet.\n //\n // Read by the settings that only the engine looks at: the layout quality\n // and the bubble spread do nothing at all to an anchored drawing, which\n // places a node from its coordinates, and the guide says so in prose\n // precisely because the dialog did not. A control that silently does\n // nothing is worse than one that says it cannot \u2014 the same rule the\n // `drawPaths` switch already follows.\n get usesLayoutEngine() {\n return self.graph\n ? modeUsesLayoutEngine(self.layoutMode, self.graph)\n : false\n },\n // A rank-0 backbone to draw x against, whether the segments declared it\n // (rGFA) or a path walk derived it. Only a GFA with neither tags nor\n // paths has no backbone at all, and there force is the one honest layout.\n get canAnchorLayout() {\n return self.graph?.nodes.some(n => n.stable?.rank === 0) ?? false\n },\n // The paths this graph could be anchored on, for the picker. Empty for an\n // rGFA, which has no P/W records and needs none.\n get anchorPaths() {\n return self.graph?.anchorPaths ?? []\n },\n // Which haplotype each ribbon colour belongs to, in the order the file\n // states the paths \u2014 the same list and the same order the geometry keys\n // its colours off, so the key cannot name a colour that is not drawn.\n // Empty unless the ribbons are actually on: a colour key beside a drawing\n // with no colours in it is a legend for nothing.\n get pathLegend() {\n const paths = self.graph?.paths\n return self.drawPaths && paths && pathColorsLegible(paths.length)\n ? pathLegend(paths)\n : []\n },\n // Every walk the graph carries, named for a picker, whatever the count.\n get walkChoices() {\n const paths = self.graph?.paths\n return paths?.length ? pathLegend(paths) : []\n },\n // The lifted walk, or undefined when none is named or the graph on\n // screen does not carry the one that was.\n get walkHighlight() {\n return self.graph && self.highlightedPath\n ? walkHighlight(self.graph, self.highlightedPath)\n : undefined\n },\n // Whether the drawing is actually painted per path, which is not the same\n // question as whether the user asked for it: past MAX_PATH_COLORS the\n // colours say nothing and cost a stroke per path per edge. A bare getter\n // returning a resolved value, the same shape and for the same reason as\n // `effectiveColorScheme` \u2014 every consumer of a path colour reads this\n // one, so the geometry, the hit index and the key cannot disagree about\n // whether there are any. The switch in the settings dialog reads the raw\n // `drawPaths`, so the user's choice stays visible as the choice it is.\n get effectiveDrawPaths() {\n return (\n self.drawPaths && pathColorsLegible(self.graph?.paths?.length ?? 0)\n )\n },\n // The path x is currently drawn on, which is not necessarily the one\n // `referencePath` asked for: an unmatched name falls back rather than\n // leaving the graph unanchored, and the picker has to show what happened.\n get activeReferencePath() {\n return self.graph?.referencePath\n },\n // What to anchor on. An explicit choice wins; otherwise a graph cut from\n // a track is anchored on the assembly it was cut against, which is the\n // one the linear view beside it is showing.\n get preferredReferencePath() {\n return self.referencePath === ''\n ? self.loadedRegion?.assemblyName\n : self.referencePath\n },\n // The span the reference-position ramp runs over: what the snapshot\n // stated, else the region the graph was cut from. Undefined only for a\n // file-loaded graph that states neither, where the ramp falls back to the\n // drawn extent (computeReferenceRamp). A follow's re-cut moves\n // loadedRegion, so the ramp re-spans every new cut, and the lane above\n // with it.\n get rampDomain() {\n return self.colorDomain ?? self.loadedRegion\n },\n // The scheme the renderer actually paints with, which is the raw prop\n // unless it is 'auto'. A bare getter returns a resolved value (root\n // CLAUDE.md): every consumer of a colour reads this one, and the two\n // dropdowns read `colorScheme` so \"Auto\" stays visible as the choice it\n // is.\n //\n // 'auto' asks the GRAPH, not the layout. A hue along the reference is\n // meaningful whenever the segments have reference coordinates \u2014\n // `anchoredBy` is exactly that question \u2014 and a force-directed drawing of\n // an rGFA is the case that proves it: the layout has no reference axis and\n // the ramp still says where on the reference each node came from, which is\n // the only quantity a linear track beside it can be painted with too.\n get effectiveColorScheme(): ResolvedColorScheme {\n return self.colorScheme !== 'auto'\n ? self.colorScheme\n : self.graph?.anchoredBy\n ? 'reference-position'\n : 'uniform'\n },\n get nodePositions() {\n return self.layoutResult?.nodePositions\n },\n // Empty rather than undefined: every consumer maps over it, and a layout\n // with no row structure (FMMM) is a normal state, not a missing one.\n get rowLabels() {\n return self.layoutResult?.rowLabels ?? []\n },\n // Deletions the layout found inside an allele, empty for the same reason\n // rowLabels is: a layout drawn at sequence scale states none, because\n // there a node's drawn extent already IS its length. `deletions` beside\n // this one is the bare-edge kind, which every layout has.\n get alleleDeletions() {\n return self.layoutResult?.alleleDeletions ?? []\n },\n // Whether the current layout states y in screen px rather than in the same\n // units as x (LayoutResult.pixelRows). Everything that has to put the two\n // axes in one expression reads this through scaleX/scaleY.\n get pixelRows() {\n return self.layoutResult?.pixelRows ?? false\n },\n get zoomPercent() {\n return `${(self.scale * 100).toFixed(1)}%`\n },\n // True while the persisted transform is still the schema default, i.e.\n // neither a restored session nor the user has positioned this view yet,\n // so an incoming layout is free to zoom-to-fit.\n get isDefaultViewport() {\n return (\n self.scale === 1 && self.translateX === 0 && self.translateY === 0\n )\n },\n }))\n .views(self => ({\n get poppedFrom() {\n return self.popStack.at(-1)\n },\n // The bubbles the graph itself states, for a graph with no index: a GBZ\n // cut, a pggb file, the inside of a popped bubble.\n get derivedBubbles() {\n return self.graph ? bubblesFromGraph(self.graph) : []\n },\n }))\n .views(self => ({\n // What the variant map draws. Index rows win where both exist: gfatools\n // measured every allele, the layered order only bounds them.\n get bubbles() {\n return self.indexBubbles?.length\n ? self.indexBubbles\n : self.derivedBubbles\n },\n }))\n .views(self => ({\n // As written: a session track is a plain object until something\n // hydrates it, so a slot left at its default reads undefined here.\n get sourceAdapter() {\n const track = getSession(self).tracks.find(\n t => t.trackId === self.loadedTrackId,\n )\n return track\n ? (readConfObject(track, 'adapter') as Record<string, unknown>)\n : undefined\n },\n get assemblyTrackChoices() {\n const region = self.loadedRegion\n if (!region) {\n return []\n }\n return getSession(self)\n .tracks.filter(t =>\n (readConfObject(t, 'assemblyNames') as string[]).includes(\n region.assemblyName,\n ),\n )\n .map(t => ({\n trackId: t.trackId as string,\n name: readConfObject(t, 'name') as string,\n adapterType: (readConfObject(t, 'adapter') as { type: string })\n .type,\n }))\n },\n }))\n .views(self => ({\n get geneTrackChoices() {\n return self.assemblyTrackChoices.filter(t =>\n GENE_ADAPTER_TYPES.has(t.adapterType),\n )\n },\n // The session's feature tracks a repeat annotation could be, for the\n // picker; which one is read is pickRepeatTrack's choice.\n get repeatTrackChoices() {\n return self.assemblyTrackChoices.filter(t =>\n REPEAT_ADAPTER_TYPES.has(t.adapterType),\n )\n },\n // The arrays over the window that state a unit, for the Repeat picker.\n get repeatChoices() {\n return self.repeatArrays ?? []\n },\n }))\n .views(self => ({\n get repeatTrack() {\n return pickRepeatTrack(self.repeatTrackChoices, self.repeatTrackId)\n },\n get selectedRepeat() {\n return self.repeatChoices.find(r => r.key === self.repeatKey)\n },\n }))\n .views(self => ({\n get nodeNeighbors() {\n return self.graph ? buildNeighbors(self.graph) : undefined\n },\n // How far each node's ink reaches from its centreline, for the hit test:\n // the widths the geometry draws with, from the same function.\n get nodeInk(): NodeInk {\n const { contigThickness, nodeWidth, nodeById } = self\n const mean = self.graph ? meanDepth(self.graph) : 0\n return {\n maxHalfWidthPx: maxNodeWidthPx(contigThickness, nodeWidth) / 2,\n halfWidthPx: id => {\n const node = nodeById?.get(id)\n return node\n ? nodeWidthPx(node, contigThickness, nodeWidth, mean) / 2\n : contigThickness / 2\n },\n }\n },\n // Links that skip reference sequence, i.e. the deletions this graph\n // holds. Computed once per graph rather than per geometry rebuild: it is\n // a pass over the edges and the drawing rebuilds on every pan.\n // Walk rows state what each walk skips as its own bar length, so the arcs\n // over the backbone would only say it again, across the bars.\n get deletions() {\n return self.graph && self.layoutMode !== 'walkrows'\n ? deletionEdges(self.graph)\n : []\n },\n // One bar per haplotype walk on its own bp axis, for the walk-rows\n // overlay. Empty under every other layout.\n get walkRowBars() {\n if (self.layoutMode !== 'walkrows' || !self.graph) {\n return undefined\n }\n const repeat = self.selectedRepeat\n const bars = repeat\n ? walkRows(self.graph, repeat, repeat.unit)\n : walkRows(self.graph, self.loadedRegion)\n if (!bars) {\n return undefined\n }\n const samples = self.walkRowSamples\n const rows = samples\n ? bars.rows\n .filter(r => samples.includes(r.sample))\n .sort(\n (a, b) =>\n samples.indexOf(a.sample) - samples.indexOf(b.sample) ||\n a.label.localeCompare(b.label),\n )\n : bars.rows\n return { ...bars, rows: withCalls(rows, repeat?.calls) }\n },\n // The labels drawn beside the rows. Walk rows label from the bars\n // themselves, which follow the selected repeat and sample filter that\n // the layout, run once per cut, cannot.\n get drawnRowLabels() {\n const bars = this.walkRowBars\n return bars\n ? [bars.reference, ...bars.rows].map((row, i) => ({\n label: row.label,\n y: i * ROW_HEIGHT_PX,\n }))\n : self.rowLabels\n },\n // Each bubble in the window with what it is, for the variant map's\n // glyphs on the reference line.\n get bubbleGlyphs() {\n const bubbles = self.layoutMode === 'variants' ? self.bubbles : []\n return bubbles.map(bubble => ({\n bubble,\n ...classifyBubble(bubble),\n }))\n },\n // Exons and names on the backbone, in layout units. Reads\n // positionsVersion so a dragged node takes its exons with it.\n get genePins() {\n dependOn(self.positionsVersion)\n const positions = self.layoutResult?.nodePositions\n return self.showGenes &&\n self.layoutMode !== 'variants' &&\n self.layoutMode !== 'walkrows' &&\n self.graph &&\n self.geneFeatures &&\n positions\n ? genePins(self.graph, self.geneFeatures, positions)\n : []\n },\n // The same bubbles over every other layout, as halos along their nodes.\n // Reads positionsVersion so a dragged node takes its halo with it.\n get bubbleHalos() {\n dependOn(self.positionsVersion)\n const positions = self.layoutResult?.nodePositions\n if (\n !self.showBubbles ||\n self.layoutMode === 'variants' ||\n self.layoutMode === 'walkrows' ||\n !self.graph ||\n !positions\n ) {\n return []\n }\n const labels = new Map(self.walkChoices.map(c => [c.name, c.label]))\n return bubbleHalos(\n self.graph,\n self.bubbles,\n positions,\n name => labels.get(name) ?? name,\n )\n },\n // Every node's midpoint on the reference plus the interval the hue ramps\n // over \u2014 what `reference-position` paints from, and undefined under every\n // other scheme so the walk is never run for a colouring that ignores it.\n //\n // Here for the same reason `deletions` is, only more so: deriving it is a\n // bounded neighbour walk PER NODE (referenceMidpoints), it depends on\n // nothing the transform touches, and it was being redone inside every\n // geometry rebuild \u2014 1.6 ms at 1.5k nodes, 45 ms at 30k, on a pass that\n // reruns on each debounced pan and each drag frame. `reference-position`\n // is also what `auto` resolves to on any anchored graph, so this was the\n // default path rather than an opt-in one.\n get referenceRamp() {\n return self.effectiveColorScheme === 'reference-position' && self.graph\n ? computeReferenceRamp(self.graph, self.rampDomain)\n : undefined\n },\n // The assemblies this view is showing, which is the interface every\n // assembly-aware piece of the app looks for \u2014 `viewTitle` first among\n // them, which falls back through it and otherwise names the view\n // \"Untitled view\". Every published HPRC figure showed that.\n // `launchSubgraphView` only avoids it by writing `displayName`, so a\n // declaratively-instantiated view (a session snapshot, a figure spec) got\n // the fallback.\n //\n // A whole-file import has none: its stable names are a GFA's business and\n // need not name anything the session has loaded. Empty rather than\n // undefined, matching what an LGV with no displayed regions reports.\n get assemblyNames() {\n const region = self.loadedRegion\n return region ? [region.assemblyName] : []\n },\n // Screen px per layout unit, per axis. `scale` is the x zoom and the whole\n // of what a user's wheel moves; y is a second question the layout answers\n // for itself.\n //\n // On a row layout the pitch is already in screen px (rowSpacing.ts), so\n // scaleY is 1 and stays 1: rows are a track's row height, unchanged by\n // zoom, and the backbone under them zooms the way the ruler above it does.\n // On an isotropic layout (FMMM) x and y are one simulation space and one\n // number scales both, exactly as before.\n //\n // Everything that mixes the two axes takes their ratio as `yToX`\n // (scaleY / scaleX) \u2014 see computeEdgeCurves.\n get scaleX() {\n return axisScaleOf(self.scale, self.pixelRows).scaleX\n },\n get scaleY() {\n return axisScaleOf(self.scale, self.pixelRows).scaleY\n },\n // The pair, for everything that draws or hit-tests. Handed over as one\n // value on purpose: every consumer needs both, and passing them separately\n // let a caller supply the x scale and default y, which compiles and draws\n // a wrong picture. See AxisScale.\n get axisScale() {\n return axisScaleOf(self.scale, self.pixelRows)\n },\n // Extent of the drawing in layout units, shared by the pane height and\n // zoomToFit. On a reference-bp layout x is the cut window rather than\n // how far the drawing reaches: an allele anchored far outside it is a\n // fact about the graph, not a reason to draw the window at 6% of the\n // frame. A popped bubble fits to what it drew. Walk rows reach as far as\n // the bars on screen, which a repeat pick or a sample filter narrows\n // after the layout ran.\n get layoutBounds() {\n let bounds:\n { minX: number; minY: number; w: number; h: number } | undefined\n if (self.layoutResult) {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const segments of Object.values(\n self.layoutResult.nodePositions,\n )) {\n for (const seg of segments) {\n minX = Math.min(minX, seg.x)\n minY = Math.min(minY, seg.y)\n maxX = Math.max(maxX, seg.x)\n maxY = Math.max(maxY, seg.y)\n }\n }\n const region =\n self.layoutResult.referenceAxis && self.popStack.length === 0\n ? self.loadedRegion\n : undefined\n if (region && region.end > region.start) {\n minX = region.start\n maxX = region.end\n }\n const bars = this.walkRowBars\n const extent =\n bars && self.layoutResult.extent\n ? walkRowsExtent(bars)\n : self.layoutResult.extent\n if (extent) {\n maxX = Math.max(maxX, extent.maxX)\n maxY = Math.max(maxY, extent.maxY)\n }\n bounds = { minX, minY, w: maxX - minX, h: maxY - minY }\n }\n return bounds\n },\n // The pane is as tall as the drawing, rather than a fixed box the drawing\n // floats in the middle of.\n //\n // A row layout says how tall it is outright: rows are px, so the drawing's\n // height is the row count times the pitch and this is a sum, not a\n // derivation. It used to be derived from the drawing's ASPECT RATIO\n // against the x-fit scale, because y was in bp and a height in px did not\n // exist until a scale was chosen \u2014 which is also why a ceiling had to be\n // put on the row pitch to stop tall graphs from binding the fit on the\n // wrong axis. Both are gone with the unit.\n //\n // An isotropic layout still has no height of its own and keeps the aspect\n // derivation. It reads neither `scale` nor the height it is replacing, so\n // zoomToFit consumes this without feeding back into it.\n get canvasHeight() {\n if (\n self.viewportOwner === 'host' &&\n self.hostPaneHeight !== undefined\n ) {\n return self.hostPaneHeight\n }\n const bounds = this.layoutBounds\n const usableWidth = self.width - FIT_PADDING * 2\n // `paneHeight` replaces the built-in ceiling rather than adding a\n // second clamp under it, and the floor still wins: a pane shorter than\n // MIN_CANVAS_HEIGHT leaves no room to hover a node and read its\n // tooltip, which is the reason that floor exists.\n const ceiling = Math.max(\n MIN_CANVAS_HEIGHT,\n self.paneHeight ?? MAX_CANVAS_HEIGHT,\n )\n if (!bounds) {\n return ceiling\n }\n if (self.pixelRows) {\n // The variant map's drawing is one line; its glyphs and labels are\n // painted above it by the overlay and need the room a row layout\n // would give to rows.\n const floor =\n self.layoutMode === 'variants'\n ? VARIANT_MAP_HEIGHT\n : MIN_CANVAS_HEIGHT\n return Math.min(ceiling, Math.max(floor, bounds.h + FIT_PADDING * 2))\n }\n return bounds.w > 0 && usableWidth > 0\n ? Math.min(\n ceiling,\n Math.max(\n MIN_CANVAS_HEIGHT,\n (bounds.h * usableWidth) / bounds.w + FIT_PADDING * 2,\n ),\n )\n : ceiling\n },\n }))\n .views(self => ({\n get geneTrack() {\n return pickGeneTrack(self.geneTrackChoices, self.geneTrackId)\n },\n // Which of `graph.edges` are deletions, and what each one bypasses, keyed\n // the way the geometry and the hit index address an edge. One map per\n // graph rather than per rebuild, since both of those take it on every\n // pan.\n get deletionEdgeIndexes() {\n return new Map(self.deletions.map(d => [d.edgeIndex, d.bypassed]))\n },\n get hiddenEdgeIndexes() {\n return new Set(\n self.showDeletionEdges ? [] : self.deletions.map(d => d.edgeIndex),\n )\n },\n // The interval the reference-position ramp runs over, for the key beside\n // the drawing, and undefined when no key should be drawn. Two tutorials\n // carry \"red to magenta is left to right of the cut window\" as a sentence\n // in prose because nothing on screen said it.\n //\n // Read off the ramp the drawing is actually painted with rather than\n // re-derived from the domain, so the key cannot come to describe a\n // different interval from the hues beside it \u2014 and so a graph that states\n // no domain, whose interval has to be measured, does not pay for a second\n // neighbour walk to say what the first one already worked out.\n get referenceRampDomain() {\n const ramp = self.referenceRamp\n return ramp\n ? { start: ramp.start, end: ramp.start + ramp.span }\n : undefined\n },\n // The reference interval of the hovered node, for a connected linear view\n // to highlight. Only a graph cut from a track has one: a whole-file import\n // has no region, and its stable names need not name anything in a loaded\n // assembly.\n get hoverHighlight() {\n let result:\n | {\n refName: string\n assemblyName: string\n start: number\n end: number\n }\n | undefined\n const region = self.loadedRegion\n const nodeId = self.hoveredNode\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n if (region && nodeId !== null && nodeById && neighbors) {\n const span = nodeReferenceSpan({ nodeId, nodeById, neighbors })\n if (span) {\n result = {\n refName: region.refName,\n assemblyName: region.assemblyName,\n ...span,\n }\n }\n }\n return result\n },\n }))\n .views(self => ({\n // Every assembly this graph names a segment from, with the locus each one\n // contributes here. rGFA's SN tag is what makes this knowable: the graph\n // states its own contributors, so the view can offer a way out to each of\n // them without consulting an alignment.\n //\n // Gaps larger than the backbone span split a contributor's segments into\n // separate loci and the widest wins, so a sample that also contributes\n // sequence from a distant duplication is launched at the locus on screen\n // rather than at the union of the two.\n get contributingAssemblies() {\n const graph = self.graph\n const backbone = graph ? backboneNodes(graph) : []\n return graph\n ? contributingAssemblies(graph, {\n maxGap: backbone.length > 0 ? backboneSpan(backbone) : Infinity,\n })\n : []\n },\n }))\n .views(self => ({\n // A node's PanSN sample as an assembly this session can open, or\n // undefined. The graph's spelling and the assembly's need not agree:\n // HPRC writes `CHM13` where the assembly is UCSC's `hs1`. The track the\n // graph was cut from states that pairing in its `assemblyNameToPanSN`,\n // so its map is read first, then `assemblyManager`'s names and aliases.\n //\n // `has` before `get`, deliberately: `get` reports an unknown name to\n // `Core-handleUnrecognizedAssembly`, which asks every installed plugin to\n // go supply it, and this is a probe run for hundreds of haplotypes per\n // graph.\n get assemblyResolver() {\n const { assemblyManager } = getSession(self)\n const panSN = self.sourceAdapter?.assemblyNameToPanSN as\n Record<string, string> | undefined\n const byPrefix = new Map(\n Object.entries(panSN ?? {}).map(([asm, prefix]) => [prefix, asm]),\n )\n const loaded = (name: string | undefined) =>\n name !== undefined && assemblyManager.has(name)\n ? (assemblyManager.get(name)?.name ?? name)\n : undefined\n return (sample: string) =>\n loaded(byPrefix.get(sample)) ?? loaded(sample)\n },\n }))\n .views(self => ({\n // The contributors a view can actually be opened on: those naming an\n // assembly this session has loaded. Every strain of an E. coli pangenome\n // demo is its own assembly, so all of them resolve; an HPRC graph names\n // hundreds of haplotypes that no session loads, so only the reference\n // does.\n get launchableAssemblies() {\n return resolveContributors(\n withReferenceRegion(self.contributingAssemblies, self.loadedRegion),\n self.assemblyResolver,\n )\n },\n // Whether there is a linear view this graph may draw a highlight into \u2014\n // the paired one, or the session's only one on the reference assembly.\n // Read by the node menu, which offers the item only when it would land\n // somewhere.\n get canHighlightInLinearView() {\n const region = self.loadedRegion\n return (\n region !== undefined &&\n linearViewTarget({\n views: [...getSession(self).views],\n connectedViewId: self.connectedViewId,\n assemblyName: region.assemblyName,\n }) !== undefined\n )\n },\n // Where one node can be opened: on its own assembly, and on the reference\n // the graph was cut against. Both are padded to a readable window \u2014 a\n // base-level allele is a few bp, and a linear view framed on exactly that\n // shows no context at all.\n nodeLaunchTargets(nodeId: string) {\n const node = self.nodeById?.get(nodeId)\n const own = node ? nodeOwnLocation(node) : undefined\n const ownAssembly = own\n ? resolveLocationAssembly(self.assemblyResolver, own)\n : undefined\n const region = self.loadedRegion\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n const span =\n region && nodeById && neighbors\n ? nodeReferenceSpan({ nodeId, nodeById, neighbors })\n : undefined\n const onReference =\n region && span\n ? {\n sample: region.assemblyName,\n haplotype: undefined,\n refName: region.refName,\n ...span,\n }\n : undefined\n return {\n own:\n own && ownAssembly\n ? { location: paddedLocation(own), assembly: ownAssembly }\n : undefined,\n reference:\n region && onReference\n ? {\n location: paddedLocation(onReference),\n assembly: region.assemblyName,\n }\n : undefined,\n // Unpadded: a mark is of the node, the same span its hover band\n // draws. The padding above is room to read a view opened on it.\n highlight:\n region && onReference\n ? { location: onReference, assembly: region.assemblyName }\n : undefined,\n }\n },\n }))\n .views(self => ({\n // The linear view's zoom past which a follow cuts the source track's\n // coarse pair, or undefined for a track with none.\n get coarseAboveBpPerPx() {\n const coarse = self.sourceAdapter?.coarse as\n { aboveBpPerPx?: unknown } | undefined\n const above = coarse?.aboveBpPerPx\n return typeof above === 'number' ? above : undefined\n },\n }))\n .views(self => ({\n get cutTier(): SubgraphTier {\n return self.coarseCut && self.coarseAboveBpPerPx !== undefined\n ? 'coarse'\n : 'fine'\n },\n }))\n .views(self => ({\n get regionCapBp() {\n return self.cutTier === 'coarse' ? Infinity : self.maxRegionBp\n },\n }))\n .views(self => ({\n // The linear view this pane draws inside, when a display hosts it: x is\n // that view's window and the cut is re-made when the window leaves it.\n // A pane that is a view of its own has none.\n get host(): LinearHost | undefined {\n let view: unknown\n try {\n view = getContainingView(self)\n } catch {\n return undefined\n }\n return view !== self && isLinearHost(view) ? view : undefined\n },\n }))\n .views(self => ({\n // Whether the host places x. Only a layout whose x is reference bp can\n // take the window's transform; force, ordered and walk rows draw in\n // their own coordinates inside the track, and a popped bubble is a\n // picture of its own.\n get hostPlacesX() {\n const { host, loadedRegion: region, layoutResult: layout } = self\n return (\n host !== undefined &&\n host.initialized &&\n region !== undefined &&\n layout?.referenceAxis === true &&\n self.popStack.length === 0 &&\n !host.dynamicBlocks.contentBlocks.some(\n b => b.refName === region.refName && b.reversed,\n )\n )\n },\n }))\n .views(self => ({\n get hostFrame() {\n const { host, loadedRegion } = self\n return self.hostPlacesX && host && loadedRegion\n ? hostFrame(host, loadedRegion)\n : undefined\n },\n }))\n .actions(self => ({\n setError(error: unknown) {\n self.error = error\n self.isLoading = false\n },\n setStatusMessage(message: string) {\n self.statusMessage = message\n },\n setFetchMs(ms: number) {\n self.lastFetchMs = ms\n },\n setLayoutMs(ms: number) {\n self.lastLayoutMs = ms\n },\n setGeometryMetrics(\n ms: number,\n strokeCount: number,\n built: { scale: number; bounds: Bounds },\n ) {\n self.lastGeometryMs = ms\n self.lastGeometryStrokeCount = strokeCount\n self.builtViewport = built\n self.geometryVersion++\n },\n setLayoutQuality(quality: number) {\n self.layoutQuality = quality\n },\n setLinearLayout(linear: boolean) {\n self.linearLayout = linear\n },\n setLayoutMode(mode: LayoutModeValue) {\n self.layoutMode = mode\n },\n // Re-anchor in place rather than re-parsing: the coordinate walk is\n // already recorded on the graph, and only which path counts as rank 0\n // changes. The caller recomputes the layout, the same way it does after\n // setLayoutMode. An rGFA is left alone \u2014 its coordinates are not derived.\n setReferencePath(name: string) {\n self.referencePath = name\n const graph = self.graph\n if (graph?.anchoredBy === 'paths') {\n const reanchored = anchorFromPaths(graph, name)\n inheritForceLayouts(graph, reanchored)\n self.graph = reanchored\n }\n },\n setDrawPaths(draw: boolean) {\n self.drawPaths = draw\n },\n setShowPerf(show: boolean) {\n self.showPerf = show\n },\n setColorScheme(scheme: ColorScheme) {\n self.colorScheme = scheme\n },\n setBubbleSpread(spread: BubbleSpread) {\n self.bubbleSpread = spread\n },\n setNodeWidth(width: NodeWidth) {\n self.nodeWidth = width\n },\n setShowBubbles(show: boolean) {\n self.showBubbles = show\n },\n setShowDeletionEdges(show: boolean) {\n self.showDeletionEdges = show\n },\n setShowGenes(show: boolean) {\n self.showGenes = show\n },\n setGeneTrackId(trackId: string) {\n self.geneTrackId = trackId\n },\n setRepeatTrackId(trackId: string) {\n self.repeatTrackId = trackId\n },\n setRepeatKey(key: string) {\n self.repeatKey = key\n },\n setHighlightedPath(name: string) {\n self.highlightedPath = name\n },\n // Undefined restores the built-in ceiling. Nothing recomputes: the pane\n // reads canvasHeight and the drawing is placed by zoomToFit, which the\n // caller runs if it wants the drawing refitted into the new pane.\n setPaneHeight(px: number | undefined) {\n self.paneHeight = px\n if (self.viewportOwner === 'host') {\n self.hostPaneHeight = px\n }\n },\n // The track a hosted pane cuts from. The first settle makes the cut.\n adoptTrack(trackId: string) {\n if (self.loadedTrackId !== trackId) {\n self.loadedTrackId = trackId\n self.loadedRegion = undefined\n self.coarseCut = false\n self.graph = undefined\n self.layoutResult = undefined\n }\n },\n // The caller refetches \u2014 the number only describes how the next cut is\n // made, and the graph on screen was cut with the old one.\n setSubgraphContext(hops: number) {\n self.subgraphContext = hops\n },\n // Same contract: the caller refetches, since the set describes the next\n // cut and the graph on screen was cut for the old one.\n setWalkRowSamples(samples: string[] | undefined) {\n self.walkRowSamples = samples\n },\n setSubgraphHaplotypes(haplotypes: string[] | undefined) {\n self.subgraphHaplotypes = haplotypes\n },\n // Same contract as setSubgraphContext: describes how the NEXT cut is\n // gated, so the caller refetches. See the prop for why a session is\n // allowed to move this and the launch menus are not.\n setMaxRegionBp(bp: number) {\n self.maxRegionBp = bp\n },\n // Pair with a linear view for the hover sync, without ever repointing an\n // existing pairing: a graph launched *from* an LGV is already paired with\n // it, and stealing that would break the sync the user came in on. So a\n // launch out of the graph only claims the slot when it is empty.\n //\n // A slot naming a view that has been closed is empty too. Held, the view\n // the graph opened next never got its hover, since a highlight is drawn\n // only in the paired view.\n pairWithLinearView(viewId: string) {\n const paired = self.connectedViewId\n const stillOpen =\n paired !== undefined &&\n withRows([...getSession(self).views]).some(\n view => (view as { id?: unknown }).id === paired,\n )\n if (!stillOpen) {\n self.connectedViewId = viewId\n }\n },\n setHoveredNode(nodeId: string | null) {\n self.hoveredNode = nodeId\n },\n setHoveredEdge(edgeIdx: number | null) {\n self.hoveredEdge = edgeIdx\n },\n setSelectedNode(nodeId: string | null) {\n self.selectedNode = nodeId\n },\n setDraggingNode(nodeId: string | null) {\n self.draggingNode = nodeId\n },\n setPanning(panning: boolean) {\n self.isPanning = panning\n },\n stopDragging() {\n self.draggingNode = null\n self.isPanning = false\n },\n // Everything that names a part of the current graph. Reset when the graph\n // is replaced, and by clearGraph.\n clearInteractionState() {\n self.hoveredNode = null\n self.hoveredEdge = null\n self.selectedNode = null\n self.draggingNode = null\n self.isPanning = false\n },\n showNodeDetails(nodeId: string) {\n const node = self.nodeById?.get(nodeId)\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n if (node && nodeById && neighbors) {\n const session = getSession(self)\n const region = self.loadedRegion\n // refName/start/end are what makes the widget show a location rather\n // than a bare id, and they are the same span the linear view\n // highlights on hover.\n const span = region\n ? nodeReferenceSpan({ nodeId, nodeById, neighbors })\n : undefined\n if (isSessionModelWithWidgets(session)) {\n session.showWidget(\n session.addWidget('BaseFeatureWidget', 'baseFeature', {\n featureData: {\n id: node.id,\n name: node.name,\n length: node.length,\n depth: node.depth,\n rank: node.stable?.rank,\n stableName: node.stable?.refName,\n // The assembly that contributed this segment, split off the\n // stable name. On an HPRC graph this is the only thing that\n // says which of 400-odd haplotypes an allele came from.\n contributingAssembly: nodeOwnLocation(node)?.sample,\n // and the haplotype, where the graph's names state one:\n // the assembly to load to open this node on its own\n // coordinates\n contributingHaplotype: nodeOwnLocation(node)?.haplotype,\n // Every assembly that traverses it, which is a different\n // question and one only a path GFA can answer. Absent on an\n // rGFA rather than approximated, so the two are never\n // confused: there `contributingAssembly` is first-seen, not\n // carriage.\n carriedBy: node.samples?.join(', '),\n ...(region && span\n ? {\n refName: region.refName,\n assemblyName: region.assemblyName,\n start: span.start,\n end: span.end,\n }\n : {}),\n },\n }),\n )\n }\n }\n },\n\n // A gesture on a hosted graph moves the linear view, and the frame\n // clock brings x back here; y stays the pane's own.\n setTransform(s: number, tx: number, ty: number) {\n const { host } = self\n if (self.viewportOwner === 'host' && host) {\n host.horizontalScroll(self.translateX - tx)\n } else {\n self.viewportOwner = 'user'\n self.scale = clampZoom(s)\n self.translateX = tx\n }\n self.translateY = ty\n },\n zoom(factor: number, centerX: number, centerY: number) {\n const { host } = self\n if (self.viewportOwner === 'host' && host) {\n host.zoomTo(host.bpPerPx / factor, centerX)\n return\n }\n self.viewportOwner = 'user'\n const newScale = clampZoom(self.scale * factor)\n const ratio = newScale / self.scale\n self.scale = newScale\n self.translateX = centerX - (centerX - self.translateX) * ratio\n // y only follows when it is on the same scale. A row layout's y is\n // screen px (scaleY === 1), so nothing about it changes as x zooms \u2014\n // moving translateY by the ratio there would slide the rows off under\n // the cursor while their pitch stayed put.\n if (!self.pixelRows) {\n self.translateY = centerY - (centerY - self.translateY) * ratio\n }\n },\n setViewportDirty() {\n self.viewportDirty++\n },\n // The positions themselves moved, as opposed to the window onto them.\n setPositionsDirty() {\n self.positionsVersion++\n },\n zoomToFit() {\n // A layout is routinely degenerate on one axis: an anchored window\n // holding only backbone segments puts every node on row 0. So each axis\n // constrains the scale only when it has extent, and only a layout with\n // no extent at all is unfittable. Requiring extent on both axes left\n // that window at scale 1 with the graph off-screen entirely, since x\n // there is reference bp.\n const bounds = self.layoutBounds\n const usableWidth = self.width - FIT_PADDING * 2\n const usableHeight = self.canvasHeight - FIT_PADDING * 2\n // A host owns x, so a fit while hosted places the rows only.\n if (self.viewportOwner === 'host') {\n if (bounds && usableHeight > 0) {\n self.translateY = fittedTranslateY(\n bounds,\n usableHeight,\n self.scaleY,\n )\n }\n return\n }\n // Nothing to fit into before the canvas is measured. The autorun re-runs\n // once width lands, so skipping here beats computing a negative scale\n // and persisting that transform into the session snapshot.\n if (\n bounds &&\n usableWidth > 0 &&\n usableHeight > 0 &&\n // A row layout needs x extent specifically: y cannot stand in for it\n // there, having no scale left to solve for.\n (bounds.w > 0 || (!self.pixelRows && bounds.h > 0))\n ) {\n // A row layout fits on x ALONE, which is the point of it: the pane is\n // as tall as the rows are and the rows are as tall as they need to be,\n // so there is no height to fit into and nothing that can make the\n // backbone narrower than the pane it is supposed to line up with. That\n // is the bug this axis change is for \u2014 past ~12 rows the drawing was\n // taller than it was wide, the vertical axis bound the fit, and the\n // backbone stopped matching the linear view above it.\n //\n // An isotropic layout still fits on whichever axis binds, and the\n // leftover on the other is split evenly.\n const fitX = bounds.w > 0 ? usableWidth / bounds.w : Infinity\n const fitY = bounds.h > 0 ? usableHeight / bounds.h : Infinity\n const newScale = clampZoom(\n self.pixelRows ? fitX : Math.min(fitX, fitY),\n )\n self.scale = newScale\n self.translateX =\n FIT_PADDING -\n bounds.minX * newScale +\n (usableWidth - bounds.w * newScale) / 2\n // scaleY, which a row layout pins at 1\n self.translateY = fittedTranslateY(\n bounds,\n usableHeight,\n self.pixelRows ? 1 : newScale,\n )\n }\n },\n clearPerfMetrics() {\n self.lastFetchMs = undefined\n self.lastLayoutMs = undefined\n self.lastGeometryMs = undefined\n self.lastGeometryStrokeCount = undefined\n self.builtViewport = undefined\n },\n }))\n .actions(self => ({\n // The frame clock. Unclamped, since clampZoom's floor is there to keep a\n // scale finite and 1 / bpPerPx already is; clamped, a whole-chromosome\n // window would stop lining up.\n hostTransform(scale: number, translateX: number) {\n const engaging = self.viewportOwner !== 'host'\n if (engaging) {\n self.hostPaneHeight = self.canvasHeight\n self.viewportOwner = 'host'\n }\n self.scale = scale\n self.translateX = translateX\n if (engaging) {\n self.zoomToFit()\n }\n },\n // A drawing still on the reference stays where the host left it; one\n // whose x no longer means bp is refit.\n releaseHost() {\n if (self.viewportOwner === 'host') {\n self.hostPaneHeight = undefined\n if (self.layoutResult?.referenceAxis) {\n self.viewportOwner = 'user'\n } else {\n self.viewportOwner = 'fit'\n self.zoomToFit()\n }\n }\n },\n }))\n .actions(self => {\n // Pan/zoom can wait: the geometry on screen is still correct while the\n // gesture runs (the viewport bounds carry 20% overscan), so rebuilding is\n // deferred until the user settles.\n function scheduleViewportDirty() {\n clearTimeout(self.viewportDirtyTimer)\n self.viewportDirtyTimer = setTimeout(() => {\n if (isAlive(self)) {\n self.setViewportDirty()\n }\n }, VIEWPORT_DEBOUNCE_MS)\n }\n\n // A node drag cannot wait \u2014 the node only moves on screen when its\n // geometry is rebuilt \u2014 but a bump costs a full geometry rebuild plus\n // invalidation of both hit-detection indexes, and mousemove fires in\n // bursts well above the frame rate. Coalescing to the next frame keeps the\n // drag live while bounding that work to once per frame instead of once\n // per event.\n function requestPositionsDirtyFrame() {\n cancelAnimationFrame(self.positionsDirtyFrame)\n self.positionsDirtyFrame = requestAnimationFrame(() => {\n if (isAlive(self)) {\n self.setPositionsDirty()\n }\n })\n }\n\n return {\n // Moves the position objects IN PLACE, which is the one thing\n // jbrowse-components' upload invariant says not to do (\"per-region\n // upload values must be freshly constructed, never mutated \u2014 backends\n // diff by reference identity\"), so it is worth saying why this is not\n // that case and what it costs instead.\n //\n // Nothing here diffs by identity: the geometry is rebuilt wholesale and\n // handed over as one batch, so `nodePositions` identity is not a\n // protocol between the model and the backend the way a per-region map\n // is. What identity DOES drive is the fit: the zoom-to-fit autorun and\n // `layoutBounds` \u2014 and so `canvasHeight` \u2014 key off `layoutResult`, and\n // they mean \"a new LAYOUT arrived\", not \"a node moved\". Publishing a\n // fresh positions record per frame of a drag would refit the drawing\n // and resize the pane under the cursor, sixty times a second.\n //\n // So the change is announced by `positionsVersion` instead, and every\n // cache that would otherwise trust identity takes it as a key. The cost\n // of that trade is that the version is a thing to remember: a caller\n // that mutates here and does not bump it gets stale curves, stale hit\n // boxes and stale labels, silently. Everything downstream of it is\n // reached from this one action.\n moveNode(nodeId: string, dx: number, dy: number) {\n const positions = self.layoutResult?.nodePositions\n if (positions?.[nodeId]) {\n for (const seg of positions[nodeId]) {\n seg.x += dx\n seg.y += dy\n }\n requestPositionsDirtyFrame()\n }\n },\n scheduleViewportDirty,\n }\n })\n .actions(self => {\n let loadController: AbortController | undefined\n\n // `scaling.nodes` rather than the graph's own: under a compressing\n // drawn-length law a node's `length` crosses this boundary as a drawn\n // length, which is all the engine ever reads it as.\n //\n // An anchored graph also carries a seed per node, the backbone along x,\n // and asks the engine not to rotate components: FMMM then keeps the\n // reference's coarse shape instead of curling it into a C\n // (docs/layout-experiments.md, experiment 2).\n function callLayout(graph: Graph, scaling: LayoutScaling) {\n const session = getSession(self)\n const { rpcManager } = session\n // Stable grouping key for the layout RPC; a view has no display-level\n // rpcSessionId. `rpcManager.call` injects sessionId into the args.\n const sessionId = 'graph'\n const anchored = graph.nodes.some(isBackbone)\n return rpcManager.call(sessionId, 'GraphComputeLayout', {\n graph: {\n nodes: anchored ? seededNodes(graph, scaling) : scaling.nodes,\n edges: graph.edges,\n },\n options: {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n ...scaling.opts,\n ...(anchored ? { rotateComponents: false } : {}),\n },\n signal: loadController?.signal,\n // A StatusCallback takes an RpcStatus, not a string \u2014 it may be a\n // bare label, a phase, or a phase that threw. `statusMessageText` is\n // core's reader for the human-facing line.\n statusCallback: status => {\n self.setStatusMessage(statusMessageText(status) ?? '')\n },\n }) as Promise<{ result: LayoutResult; duration: number }>\n }\n\n // The engine's inputs, and only those: the graph, plus what `callLayout`\n // puts in `options`. The reference path is there because the seeds are a\n // function of it; the colour scheme and the anchored modes' own settings\n // are absent because none of them reaches the engine.\n function forceLayoutKey(graph: Graph) {\n const anchored = graph.nodes.some(isBackbone)\n return `${self.layoutQuality}|${self.linearLayout}|${self.bubbleSpread}|${anchored}|${graph.referencePath ?? ''}`\n }\n\n // Single dispatch point for every layout mode. A mode that returns a\n // result computed it locally; one that returns undefined can't draw this\n // graph and hands off to the remote FMMM engine, which is also how\n // 'force' is expressed. See LAYOUT_MODES.\n function* computeLayout(graph: Graph) {\n const start = performance.now()\n const local = layoutModeByValue(self.layoutMode).run(\n graph,\n self.loadedRegion,\n self.host ? self.layoutResult?.sampleRows : undefined,\n )\n if (local) {\n return { result: local, duration: performance.now() - start }\n }\n const cache = forceLayoutsOf(graph)\n const key = forceLayoutKey(graph)\n const hit = cache.get(key)\n if (hit) {\n return { result: hit, duration: performance.now() - start }\n }\n // The engine lays out the runs, not the nodes: a base-level cut is\n // thousands of nodes in unbranching chains, and one chain per run is\n // the same drawing at a third of the time. Members take their share of\n // the run's polyline by drawn length, so the picture is per node again\n // before anything else sees it.\n const spread = spreadFor(self.bubbleSpread)\n const merged = mergeRuns(graph)\n const { result, duration } = (yield callLayout(\n merged.graph,\n layoutScaling(merged.graph, spread),\n )) as { result: LayoutResult; duration: number }\n const scaling = layoutScaling(graph, spread)\n const drawn = new Map(\n scaling.nodes.map(n => [\n n.id,\n drawnNodeLength(scaling.opts, n.length),\n ]),\n )\n const positions = splitRuns(\n result.nodePositions,\n merged.runs,\n id => drawn.get(id) ?? 0,\n )\n // Turned so the reference reads left to right, like the linear view\n // above it. Before `remember`, so the cache hands back the drawing as\n // it was shown.\n const oriented = {\n ...result,\n nodePositions: graph.nodes.some(isBackbone)\n ? orientToReference(graph, positions)\n : positions,\n }\n // Under the key read BEFORE the call: the settings that produced this\n // drawing are not necessarily the ones on screen now, and filing it\n // under the current ones would serve it up as a layout it is not.\n remember(cache, key, oriented)\n return { result: oriented, duration }\n }\n\n // Which layout request is the live one. A layout is async and nothing in\n // the UI waits for it, so several are routinely in flight: every control\n // in the settings dialog fires `recomputeLayout` on change, and a force\n // layout takes seconds where an anchored one is instant.\n //\n // The graph identity below is not enough to order them, because the\n // competing requests are usually layouts of the SAME graph. Left to\n // resolve order the last one to FINISH won rather than the last one\n // asked for, so switching away from a slow force layout \u2014 to an anchored\n // mode, a cheaper bubble spread, a lower quality \u2014 drew the abandoned\n // one over the top of the chosen one seconds later, with the dropdown\n // still naming the choice that was discarded.\n let liveRequest = 0\n\n // Applied under a guard because a layout is async and the user can load a\n // different graph, or ask for a different layout of it, while one is in\n // flight; the stale result must not land.\n function* layoutInto(graph: Graph) {\n const request = ++liveRequest\n const isLive = () => self.graph === graph && request === liveRequest\n const signal = loadController?.signal\n let computed\n try {\n computed = yield* computeLayout(graph)\n } catch (e) {\n // A discarded layout's failure is not the user's problem: the drawing\n // they asked for is on screen or on its way, and raising a banner\n // over it reports a graph as broken because a setting they moved on\n // from could not be drawn. An aborted one was discarded by the load\n // that replaced it, even when the graph is still the same object.\n if (!isLive() || signal?.aborted) {\n return false\n }\n throw e\n }\n const live = isLive()\n if (live) {\n self.layoutResult = computed.result\n self.setLayoutMs(computed.duration)\n }\n return live\n }\n\n // `keepSelection` for a re-cut of the same source: node ids survive one\n // where edge indexes do not, so the selection is found again by id.\n function* parseAndLayout(\n text: string,\n name: string,\n keepSelection = false,\n ) {\n self.setStatusMessage('Parsing GFA')\n const gfaGraph = parseGFA(text)\n // A general GFA states its coordinates only in its P/W lines, so the\n // walk that recovers them happens before anything reads `stable` \u2014\n // otherwise the anchored layouts see an unanchored graph and hand off\n // to force.\n const graph = anchorGraph(\n convertGFAToGraph(gfaGraph, name),\n self.preferredReferencePath,\n )\n // Checked here, between parsing and laying out, because this is the one\n // point both load paths pass through and it is upstream of everything\n // expensive: the layout, the geometry and the per-frame draw calls all\n // scale with this number. The whole-file import path had no cap at all,\n // so a chromosome-scale GFA would parse and then freeze the tab.\n if (graph.nodes.length === 0) {\n throw new Error(`No graph segments in ${name}`)\n }\n if (graph.nodes.length > self.maxGraphNodes) {\n throw new Error(\n `Graph too large to draw: ${graph.nodes.length.toLocaleString()} nodes (limit ${self.maxGraphNodes.toLocaleString()}). Zoom in to a smaller region, or raise maxGraphNodes on this view.`,\n )\n }\n const selected = keepSelection ? self.selectedNode : null\n self.graph = graph\n self.indexBubbles = undefined\n self.geneFeatures = undefined\n self.repeatArrays = undefined\n self.popStack = []\n // hoveredEdge is an index into graph.edges and hoveredNode/selectedNode\n // are ids, so all three address the graph being replaced here. Carrying\n // them over points the tooltip and the highlight at whatever now happens\n // to sit at that index.\n self.clearInteractionState()\n if (selected !== null && self.nodeById?.has(selected)) {\n self.selectedNode = selected\n }\n self.setStatusMessage('Computing layout')\n yield* layoutInto(graph)\n }\n\n // Which load is the live one, for the reason liveRequest orders layouts:\n // every change in the settings dialog re-cuts, a remote cut takes seconds,\n // and the last one to finish is not the last one asked for.\n let liveLoad = 0\n\n function beginLoad() {\n const load = ++liveLoad\n loadController?.abort()\n loadController = new AbortController()\n self.loadCanceled = false\n return {\n isLive: () => load === liveLoad,\n signal: loadController.signal,\n }\n }\n\n function loadedTrack() {\n return self.loadedTrackId\n ? getSession(self).tracks.find(t => t.trackId === self.loadedTrackId)\n : undefined\n }\n\n // The bubble index that the hosted HPRC build keeps beside its segments,\n // `<prefix>.bubbles.bed.gz`, read through the bubble adapter over the same\n // window. A graph whose source has no such file, the ordinary case for a\n // graph of one's own, keeps its derived bubbles, so a failure here is not\n // the graph's problem. A coarse cut reads none: its nodes are the\n // bubbles, and the index over its window can run to a chromosome's rows.\n function* loadBubbles(\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n isLive: () => boolean,\n ) {\n // The track config arrives as written, so the prefix is either the\n // `uri` shorthand or the segments location it expands to.\n const prefix = bubblePrefix(adapterConfig)\n if (\n adapterConfig.type !== 'RgfaTabixAdapter' ||\n prefix === undefined ||\n self.cutTier === 'coarse'\n ) {\n return\n }\n try {\n const features = (yield getSession(self).rpcManager.call(\n 'graph',\n 'CoreGetFeatures',\n {\n adapterConfig: {\n type: 'MinigraphBubbleAdapter',\n uri: `${prefix}.bubbles.bed.gz`,\n baseUri: adapterConfig.baseUri,\n assemblyNameToPanSN: adapterConfig.assemblyNameToPanSN,\n },\n regions: [region],\n },\n )) as Feature[]\n if (isLive()) {\n self.indexBubbles = features.map(f => ({\n refName: region.refName,\n start: f.get('start'),\n end: f.get('end'),\n segmentCount: f.get('segmentCount') as number,\n pathCount: (f.get('pathCount') as number | undefined) ?? 0,\n inversion: f.get('inversion') as boolean,\n shortestAlleleLength: f.get('shortestAlleleLength') as number,\n longestAlleleLength: f.get('longestAlleleLength') as number,\n segments: f.get('segments') as string,\n shortestAllele: undefined,\n longestAllele: undefined,\n }))\n }\n } catch (e) {\n console.warn('[GraphGenomeView] no bubble index for this graph', e)\n }\n }\n\n // A session track's features over the cut. Undefined when the session\n // has no such track or the read fails: an annotation is never the\n // graph's problem.\n function* trackFeatures(\n trackId: string | undefined,\n region: SubgraphRegion,\n what: string,\n ) {\n const session = getSession(self)\n const config = trackId\n ? session.tracks.find(t => t.trackId === trackId)\n : undefined\n if (!config) {\n return undefined\n }\n try {\n return (yield session.rpcManager.call('graph', 'CoreGetFeatures', {\n adapterConfig: readConfObject(config, 'adapter'),\n regions: [region],\n })) as Feature[]\n } catch (e) {\n console.warn(`[GraphGenomeView] no ${what} for this graph`, e)\n return undefined\n }\n }\n\n // The genes over the cut, from the session's annotation track for the\n // assembly, so the backbone can carry its exons and names.\n function* loadGenes(region: SubgraphRegion, isLive: () => boolean) {\n const features = yield* trackFeatures(\n self.geneTrack?.trackId,\n region,\n 'genes',\n )\n if (features && isLive()) {\n self.geneFeatures = geneModelsFrom(features)\n }\n }\n\n // The tandem repeat arrays over the cut, from the session's repeat\n // track, for the walk rows to measure between and tile by.\n function* loadRepeats(region: SubgraphRegion, isLive: () => boolean) {\n const features = yield* trackFeatures(\n self.repeatTrack?.trackId,\n region,\n 'repeats',\n )\n if (features && isLive()) {\n self.repeatArrays = repeatArraysFrom(features)\n }\n }\n\n // Inner loading logic shared by loadFromTabixSubgraph and refetchIfNeeded\n function* doSubgraphLoad(\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n opts: SubgraphCutOptions = {},\n ) {\n const { isLive, signal } = beginLoad()\n const track = loadedTrack()\n const regionSize = region.end - region.start\n // Past the size cap the graph view declines rather than degrading to a\n // non-graph rectangle rendering; large-region and full-genome\n // comparison is a linear synteny view instead.\n const refusal =\n (track &&\n offReferenceProblem(\n graphReferenceAssembly(track),\n region.assemblyName,\n )) ??\n (regionSize > self.regionCapBp\n ? `Region too large (${formatSpanBp(regionSize)}) \u2014 zoom in to view graph (max ${formatSpanBp(self.regionCapBp)})`\n : undefined)\n if (refusal !== undefined) {\n self.graph = undefined\n self.layoutResult = undefined\n self.isLoading = false\n self.error = new Error(refusal)\n return\n }\n self.isLoading = true\n self.error = undefined\n self.setStatusMessage('Fetching subgraph')\n try {\n const session = getSession(self)\n const { rpcManager } = session\n const sessionId = 'graph' // getRpcSessionId(self) no rpcSessionId getter\n const fetchStart = performance.now()\n const gfaText = (yield rpcManager.call(sessionId, 'GetSubgraph', {\n adapterConfig,\n region,\n opts,\n signal,\n })) as string\n if (!isLive()) {\n return\n }\n self.setFetchMs(performance.now() - fetchStart)\n if (!gfaText) {\n throw new Error(\n 'Adapter returned no GFA \u2014 region may be outside indexed data or the adapter does not implement getSubgraph',\n )\n }\n const label = locLabel(region)\n yield* parseAndLayout(gfaText, label, true)\n if (!isLive()) {\n return\n }\n // Three independent remote reads, each landing as it arrives: in\n // turn they held the overlay over a drawn graph for the sum of their\n // round trips.\n self.setStatusMessage('Reading annotations')\n yield Promise.all([\n flow(loadBubbles)(adapterConfig, region, isLive),\n flow(loadGenes)(region, isLive),\n flow(loadRepeats)(region, isLive),\n ])\n } catch (e) {\n if (isLive()) {\n console.error('[GraphGenomeView.loadFromTabixSubgraph]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.isLoading = false\n }\n }\n }\n\n // The cut behind a graph whose source is a track in this session, which is\n // what both a launch snapshot and a restored session carry. Silent when\n // there is no such pair, which is the whole-file case. A pair naming a\n // track the session lacks is reported, because otherwise the view sits on\n // an empty import form with nothing saying why.\n //\n // Nothing here saves and restores the transform: `viewportOwner` is\n // what protects a restored session's pan/zoom, and it gates the fit\n // autorun \u2014 the only thing in this flow that would otherwise move the\n // view. A save/restore pair around the load wrote back the values it had\n // just read.\n function* cutFromLoadedTrack() {\n const region = self.loadedRegion\n const track = loadedTrack()\n if (region && self.loadedTrackId && !track) {\n self.error = new Error(\n `The track this graph was cut from, \"${self.loadedTrackId}\", is not in this session`,\n )\n } else if (track && region) {\n // A hop past a coarse cut reaches nothing new: every bubble node's\n // two links are indexed under the backbone either side of it.\n const coarse = self.cutTier === 'coarse'\n yield* doSubgraphLoad(readConfObject(track, 'adapter'), region, {\n hops: coarse ? 0 : self.subgraphContext,\n haplotypes: self.subgraphHaplotypes,\n tier: coarse ? 'coarse' : undefined,\n })\n }\n }\n\n // Raises `isLoading` before any fetch, so a view waiting on a remote file\n // shows its loading state instead of the import form. Text already in\n // hand is parsed without yielding first.\n // `region` is the window a declared file was stated beside. Held through\n // the load rather than restored after it, so the parse anchors on that\n // assembly's path and a failed or canceled load can still be retried\n // with it.\n function* loadWholeGFA(\n name: string,\n source: string | ((signal: AbortSignal) => Promise<string>),\n region?: SubgraphRegion,\n ) {\n const { isLive, signal } = beginLoad()\n self.loadedTrackId = ''\n self.loadedRegion = region\n self.coarseCut = false\n self.isLoading = true\n self.error = undefined\n try {\n const text =\n typeof source === 'string'\n ? source\n : ((yield source(signal)) as string)\n if (isLive()) {\n yield* parseAndLayout(text, name)\n }\n } catch (e) {\n if (isLive()) {\n console.error('[GraphGenomeView.loadWholeGFA]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.isLoading = false\n }\n }\n return isLive()\n }\n\n return {\n // Back to the import form: drop the graph, everything derived from it,\n // and the source it came from. Leaving the source declared kept\n // `showLoading` true over the import form and had a reloaded session\n // cut the dismissed graph again. Any load in flight ends here too,\n // or it would land its graph afterwards.\n clearGraph() {\n loadController?.abort()\n loadController = undefined\n liveLoad++\n liveRequest++\n self.graph = undefined\n self.layoutResult = undefined\n self.loadedTrackId = ''\n self.loadedRegion = undefined\n self.coarseCut = false\n self.cutNote = undefined\n self.gfaLocation = undefined\n self.indexBubbles = undefined\n self.geneFeatures = undefined\n self.repeatArrays = undefined\n self.popStack = []\n self.error = undefined\n self.isLoading = false\n self.loadCanceled = false\n self.statusMessage = ''\n self.clearInteractionState()\n self.clearPerfMetrics()\n },\n cancelLoad() {\n if (self.canCancelLoad) {\n loadController?.abort()\n loadController = undefined\n liveLoad++\n liveRequest++\n self.graph = undefined\n self.isLoading = false\n self.statusMessage = ''\n self.loadCanceled = true\n }\n },\n loadGFA: flow(function* (text: string, name = 'Imported GFA') {\n yield* loadWholeGFA(name, text)\n }),\n loadGFAFromLocation: flow(function* (location: FileLocation) {\n self.setStatusMessage('Fetching GFA')\n const stated = self.loadedRegion\n const live = yield* loadWholeGFA(\n 'uri' in location\n ? (location.uri.split('/').pop() ?? 'GFA')\n : 'GFA',\n signal =>\n openLocation(location).readFile({ encoding: 'utf8', signal }),\n stated,\n )\n if (stated && live && self.graph) {\n yield* loadRepeats(stated, () => self.loadedRegion === stated)\n }\n }),\n loadFromTabixSubgraph: flow(function* (\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n opts: {\n trackId?: string\n } = {},\n ) {\n self.loadedTrackId = opts.trackId ?? ''\n self.loadedRegion = opts.trackId ? region : undefined\n self.coarseCut = false\n yield* doSubgraphLoad(adapterConfig, region, {\n hops: self.subgraphContext,\n haplotypes: self.subgraphHaplotypes,\n })\n }),\n // Cut on attach only \u2014 a graph already on screen is either the user's\n // own or one this just drew.\n refetchIfNeeded: flow(function* () {\n if (!self.graph) {\n yield* cutFromLoadedTrack()\n }\n }),\n // Cut the same region again, for a change to what the cut returns\n // rather than to how it is drawn (subgraphContext).\n reloadSubgraph: flow(function* () {\n yield* cutFromLoadedTrack()\n }),\n // Re-read one annotation track alone, for a track change or a graph\n // that came from a whole file beside a stated region. The graph, its\n // layout and any open bubble stay as they are. Live only while the\n // track it read is still the one chosen, so a slow read cannot land\n // over the pick that followed it.\n reloadRepeats: flow(function* () {\n const region = self.loadedRegion\n const trackId = self.repeatTrack?.trackId\n if (region) {\n yield* loadRepeats(\n region,\n () =>\n self.loadedRegion === region &&\n self.repeatTrack?.trackId === trackId,\n )\n }\n }),\n reloadGenes: flow(function* () {\n const region = self.loadedRegion\n const trackId = self.geneTrack?.trackId\n if (region) {\n yield* loadGenes(\n region,\n () =>\n self.loadedRegion === region &&\n self.geneTrack?.trackId === trackId,\n )\n }\n }),\n // Open one bubble: the graph becomes the segments the bubble row names,\n // drawn in the layout the reader is in, or force-directed from the\n // variant map. The graph it came from stays behind it, one click away,\n // and the popped graph gets its own derived bubbles, so a superbubble\n // opens progressively.\n popBubble: flow(function* (bubble: MinigraphBubble) {\n const graph = self.graph\n if (!graph) {\n return\n }\n const sub = bubbleSubgraph(graph, bubbleSegmentIds(bubble))\n if (sub.nodes.length === 0) {\n getSession(self).notify(\n 'None of the segments of this bubble are in the cut; widen the graph context to open it',\n 'info',\n )\n return\n }\n const { isLive } = beginLoad()\n self.popStack = [\n ...self.popStack,\n {\n graph,\n layoutMode: self.layoutMode,\n label: graph.name,\n indexBubbles: self.indexBubbles,\n },\n ]\n self.indexBubbles = undefined\n const label = `${BUBBLE_KIND_NAMES[classifyBubble(bubble).kind]} at ${bubble.refName}:${bubble.start.toLocaleString()}`\n self.graph = { ...sub, name: label }\n if (self.layoutMode === 'variants') {\n self.layoutMode = 'force'\n }\n self.clearInteractionState()\n self.viewportOwner = 'fit'\n self.isLoading = true\n try {\n if (yield* layoutInto(self.graph)) {\n self.isLoading = false\n }\n } catch (e) {\n if (isLive()) {\n self.error = e\n self.isLoading = false\n }\n }\n }),\n unpopBubble: flow(function* () {\n const from = self.poppedFrom\n if (!from) {\n return\n }\n self.popStack = self.popStack.slice(0, -1)\n self.graph = from.graph\n self.layoutMode = from.layoutMode\n self.indexBubbles = from.indexBubbles\n self.clearInteractionState()\n self.viewportOwner = 'fit'\n self.isLoading = true\n try {\n if (yield* layoutInto(from.graph)) {\n self.isLoading = false\n }\n } catch (e) {\n self.error = e\n self.isLoading = false\n }\n }),\n recomputeLayout: flow(function* () {\n const graph = self.graph\n if (!graph) {\n return\n }\n self.isLoading = true\n self.setStatusMessage('Computing layout')\n\n try {\n // A superseded request leaves the spinner to the request that\n // replaced it: clearing it here reports \"done\" while the layout the\n // user actually asked for is still being computed, which is the\n // common case when a cheap choice follows an expensive one.\n if (yield* layoutInto(graph)) {\n self.isLoading = false\n }\n } catch (e) {\n console.error('[GraphGenomeView.recomputeLayout]', e)\n self.error = e\n self.isLoading = false\n }\n }),\n }\n })\n .actions(self => ({\n // The settle clock. A pan the cut still holds fetches nothing; one past\n // its edge re-cuts the window plus a window-width each side, on the tier\n // the zoom asks for. Overlapping re-cuts are ordered by doSubgraphLoad's\n // liveLoad, so the latest window wins.\n settleOn(seen: HostWindow) {\n const above = self.coarseAboveBpPerPx\n const tier =\n above !== undefined && seen.bpPerPx > above ? 'coarse' : 'fine'\n const cap = tier === 'coarse' ? Infinity : self.maxRegionBp\n const visible = seen.end - seen.start\n self.cutNote =\n visible > cap\n ? `Holding the last cut: ${formatSpanBp(visible)} is past the ${formatSpanBp(cap)} a cut may span`\n : undefined\n if (\n self.cutNote !== undefined ||\n (tier === self.cutTier && cutHolds(self.loadedRegion, seen))\n ) {\n return\n }\n self.coarseCut = tier === 'coarse'\n self.loadedRegion = hostCut(seen, cap)\n self.recuts++\n void self.reloadSubgraph()\n },\n }))\n .actions(self => ({\n // The host's two clocks, started by the display that hosts the pane.\n // The frame clock moves x with every frame of the linear view and\n // fetches nothing; the settle clock wakes on its debounced blocks and\n // re-cuts only when the window has left the cut, whatever the layout.\n startHosting() {\n addDisposer(\n self,\n reaction(\n () => self.hostPlacesX,\n places => {\n if (!places) {\n self.releaseHost()\n }\n },\n { name: 'GraphHostPlacesX' },\n ),\n )\n addDisposer(\n self,\n reaction(\n () => self.hostFrame,\n frame => {\n if (frame) {\n self.hostTransform(frame.scale, frame.translateX)\n }\n },\n {\n equals: (a, b) =>\n a?.scale === b?.scale && a?.translateX === b?.translateX,\n fireImmediately: true,\n name: 'GraphHostFrame',\n },\n ),\n )\n addDisposer(\n self,\n reaction(\n () => self.host?.coarseDynamicBlocks,\n blocks => {\n const { host } = self\n const seen = blocks && host ? hostWindow(host) : undefined\n if (seen) {\n self.settleOn(seen)\n }\n },\n { fireImmediately: true, name: 'GraphHostSettle' },\n ),\n )\n },\n }))\n .actions(self => ({\n startRenderingBackend(backend: Renderer) {\n if (!self.autorunsInstalled) {\n // Autorun: paint the lane this graph was cut from in the graph's own\n // reference-position ramp, so a block above and its node below share\n // a hue with nothing configured. Follows the domain, so a re-cut or\n // an opened bubble moves the lane's ramp with the drawing.\n addDisposer(\n self,\n autorun(() => {\n const domain = self.rampDomain\n const trackId = self.loadedTrackId\n if (\n domain &&\n trackId &&\n self.effectiveColorScheme === 'reference-position'\n ) {\n const display = untracked(() =>\n sourceLaneDisplay(\n getSession(self).views,\n self.connectedViewId,\n trackId,\n ),\n )\n if (display) {\n paintSourceLane(display, referencePositionColor(domain))\n }\n }\n }),\n )\n\n // Autorun: keep the view fitted to the graph until the user moves it.\n // Reads layoutResult plus (via zoomToFit) width/canvasHeight, so it\n // re-fires \u2014 and re-fits \u2014 as the layout arrives and the canvas is\n // measured, rather than firing once against not-yet-known dimensions.\n // A manual pan/zoom (or a restored-session transform) makes the\n // viewport the user's, and a host makes it the linear view's.\n addDisposer(\n self,\n autorun(() => {\n if (\n self.layoutResult &&\n untracked(() => self.viewportOwner === 'fit')\n ) {\n self.zoomToFit()\n }\n }),\n )\n\n // Autorun: mirror a connected linear view's hover onto the graph. An\n // LGV writes `{hoverPosition, hoverFeature}` to session.hovered on\n // every mousemove; neither field names the source view, so the guard\n // is that the position lies in the region this graph was cut from.\n //\n // Only `hovered` is tracked \u2014 the graph reads are untracked, so a\n // geometry rebuild can't re-fire this and clobber a hover the canvas\n // itself set. Assigning an unchanged id doesn't notify, so a hover\n // that travels within one segment costs nothing downstream.\n addDisposer(\n self,\n autorun(() => {\n const hover = readLgvHover(getSession(self).hovered)\n untracked(() => {\n const region = self.loadedRegion\n const graph = self.graph\n if (region && graph) {\n self.setHoveredNode(\n hover && hoverInRegion(hover, region)\n ? nodeForLgvHover({ hover, nodes: graph.nodes })\n : null,\n )\n }\n })\n }),\n )\n\n // Reaction: drop a hover the pointer cannot still be over.\n //\n // A hover is set by a mousemove on the canvas, and here the drawing\n // moves under a STATIONARY cursor on three axes \u2014 the wheel zoom, the\n // toolbar's zoom and fit buttons, and a pan \u2014 none of which fire a\n // pointer event. Left alone the tooltip goes on naming the node that\n // used to be under the cursor, `hoverHighlight` goes on publishing its\n // span, and the paired linear view paints a band over the wrong\n // sequence until the mouse happens to move.\n //\n // This is jbrowse-components' `installClearHoverOnViewportChange`\n // (BaseLinearDisplay), whose three axes are bpPerPx / offsetPx /\n // scrollTop; a graph view's are its own transform. Its rule is worth\n // restating rather than just citing: a sticky canvas gets no\n // mousemove and no mouseleave for any of them. Not imported because\n // it lives in the LGV plugin and reads a containing LGV, and this\n // plugin deliberately takes no runtime dependency on that plugin\n // (see hoverSync/index.tsx).\n //\n // A `reaction`, not an autorun, for the reason stated there: the\n // effect touches hover state, and an autorun would re-enter itself.\n // Selection is untouched \u2014 a click is a choice, and the content\n // moving does not unmake it.\n addDisposer(\n self,\n reaction(\n () => `${self.scale}-${self.translateX}-${self.translateY}`,\n () => {\n self.setHoveredNode(null)\n self.setHoveredEdge(null)\n },\n { name: 'GraphClearHoverOnViewportChange' },\n ),\n )\n\n // Autorun: a zoom, or a pan that leaves the window the last build\n // covered, schedules a debounced rebuild. A pan inside it costs a\n // repaint and nothing else. Skips the first run.\n let firstViewport = true\n addDisposer(\n self,\n autorun(() => {\n const { scale } = self\n const viewport = viewportOf(self)\n if (firstViewport) {\n firstViewport = false\n return\n }\n const built = untracked(() => self.builtViewport)\n if (built?.scale === scale && contains(built.bounds, viewport)) {\n return\n }\n self.scheduleViewportDirty()\n }),\n )\n\n // Autorun: hover/select are draw-time colour overrides, stated whole\n // on every change, so there is nothing to restore and nothing to go\n // stale when a rebuild renumbers the batch. Tracks `geometryVersion`\n // because an upload drops the renderer's edge highlight.\n addDisposer(\n self,\n autorun(() => {\n const b = self.currentRenderingBackend as Renderer | undefined\n const hoveredNode = self.hoveredNode\n const hoveredEdge = self.hoveredEdge\n const selectedNode = self.selectedNode\n dependOn(self.geometryVersion)\n if (b) {\n const nodes = new Map<string, number>()\n if (selectedNode !== null) {\n nodes.set(selectedNode, SELECT_BRIGHTEN)\n }\n if (hoveredNode !== null && hoveredNode !== selectedNode) {\n nodes.set(hoveredNode, HOVER_BRIGHTEN)\n }\n b.setNodeHighlights(nodes)\n b.setEdgeHighlight(hoveredEdge, HOVER_BRIGHTEN)\n self.renderNow()\n }\n }),\n )\n }\n\n // The second argument is a SETUP THUNK, not the callbacks themselves:\n // anything it closes over lives exactly as long as the callbacks that\n // read it, which is what lets a per-backend memo work.\n self.attachRenderingBackend<Renderer>(backend, () => ({\n // Autorun: rebuild geometry when graph data or display options\n // change. scale/translate are untracked so they don't trigger a full\n // rebuild \u2014 only the debounced viewportDirty flag does.\n upload: (b: Renderer) => {\n b.resize(self.width, self.canvasHeight)\n const nodeById = self.nodeById\n if (self.nodePositions && self.graph && nodeById) {\n // The window moved (debounced pan/zoom), or the positions did (a\n // node drag, coalesced to a frame). Both change the drawing;\n // everything below reads them untracked or not at all.\n dependOn(self.viewportDirty, self.positionsVersion)\n const geometryStart = performance.now()\n const viewportBounds = untracked(() =>\n padded(viewportOf(self), VIEWPORT_PANES_BUILT),\n )\n const batch = buildGeometry({\n nodePositions: self.nodePositions,\n graph: self.graph,\n nodeById,\n colorScheme: self.effectiveColorScheme,\n contigThickness: self.contigThickness,\n connectorThickness: self.connectorThickness,\n drawPaths: self.effectiveDrawPaths,\n nodeWidth: self.nodeWidth,\n highlight: self.walkHighlight,\n // Untracked, so a zoom does not eagerly rebuild geometry \u2014 the\n // debounced viewportDirty bump drives the scale-dependent\n // rebuild (flatness, arrow visibility, viewport culling), same\n // as pan.\n axis: untracked(() => self.axisScale),\n linearLayout: self.linearLayout,\n viewportBounds,\n // Where each node sits on the reference and what interval the\n // hue spans. Held against the graph rather than derived here,\n // the same way `deletions` is and for the same reason \u2014 see\n // the `referenceRamp` view.\n referenceRamp: self.referenceRamp,\n deletions: self.deletionEdgeIndexes,\n hiddenEdges: self.hiddenEdgeIndexes,\n // Read tracked above; passed here so the shared edge-curve\n // cache can tell a drag from a pan.\n version: self.positionsVersion,\n })\n b.uploadGeometry(batch)\n self.setGeometryMetrics(\n performance.now() - geometryStart,\n batch.nodeStrokes.length,\n { scale: untracked(() => self.scale), bounds: viewportBounds },\n )\n return true\n }\n // Nothing reached the backend, so the autorun may skip the redraw\n // it would otherwise force. Safe despite the resize above: with no\n // positions there is nothing to draw, which is the same case\n // `render` below returns false for.\n return false\n },\n // Autorun: re-render on pan/zoom/darkMode without rebuilding geometry\n render: (b: Renderer) => {\n if (!self.nodePositions) {\n return false\n }\n // getDpr(), never a bare `devicePixelRatio`: it is capped at\n // MAX_DPR so this canvas costs what every other canvas in the app\n // costs on a 3x display (the square of the ratio, i.e. 9x the\n // pixels of 1x against the 4x everything else pays), and it is the\n // same read `syncCanvasSize` sizes the backing store with \u2014 two\n // call sites reading the global separately can disagree, and then\n // the geometry lands at a different scale from the canvas under it.\n const dpr = getDpr()\n b.updateTransform({\n scaleX: self.scaleX * dpr,\n scaleY: self.scaleY * dpr,\n translateX: self.translateX * dpr,\n translateY: self.translateY * dpr,\n // Handed over rather than read again by the backend: the\n // thicknesses in the vertex buffer are css px and are expanded\n // after this transform, so they need the same ratio the fields\n // above were already multiplied by. See TransformUniform.dpr.\n dpr,\n })\n b.render(self.darkMode ? [0.12, 0.12, 0.12, 1] : [1, 1, 1, 1])\n return true\n },\n }))\n },\n }))\n .actions(self => ({\n // A layout picked from a menu: the mode, then the drawing it makes.\n switchLayout(mode: LayoutModeValue) {\n self.setLayoutMode(mode)\n return self.recomputeLayout()\n },\n retryLoad() {\n if (self.loadedTrackId && self.loadedRegion) {\n void self.reloadSubgraph()\n } else if (self.gfaLocation) {\n void self.loadGFAFromLocation(self.gfaLocation)\n }\n },\n // A declaratively-instantiated view loads itself on attach, from either\n // declarative source: a whole-GFA `gfaLocation`, or the\n // `loadedTrackId`/`loadedRegion` pair the launch menu writes and a\n // reloaded session restores.\n //\n // This has to happen here rather than when the rendering backend starts:\n // the canvas only mounts once `hasGraph` is true, so a view whose graph\n // must be fetched would never fetch it.\n afterAttach() {\n // A restored session that already carries a non-default transform is\n // the user's own view \u2014 mark it so the fit autorun leaves it alone.\n if (!self.isDefaultViewport) {\n self.viewportOwner = 'user'\n }\n // loadGFAFromLocation leaves `gfaLocation` intact, so the source\n // round-trips through a session snapshot.\n if (self.gfaLocation && !self.graph) {\n void self.loadGFAFromLocation(self.gfaLocation)\n }\n void self.refetchIfNeeded()\n },\n }))\n .actions(self => ({\n // Move the linear view already on screen, or open one if there is none,\n // and pair with whichever it was. A view being created gets the session's\n // annotation for the assembly it opens on (see launchTracks), led by the\n // graph's own segments track when the launch is on the reference \u2014 the one\n // assembly that track is configured for.\n //\n // Only a view on the reference is paired with. A node's hover band is on\n // that assembly, so paired with a view on another the band fails that\n // view's assembly check and the reference view's id check, and is drawn\n // nowhere.\n showInLinearView(target: { location: GraphLocation; assembly: string }) {\n const session = getSession(self)\n const onReference = target.assembly === self.loadedRegion?.assemblyName\n const viewId = showInLinearView({\n session,\n location: target.location,\n assembly: target.assembly,\n connectedViewId: self.connectedViewId,\n tracks: launchTracks({\n session,\n assemblyName: target.assembly,\n first:\n self.loadedTrackId && onReference\n ? self.loadedTrackId\n : undefined,\n }),\n })\n if (onReference) {\n self.pairWithLinearView(viewId)\n }\n },\n // Mark the node's reference interval in the linear view beside the graph.\n // Not an action that opens anything: with no view to mark, the menu does\n // not offer it (see nodeLaunchMenuItems).\n highlightInLinearView(target: {\n location: GraphLocation\n assembly: string\n }) {\n highlightInLinearView({\n session: getSession(self),\n location: target.location,\n assembly: target.assembly,\n connectedViewId: self.connectedViewId,\n })\n },\n showSyntenyView(trackId: string) {\n launchSyntenyView({\n session: getSession(self),\n contributors: self.launchableAssemblies,\n trackId,\n graphTrackId: self.loadedTrackId || undefined,\n })\n },\n }))\n .views(self => ({\n // Synteny datasets that could fill the panels of a multi-genome launch.\n // Below two openable contributors there is nothing to compare, so the scan\n // is skipped rather than run and discarded.\n get syntenyLaunchTracks() {\n const samples = self.launchableAssemblies.map(c => c.sample)\n return samples.length >= 2\n ? launchableSyntenyTracks(getSession(self), samples)\n : []\n },\n }))\n .views(self => ({\n // The graph's way out, in the same shared \"Launch view\" submenu every\n // other view contributes to. Until this existed the triangle had two edges:\n // a linear view could open a graph or a synteny view of a locus, and the\n // graph could open nothing at all.\n menuItems(): MenuItem[] {\n const items: MenuItem[] = []\n for (const item of graphLaunchMenuItems({\n contributors: self.launchableAssemblies,\n syntenyTracks: self.syntenyLaunchTracks,\n onShowLinear: target => {\n self.showInLinearView(target)\n },\n onShowSynteny: trackId => {\n self.showSyntenyView(trackId)\n },\n })) {\n pushLaunchViewMenuItem(items, item)\n }\n return items\n },\n }))\n}\n\nexport type GraphGenomeViewModel = ReturnType<\n ReturnType<typeof stateModelFactory>['create']\n>\n", "import { bubbleSegmentIds, classifyBubble, formatBp } from './classifyBubble'\nimport { isBackbone } from '../anchoredNodes'\nimport { svgPath } from '../util/geometry'\n\nimport type { BubbleKind } from './classifyBubble'\nimport type {\n BubbleRoute,\n MinigraphBubble,\n} from '../../MinigraphBubbleAdapter/bubbleLine'\nimport type { Graph, NodeSegment } from '../types'\n\n// A bubble drawn over the graph itself: a wide translucent stroke along the\n// nodes inside it, in layout coordinates, so the same halo follows the nodes\n// through the force layout's loops as through the ordered layout's lenses. The\n// two backbone nodes a bubble hangs between are not inside it, and are left\n// out so neighbouring halos do not touch.\nexport interface BubbleHalo {\n bubble: MinigraphBubble\n kind: BubbleKind\n label: string\n // an SVG path in layout units; a drawing transform puts it on screen\n path: string\n // where the label goes: the highest point of the halo, in layout units\n top: NodeSegment\n members: number\n // the ids of those nodes, so a lifted walk can say which bubbles it enters\n nodeIds: string[]\n // the bubble is the whole drawing, as a popped bubble's own row is: its\n // label still names it, but a halo around everything marks nothing\n whole: boolean\n // each route the walks take through the bubble, the reference's own\n // included, named for the haplotypes that take it; a route with no steps, a\n // deletion, has nowhere to carry a chip\n routes: RouteLabel[]\n}\n\nexport interface RouteLabel {\n route: BubbleRoute\n at: NodeSegment\n text: string\n}\n\nconst WHOLE_FRACTION = 0.9\nconst NAMED_WALKS = 2\n// a SNP's routes are a dot each; chips are for loops a reader can see\nconst MIN_CHIPPED_BP = 1000\n\nexport function bubbleHalos(\n graph: Graph,\n bubbles: MinigraphBubble[],\n positions: Record<string, NodeSegment[]>,\n walkLabel: (name: string) => string = name => name,\n): BubbleHalo[] {\n const byName = new Map(graph.nodes.map(n => [n.name, n]))\n const halos: BubbleHalo[] = []\n for (const bubble of bubbles) {\n const parts: string[] = []\n let top: NodeSegment | undefined\n const nodeIds: string[] = []\n const ends: NodeSegment[] = []\n for (const name of bubbleSegmentIds(bubble)) {\n const node = byName.get(name)\n const line = node && positions[node.id]\n if (!node || !line?.length) {\n continue\n }\n if (\n isBackbone(node) &&\n (node.stable.start < bubble.start || node.stable.start >= bubble.end)\n ) {\n ends.push(line[Math.floor(line.length / 2)]!)\n continue\n }\n nodeIds.push(node.id)\n // a one-point line still needs a segment to stroke as a dot\n parts.push(svgPath(line.length === 1 ? [line[0]!, line[0]!] : line))\n for (const p of line) {\n if (!top || p.y < top.y) {\n top = p\n }\n }\n }\n if (!top) {\n continue\n }\n const anchor = ends.length\n ? {\n x: ends.reduce((s, p) => s + p.x, 0) / ends.length,\n y: ends.reduce((s, p) => s + p.y, 0) / ends.length,\n }\n : top\n // A chip goes on the stretch that is the route's own: among the nodes the\n // fewest other routes share, the point farthest from the bubble's ends.\n // Routes through a repeat array share most of their copies, and the far\n // point of a shared copy would put every chip on one loop.\n const sharing = new Map<string, number>()\n for (const route of bubble.routes ?? []) {\n for (const id of new Set(route.steps)) {\n sharing.set(id, (sharing.get(id) ?? 0) + 1)\n }\n }\n const routes: RouteLabel[] = []\n for (const route of bubble.routes ?? []) {\n if (bubble.longestAlleleLength < MIN_CHIPPED_BP) {\n break\n }\n if (route.steps.length === 0) {\n continue\n }\n const rarest = Math.min(...route.steps.map(id => sharing.get(id)!))\n let at: NodeSegment | undefined\n let far = -1\n for (const id of route.steps) {\n if (sharing.get(id) !== rarest) {\n continue\n }\n for (const p of positions[id] ?? []) {\n const d = Math.hypot(p.x - anchor.x, p.y - anchor.y)\n if (d > far) {\n far = d\n at = p\n }\n }\n }\n if (at) {\n routes.push({ route, at, text: routeText(route, walkLabel) })\n }\n }\n halos.push({\n bubble,\n ...classifyBubble(bubble),\n path: parts.join(''),\n top,\n members: nodeIds.length,\n nodeIds,\n whole: nodeIds.length >= WHOLE_FRACTION * graph.nodes.length,\n routes,\n })\n }\n return halos\n}\n\nfunction routeText(route: BubbleRoute, walkLabel: (name: string) => string) {\n const names = [...new Set(route.walks.map(walkLabel))].sort()\n const shown = names.slice(0, NAMED_WALKS).join(', ')\n const more =\n names.length > NAMED_WALKS ? ` +${names.length - NAMED_WALKS}` : ''\n return `${shown}${more} \u00B7 ${formatBp(route.bp)}`\n}\n", "import { SATURATED_PATH_COUNT } from '../../MinigraphBubbleAdapter/bubbleLine'\nimport { isBackbone } from '../anchoredNodes'\nimport { referenceOrder } from '../layout/orderedLayout'\n\nimport type {\n BubbleRoute,\n MinigraphBubble,\n} from '../../MinigraphBubbleAdapter/bubbleLine'\nimport type { Graph, GraphNode } from '../types'\n\n// Bubbles from the graph alone, off the reference order the ordered layout is\n// also drawn from (referenceOrder): a backbone node no link reaches across is a\n// bubble boundary, and whatever sorts between two consecutive boundaries is a\n// bubble. Needs a reference, not an index, so it works on a GBZ cut, a pggb\n// file and the inside of a popped superbubble alike.\n//\n// Read off the order itself, not off the layers the drawing puts it in. A\n// layer is where a node is drawn, and one nothing leads into is drawn in the\n// first layer wherever it sorts. That is every node a walk enters the cut at,\n// the ordinary shape of a GBZ cut whose context stops short of a haplotype's\n// walk: its link into the backbone then crossed every layer between, and the\n// bubbles on either side of those boundaries fused into one.\n//\n// Route statistics come from the walks when the graph has paths (exact, and\n// immune to a repeat array whose copies the order cannot direct), and from a\n// DP over the ordered DAG otherwise. A reversed stretch comes out as one\n// bubble whose interior runs against the reference, not as an inversion flag.\n\ninterface Routes {\n min: number\n max: number\n n: number\n}\n\nexport function bubblesFromGraph(graph: Graph): MinigraphBubble[] {\n if (!graph.nodes.some(isBackbone)) {\n return []\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const order = referenceOrder(graph)\n const indexOf = new Map(order.map((id, i) => [id, i]))\n // how many links reach across each place in the order, as a running sum of\n // where each one starts and stops\n const reach = new Int32Array(order.length + 1)\n // successors as a set: a repeat's back edge is the same DAG edge as its\n // forward one, and counting it twice doubles every route through the copy\n const succ = new Map<string, Set<string>>()\n const linked = new Set<string>()\n for (const e of graph.edges) {\n const a = indexOf.get(e.from)\n const b = indexOf.get(e.to)\n if (a === undefined || b === undefined || a === b) {\n continue\n }\n linked.add(`${e.from}>${e.to}`).add(`${e.to}>${e.from}`)\n const [lo, hi] = a < b ? [a, b] : [b, a]\n reach[lo + 1]!++\n reach[hi]!--\n const [from, to] = a < b ? [e.from, e.to] : [e.to, e.from]\n ;(succ.get(from) ?? succ.set(from, new Set()).get(from)!).add(to)\n }\n for (let i = 1; i < reach.length; i++) {\n reach[i]! += reach[i - 1]!\n }\n // Boundaries are nodes: every backbone node no link reaches across, plus the\n // window's first and last backbone node whatever reaches across them, since a\n // repeat array whose copies reach the last backbone node would otherwise\n // never close.\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort((a, b) => a.stable.start - b.stable.start)\n const ends = new Set([backbone[0]!.id, backbone.at(-1)!.id])\n const boundaries = backbone\n .map(node => [node, indexOf.get(node.id)!] as const)\n .filter(([node, i]) => ends.has(node.id) || reach[i] === 0)\n .sort(([a, ia], [b, ib]) => ia - ib || a.stable.start - b.stable.start)\n const walkIndex = graph.paths?.map(p => {\n const at = new Map<string, number>()\n p.nodeIds.forEach((id, i) => {\n if (!at.has(id)) {\n at.set(id, i)\n }\n })\n return at\n })\n\n const bubbles: MinigraphBubble[] = []\n for (let i = 0; i + 1 < boundaries.length; i++) {\n const [start, i0] = boundaries[i]!\n const [end, i1] = boundaries[i + 1]!\n const interior = order.slice(i0 + 1, i1)\n if (interior.length === 0) {\n continue\n }\n\n const best = new Map<string, Routes>([[start.id, { min: 0, max: 0, n: 1 }]])\n for (const id of [start.id, ...interior]) {\n const cur = best.get(id)\n if (!cur) {\n continue\n }\n for (const t of succ.get(id) ?? []) {\n const ti = indexOf.get(t)!\n if (ti > i1) {\n continue\n }\n const add = t === end.id ? 0 : byId.get(t)!.length\n const prev = best.get(t)\n best.set(t, {\n min: Math.min(prev?.min ?? Infinity, cur.min + add),\n max: Math.max(prev?.max ?? -Infinity, cur.max + add),\n n: Math.min(SATURATED_PATH_COUNT, cur.n + (prev?.n ?? 0)),\n })\n }\n }\n const walked =\n walkIndex && walkRoutes(graph, byId, walkIndex, start.id, end.id)\n const crossed = walked?.n ? walked : undefined\n const routes = crossed ?? best.get(end.id) ?? { min: 0, max: 0, n: 0 }\n // A walk that enters the bubble and never reaches its other end left the\n // cut: a GBZ cut of a repeat array at 1 kb of context splits each\n // haplotype's walk into pieces, and the routes seen are then a floor.\n const walksLeave = walked !== undefined && walked.left > 0\n\n const refStart = start.stable.start + start.length\n const refEnd = end.stable.start\n // The reference route exists in the cut only if every backbone pair inside\n // is linked; a flank the hop reached from an allele is not.\n const chain = [\n start.id,\n ...interior.filter(id => isBackbone(byId.get(id)!)),\n end.id,\n ]\n const chainBroken = chain.some(\n (id, k) => k > 0 && !linked.has(`${chain[k - 1]}>${id}`),\n )\n const partial = chainBroken || walksLeave\n const fallback = chainBroken && !crossed\n bubbles.push({\n refName: start.stable.refName,\n start: refStart,\n end: refEnd,\n segmentCount: interior.length + 2,\n pathCount: routes.n,\n // a real inversion test needs each walk's direction relative to the\n // reference; the first visit's strand is just whichever path anchored the\n // node\n inversion: false,\n shortestAlleleLength: fallback ? refEnd - refStart : routes.min,\n longestAlleleLength: fallback ? refEnd - refStart : routes.max,\n segments: [start, ...interior.map(id => byId.get(id)!), end]\n .map(n => n.name)\n .join(','),\n shortestAllele: undefined,\n longestAllele: undefined,\n partial,\n routes: crossed?.routes,\n })\n }\n return bubbles\n}\n\n// For every walk that passes both boundary nodes, the bp between them and the\n// step sequence, so routes are distinct sequences and lengths are the true\n// haplotype lengths. `left` counts the walks that pass one boundary and end\n// before the other.\n//\n// `byId` is the caller's: this runs once per bubble, and a map of every node\n// built here made the whole pass quadratic, seven seconds at 15k nodes.\nfunction walkRoutes(\n graph: Graph,\n byId: Map<string, GraphNode>,\n walkIndex: Map<string, number>[],\n startId: string,\n endId: string,\n): (Routes & { left: number; routes: BubbleRoute[] }) | undefined {\n const seen = new Map<string, BubbleRoute>()\n let min = Infinity\n let max = -Infinity\n let left = 0\n graph.paths!.forEach((p, k) => {\n const i0 = walkIndex[k]!.get(startId)\n const i1 = walkIndex[k]!.get(endId)\n if (i0 === undefined || i1 === undefined) {\n if (i0 !== undefined || i1 !== undefined) {\n left++\n }\n return\n }\n // Start to end, whichever way the walk crosses: a contig on the reverse\n // strand takes the same route, and read end-first it keyed as a second one.\n const steps = p.nodeIds.slice(Math.min(i0, i1) + 1, Math.max(i0, i1))\n if (i1 < i0) {\n steps.reverse()\n }\n let bp = 0\n for (const id of steps) {\n bp += byId.get(id)?.length ?? 0\n }\n const key = steps.join(',')\n const route = seen.get(key) ?? { steps, bp, walks: [] }\n route.walks.push(p.name)\n seen.set(key, route)\n min = Math.min(min, bp)\n max = Math.max(max, bp)\n })\n return seen.size || left\n ? { min, max, n: seen.size, left, routes: [...seen.values()] }\n : undefined\n}\n", "import type { Graph } from '../types'\n\n// The graph inside one bubble: the segments the bubble row names, with the\n// links among them. The row's list includes the two backbone segments the\n// bubble hangs between, so the cut keeps its anchors and every layout that\n// needs a backbone still has one. A walk keeps the steps it takes inside the\n// bubble, so the bubbles derived inside it still read their routes and\n// lengths off the haplotypes, and carriage still sets node width.\nexport function bubbleSubgraph(graph: Graph, segmentIds: string[]): Graph {\n const keep = new Set(segmentIds)\n const nodes = graph.nodes.filter(n => keep.has(n.name))\n const ids = new Set(nodes.map(n => n.id))\n const paths = graph.paths\n ?.map(p => ({ ...p, nodeIds: p.nodeIds.filter(id => ids.has(id)) }))\n .filter(p => p.nodeIds.length > 0)\n return {\n name: graph.name,\n nodes,\n edges: graph.edges.filter(e => ids.has(e.from) && ids.has(e.to)),\n ...(paths?.length ? { paths } : {}),\n anchorPaths: graph.anchorPaths,\n pathVisits: graph.pathVisits,\n anchoredBy: graph.anchoredBy,\n referencePath: graph.referencePath,\n }\n}\n", "import type { Feature } from '@jbrowse/core/util'\n\n// A gene as the pins need it: its name, its span on the reference, and the\n// exons of all its transcripts merged into one set of intervals.\nexport interface GeneModel {\n name: string\n refName: string\n start: number\n end: number\n strand: number\n exons: { start: number; end: number }[]\n}\n\n// The adapters whose tracks hold gene models, in the order the session's\n// tracks are searched when no track is named.\nexport const GENE_ADAPTER_TYPES = new Set([\n 'Gff3TabixAdapter',\n 'Gff3Adapter',\n 'GtfTabixAdapter',\n 'GtfAdapter',\n 'BigBedAdapter',\n 'NCListAdapter',\n])\n\nconst GENE_TRACK_HINT = /gene|refseq|gencode|ensembl|annotation/i\n\n// Which of the session's tracks to read genes from: the named one, else the\n// first gene-bearing track on the assembly whose name says it is annotation,\n// else the first gene-bearing track at all.\nexport function pickGeneTrack<\n T extends { trackId: string; name?: string; adapterType: string },\n>(tracks: T[], named: string) {\n if (named) {\n return tracks.find(t => t.trackId === named)\n }\n const candidates = tracks.filter(t => GENE_ADAPTER_TYPES.has(t.adapterType))\n return (\n candidates.find(t =>\n GENE_TRACK_HINT.test(`${t.trackId} ${t.name ?? ''}`),\n ) ?? candidates[0]\n )\n}\n\ntype FeatureLike = Feature | Record<string, unknown>\n\nfunction field(f: FeatureLike, name: string): unknown {\n return typeof (f as Feature).get === 'function'\n ? (f as Feature).get(name)\n : (f as Record<string, unknown>)[name]\n}\n\nfunction exonsOf(f: FeatureLike, into: { start: number; end: number }[]) {\n const type = field(f, 'type')\n if (type === 'exon') {\n into.push({\n start: field(f, 'start') as number,\n end: field(f, 'end') as number,\n })\n }\n const children = field(f, 'subfeatures') as FeatureLike[] | undefined\n for (const child of children ?? []) {\n exonsOf(child, into)\n }\n}\n\nfunction merged(intervals: { start: number; end: number }[]) {\n const sorted = [...intervals].sort((a, b) => a.start - b.start)\n const out: { start: number; end: number }[] = []\n for (const iv of sorted) {\n const last = out.at(-1)\n if (last && iv.start <= last.end) {\n last.end = Math.max(last.end, iv.end)\n } else {\n out.push({ ...iv })\n }\n }\n return out\n}\n\n// Genes from a track's features: every top-level feature, named by the first\n// of gene_name, name and id it carries, with the exons found anywhere under it\n// merged. A feature with no exons is one exon, its whole span.\nexport function geneModelsFrom(features: FeatureLike[]): GeneModel[] {\n const genes: GeneModel[] = []\n for (const f of features) {\n const name =\n (field(f, 'gene_name') as string | undefined) ??\n (field(f, 'name') as string | undefined) ??\n (field(f, 'id') as string | undefined)\n const start = field(f, 'start') as number\n const end = field(f, 'end') as number\n if (!name || !(end > start)) {\n continue\n }\n const exons: { start: number; end: number }[] = []\n exonsOf(f, exons)\n genes.push({\n name,\n refName: field(f, 'refName') as string,\n start,\n end,\n strand: (field(f, 'strand') as number | undefined) ?? 0,\n exons: exons.length ? merged(exons) : [{ start, end }],\n })\n }\n return genes\n}\n", "import { firstNodeAtOrAfter, isBackbone } from '../anchoredNodes'\nimport { polylineSlice } from '../layout/mergeRuns'\nimport { svgPath } from '../util/geometry'\n\nimport type { GeneModel } from './geneFeatures'\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph, NodeSegment } from '../types'\n\n// A gene drawn onto the graph: its exons as stretches of the backbone nodes\n// that carry them, in layout units, and one point on the backbone to pin its\n// name to. Only the backbone has coordinates, so an allele shows no exon even\n// where a haplotype's own annotation would put one.\nexport interface GenePin {\n gene: GeneModel\n // an SVG path of the exon stretches, in layout units\n exons: string\n // where the name goes: the backbone point at the gene's midpoint, or the\n // nearest backbone point inside the gene where the midpoint is not in the cut\n at: NodeSegment\n // how much of the gene's span the cut's backbone covers, so a label can say\n // when a gene runs off the cut\n covered: number\n}\n\n// A backbone node names its sequence the way the graph does, `GRCh38#0#chr6`,\n// and a gene the way the assembly does, `chr6`; the contig is the part they\n// share.\nfunction contig(name: string) {\n return name.split('#').at(-1)!\n}\n\n// The backbone of one contig in offset order, for finding the nodes a gene\n// lies over without reading the rest. `reach` is the longest node: a node over\n// a gene's start begins no further before it than that.\ninterface ContigBackbone {\n nodes: AnchoredNode[]\n reach: number\n}\n\nexport function genePins(\n graph: Graph,\n genes: GeneModel[],\n positions: Record<string, NodeSegment[]>,\n): GenePin[] {\n // Per contig and sorted once, so a gene reads the few nodes it lies over.\n // Every gene used to read every backbone node, splitting both names each\n // time, on each frame of a node drag: 199 ms for 100 genes over 15k nodes,\n // now 12.\n const byContig = new Map<string, ContigBackbone>()\n const contigOf = new Map<string, string>()\n for (const node of graph.nodes) {\n if (isBackbone(node) && positions[node.id]?.length) {\n const { refName } = node.stable\n const name =\n contigOf.get(refName) ??\n contigOf.set(refName, contig(refName)).get(refName)!\n const entry =\n byContig.get(name) ??\n byContig.set(name, { nodes: [], reach: 0 }).get(name)!\n entry.nodes.push(node)\n entry.reach = Math.max(entry.reach, node.length)\n }\n }\n for (const { nodes } of byContig.values()) {\n nodes.sort((a, b) => a.stable.start - b.stable.start)\n }\n const pins: GenePin[] = []\n for (const gene of genes) {\n const backbone = byContig.get(contig(gene.refName))\n if (!backbone) {\n continue\n }\n const parts: string[] = []\n let at: NodeSegment | undefined\n let atDistance = Infinity\n let covered = 0\n const mid = (gene.start + gene.end) / 2\n const { nodes, reach } = backbone\n for (\n let i = firstNodeAtOrAfter(nodes, gene.start - reach);\n i < nodes.length && nodes[i]!.stable.start < gene.end;\n i++\n ) {\n const node = nodes[i]!\n const nodeStart = node.stable.start\n const nodeEnd = nodeStart + node.length\n if (nodeEnd <= gene.start) {\n continue\n }\n const line = positions[node.id]!\n covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start)\n for (const exon of gene.exons) {\n const a = Math.max(exon.start, nodeStart)\n const b = Math.min(exon.end, nodeEnd)\n if (b <= a) {\n continue\n }\n const stretch = polylineSlice(\n line,\n (a - nodeStart) / node.length,\n (b - nodeStart) / node.length,\n )\n if (stretch.length === 1) {\n stretch.push({ ...stretch[0]! })\n }\n parts.push(svgPath(stretch))\n }\n const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd)\n const distance = Math.abs(pinBp - mid)\n if (distance < atDistance) {\n atDistance = distance\n const [p] = polylineSlice(\n line,\n (pinBp - nodeStart) / node.length,\n (pinBp - nodeStart) / node.length,\n )\n at = p\n }\n }\n if (at) {\n pins.push({\n gene,\n exons: parts.join(''),\n at,\n covered: covered / (gene.end - gene.start),\n })\n }\n }\n return pins\n}\n", "// Minimal GFA (Graphical Fragment Assembly) text parser shared by the graph\n// pangenome plugins (GraphGenomeView, TubeMapView). Handles GFA1/GFA2 segment,\n// link, edge, path and walk records plus header tags.\n\n// Only the first two colons delimit a tag; everything after them is the value.\n// A Z value may contain colons of its own, and rGFA stable names routinely do\n// (`SN:Z:chr1:1-1000` from a sliced reference), where splitting on every colon\n// silently truncates the name to `chr1` and mis-anchors the whole layout.\nfunction parseTag(tag: string, tags: Record<string, string | number>) {\n const nameEnd = tag.indexOf(':')\n const typeEnd = tag.indexOf(':', nameEnd + 1)\n if (nameEnd !== -1 && typeEnd !== -1) {\n const name = tag.slice(0, nameEnd)\n const type = tag.slice(nameEnd + 1, typeEnd)\n const val = tag.slice(typeEnd + 1)\n if (type === 'i' || type === 'f') {\n tags[name] = +val\n } else if (type === 'Z') {\n tags[name] = val\n }\n }\n}\n\nfunction parseTags(fields: string[]) {\n const tags: Record<string, string | number> = {}\n for (const field of fields) {\n parseTag(field, tags)\n }\n return tags\n}\n\nexport interface GFANode {\n id: string\n length: number\n sequence: string\n tags: Record<string, string | number>\n}\n\n// rGFA (minigraph) anchors every segment to a reference coordinate: SN is the\n// stable sequence name, SO the offset on it, and SR the stable rank, where 0 is\n// the reference backbone and higher ranks are the sequence that diverges from\n// it. Plain GFA (pggb, odgi, Minigraph-Cactus) carries none of these, which is\n// why coordinates there exist only inside the P/W lines.\nexport interface StableCoordinate {\n refName: string\n start: number\n rank: number\n // How the anchoring path reads this segment. Only path-derived coordinates\n // set it: an rGFA segment is by construction forward on the sequence SN\n // names, so there is nothing there for it to say.\n strand?: '+' | '-'\n}\n\nexport function stableCoordinate(node: GFANode): StableCoordinate | undefined {\n const { SN, SO, SR } = node.tags\n return typeof SN === 'string' &&\n typeof SO === 'number' &&\n typeof SR === 'number'\n ? { refName: SN, start: SO, rank: SR }\n : undefined\n}\n\nexport interface GFALink {\n source: string\n target: string\n strand1?: string\n strand2?: string\n cigar: string\n tags: Record<string, string | number>\n}\n\nexport interface GFAPath {\n name: string\n path: string\n rest: string[]\n}\n\nexport interface GFAWalkSegment {\n id: string\n strand: string\n}\n\nexport interface GFAWalk {\n sample: string\n haplotype: number\n contig: string\n start: number\n end: number\n segments: GFAWalkSegment[]\n tags: Record<string, string | number>\n}\n\nconst GT = 62 // '>'\nconst LT = 60 // '<'\n\n// A W record's body is `>s1<s2>s3\u2026`, and on a Minigraph-Cactus graph one record\n// is the whole walk of one haplotype across a chromosome \u2014 hundreds of\n// thousands of steps in a single field.\n//\n// So the ids are cut out by index rather than accumulated a character at a\n// time. `current += ch` builds and discards one string per character of every\n// id, and it dominated the parse: a file whose single W record walks 200k steps\n// went from ~55 ms to ~24 ms by slicing instead. `pnpm bench` keeps the number.\nfunction parseWalkBody(body: string) {\n const segments: GFAWalkSegment[] = []\n let strand = '+'\n let start = 0\n // An empty run \u2014 a leading delimiter, or two in a row \u2014 names no segment.\n function push(end: number) {\n if (end > start) {\n segments.push({ id: body.slice(start, end), strand })\n }\n }\n for (let i = 0; i < body.length; i++) {\n const ch = body.charCodeAt(i)\n if (ch === GT || ch === LT) {\n push(i)\n strand = ch === GT ? '+' : '-'\n start = i + 1\n }\n }\n push(body.length)\n return segments\n}\n\nexport interface GFAGraph {\n nodes: GFANode[]\n links: GFALink[]\n paths: GFAPath[]\n walks: GFAWalk[]\n header: Record<string, string | number>[]\n id: string\n}\n\nexport function parseGFA(file: string) {\n const graph: GFAGraph = {\n nodes: [],\n links: [],\n paths: [],\n walks: [],\n header: [],\n id: '',\n }\n\n // \\r is stripped rather than split on, so a CRLF file does not leave it on\n // whichever field happens to be last: a trailing Z tag would carry it into a\n // stable name and a trailing cigar into the CIGAR string.\n //\n // Stripped per line inside the loop rather than with a .map() over the split,\n // which would hold a second copy of every line in the file at once.\n for (const rawLine of file.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine\n if (line.startsWith('H')) {\n const [, ...rest] = line.split('\\t')\n graph.header.push(parseTags(rest))\n } else if (line.startsWith('S')) {\n const [, name, ...rest] = line.split('\\t')\n let len: number\n let seq: string\n let tagfields: string[]\n let gfa1 = false\n // A truncated record has no field to read; `*` is what the spec writes for\n // a segment whose sequence is absent, so treating a missing one the same\n // way yields a 0 bp segment instead of throwing partway through the file.\n const first = rest[0] ?? '*'\n // GFA2 puts an integer length in this field, GFA1 the sequence. Testing\n // the digits rather than the coerced number keeps a GFA2 `S sid 0 *` from\n // being read as a GFA1 sequence of \"0\".\n if (/^\\d+$/.test(first)) {\n len = +first\n seq = rest[1] ?? '*'\n tagfields = rest.slice(2)\n } else {\n gfa1 = true\n seq = first\n // `*` is GFA1's \"no sequence here\"; its length is whatever LN says\n // below, and 0 if the file states none. Counting the placeholder's own\n // character instead reports every such segment as 1 bp.\n len = seq === '*' ? 0 : seq.length\n tagfields = rest.slice(1)\n }\n const tags = parseTags(tagfields)\n if (gfa1 && 'LN' in tags) {\n len = +tags.LN\n }\n graph.nodes.push({ id: name!, length: len, sequence: seq, tags })\n } else if (line.startsWith('E')) {\n // From here down, a record cut off before the fields that make it a\n // link, walk or path states nothing drawable and is dropped; the file\n // around it still reads. A link missing only its overlap is kept.\n const [, , source, target, , , , , cigar, ...rest] = line.split('\\t')\n if (source && target) {\n graph.links.push({\n source: source.slice(0, -1),\n target: target.slice(0, -1),\n strand1: source.at(-1),\n strand2: target.at(-1),\n cigar: cigar ?? '*',\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('L')) {\n const [, source, strand1, target, strand2, cigar, ...rest] =\n line.split('\\t')\n if (source && target && strand2) {\n graph.links.push({\n source,\n target,\n strand1,\n strand2,\n cigar: cigar ?? '*',\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('W')) {\n const [, sample, hap, contig, start, end, body, ...rest] =\n line.split('\\t')\n if (sample && hap && contig && start && end && body) {\n graph.walks.push({\n sample,\n haplotype: +hap,\n contig,\n start: start === '*' ? -1 : +start,\n end: end === '*' ? -1 : +end,\n segments: parseWalkBody(body),\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('P')) {\n const [, name, path, ...rest] = line.split('\\t')\n if (name && path) {\n graph.paths.push({ name, path, rest })\n }\n }\n }\n return graph\n}\n", "import { stableCoordinate } from '../../gfa-core/index'\nimport { pathOrigin, surveyPaths } from '../pathAnchoring'\n\nimport type { GFAGraph, GFANode } from '../../gfa-core/index'\nimport type { PathSteps } from '../pathAnchoring'\nimport type { Graph, GraphEdge, GraphNode, GraphPath } from '../types'\n\n// Single pass over every reference a GFA makes to a segment, collecting the two\n// things the drawn graph needs from them.\n//\n// `canonical` is the one orientation each segment is drawn in, which is what\n// Bandage calls single-node mode. A segment is the same sequence read either\n// way (`L 1 + 2 +` is the same edge as `L 2 - 1 -`), so `1+` and `1-` are one\n// node, not two: drawing both doubles every segment, and since the L lines only\n// wire one of the pair, the other copy has no edges at all and scatters loose\n// across the layout. Links carry the topology, so their orientation wins; a\n// segment only paths mention takes the orientation they walk it in.\n//\n// `traversals` is how many paths cross each segment. Assembly GFAs carry read\n// depth in a dp/RC/FC/KC tag, but a pangenome GFA (odgi, vg) carries none \u2014\n// there the meaningful depth is how many samples go through the segment, i.e.\n// core versus accessory sequence.\nfunction surveySegments(gfaGraph: GFAGraph) {\n const canonical = new Map<string, '+' | '-'>()\n const traversals = new Map<string, number>()\n function claim(id: string, strand: '+' | '-') {\n if (!canonical.has(id)) {\n canonical.set(id, strand)\n }\n }\n function traverse(id: string, strand: '+' | '-') {\n claim(id, strand)\n traversals.set(id, (traversals.get(id) ?? 0) + 1)\n }\n for (const link of gfaGraph.links) {\n claim(link.source, link.strand1 === '-' ? '-' : '+')\n claim(link.target, link.strand2 === '-' ? '-' : '+')\n }\n for (const gfaPath of gfaGraph.paths) {\n for (const segment of gfaPath.path.split(',')) {\n traverse(segment.slice(0, -1), segment.endsWith('-') ? '-' : '+')\n }\n }\n for (const walk of gfaGraph.walks) {\n for (const seg of walk.segments) {\n traverse(seg.id, seg.strand === '-' ? '-' : '+')\n }\n }\n return { canonical, traversals }\n}\n\n// P and W state the same thing in different shapes: a P body is a comma-joined\n// `<id><strand>` list whose start offset lives in the record's *name*, a W\n// record arrives already split into steps and states its start as a field.\n// Normalized here so the coordinate walk sees one kind of path.\nfunction anchorablePaths(gfaGraph: GFAGraph): PathSteps[] {\n return [\n ...gfaGraph.paths.map(p => ({\n ...pathOrigin(p.name),\n steps: p.path.split(',').map(segment => ({\n id: segment.slice(0, -1),\n strand: segment.endsWith('-') ? ('-' as const) : ('+' as const),\n })),\n })),\n ...gfaGraph.walks.map(w => ({\n name: `${w.sample}#${w.haplotype}#${w.contig}`,\n // `*` parses to -1, meaning the record declines to say; 0 is then the\n // only offset that can be assumed, and it makes the walk self-relative\n // rather than wrong\n start: w.start === -1 ? 0 : w.start,\n steps: w.segments.map(s => ({\n id: s.id,\n strand: s.strand === '-' ? ('-' as const) : ('+' as const),\n })),\n })),\n ]\n}\n\n// Carriage stated as a tag rather than derived from a walk. `RgfaTabixAdapter`\n// synthesizes its S-lines from a segs.bed whose sixth column is a list of GFA\n// tags, and `scripts/pggb_gfa_to_bed.py` in jbrowse-components puts every\n// haplotype that visits a segment there as `SM:Z:K12.1,Sakai.1`. That is the\n// only way carriage reaches an INDEXED graph: the cut has no P or W lines to\n// walk, so `pathAnchoring` has no visits to derive it from.\n//\n// It does not compete with that walk. `pathAnchoring.anchorNode` rebuilds\n// `samples` from the visits whenever there are any, so a graph loaded as a file\n// keeps the authoritative path-derived set and this is what a tabix cut falls\n// back to.\nfunction carriedSamples(gfaNode: GFANode) {\n const sm = gfaNode.tags.SM\n if (typeof sm !== 'string') {\n return undefined\n }\n const samples = sm\n .split(',')\n .map(s => s.trim())\n .filter(Boolean)\n return samples.length > 0 ? samples : undefined\n}\n\nconst positive = (v: unknown) =>\n typeof v === 'number' && v > 0 ? v : undefined\n\n// The depth a segment states, or undefined. DP is a depth in either case it is\n// written. RC, FC and KC are read, fragment and k-mer counts, so depth is the\n// count over the length: taken raw, a 100 kb node at 5x outweighed a 1 kb node\n// at 50x tenfold in every width drawn from it.\nfunction statedDepth({ tags, length }: GFANode) {\n const count = positive(tags.RC) ?? positive(tags.FC) ?? positive(tags.KC)\n return (\n positive(tags.dp) ??\n positive(tags.DP) ??\n (count !== undefined && length > 0 ? count / length : undefined)\n )\n}\n\nfunction makeNode(\n gfaNode: GFANode,\n strand: '+' | '-',\n depth: number,\n): GraphNode {\n return {\n id: `${gfaNode.id}${strand}`,\n name: gfaNode.id,\n length: gfaNode.length,\n depth,\n stable: stableCoordinate(gfaNode),\n samples: carriedSamples(gfaNode),\n }\n}\n\nexport function convertGFAToGraph(gfaGraph: GFAGraph, name = 'Imported GFA') {\n const nodes: GraphNode[] = []\n const edges: GraphEdge[] = []\n\n const { canonical, traversals } = surveySegments(gfaGraph)\n // segments no link, path, or walk mentions are drawn forward-strand\n const nodeId = (segmentId: string) =>\n `${segmentId}${canonical.get(segmentId) ?? '+'}`\n\n for (const gfaNode of gfaGraph.nodes) {\n const depth =\n statedDepth(gfaNode) ?? Math.max(traversals.get(gfaNode.id) ?? 1, 1)\n nodes.push(makeNode(gfaNode, canonical.get(gfaNode.id) ?? '+', depth))\n }\n\n for (const link of gfaGraph.links) {\n edges.push({\n from: nodeId(link.source),\n to: nodeId(link.target),\n fromStrand: link.strand1 === '-' ? '-' : '+',\n toStrand: link.strand2 === '-' ? '-' : '+',\n })\n }\n\n const paths: GraphPath[] = []\n const edgeToPathsMap = new Map<string, Set<string>>()\n\n // A path that walks the graph on the reverse strand visits an edge's nodes in\n // the opposite order to the L line that declared it, so the key is unordered.\n const edgeKey = (a: string, b: string) => [a, b].sort().join('--')\n\n function recordPathEdges(nodeIds: string[], name: string) {\n for (let i = 0; i < nodeIds.length - 1; i++) {\n const key = edgeKey(nodeIds[i]!, nodeIds[i + 1]!)\n if (!edgeToPathsMap.has(key)) {\n edgeToPathsMap.set(key, new Set())\n }\n edgeToPathsMap.get(key)!.add(name)\n }\n }\n\n for (const gfaPath of gfaGraph.paths) {\n // path segments arrive as `<id><strand>`; the node they land on is the\n // segment's canonical orientation, whichever way this path reads it\n const nodeIds = gfaPath.path\n .split(',')\n .map(segment => nodeId(segment.slice(0, -1)))\n paths.push({ name: gfaPath.name, nodeIds } satisfies GraphPath)\n recordPathEdges(nodeIds, gfaPath.name)\n }\n\n for (const walk of gfaGraph.walks) {\n const nodeIds = walk.segments.map(seg => nodeId(seg.id))\n // The full PanSN name, contig included, which is also what `anchorablePaths`\n // above calls the same record. Dropping the contig made a path's name an\n // ASSEMBLY's name, and one assembly walks one W record per contig: two walks\n // of `HG00438#1` then shared an identity, so `edgeToPathsMap` merged them,\n // `pathColors` (keyed by name) kept only the last colour written while the\n // node stripes kept the first, and the key drew one haplotype twice in two\n // colours. That is the ordinary Minigraph-Cactus shape, not a corner case.\n //\n // The legend narrows it back: `labelTiers` tries sample, then sample and\n // haplotype, then the whole name, so a graph with one contig per haplotype\n // is labelled exactly as before and only a colliding one widens.\n const name = `${walk.sample}#${walk.haplotype}#${walk.contig}`\n paths.push({\n name,\n nodeIds,\n sample: walk.sample,\n haplotype: walk.haplotype,\n contig: walk.contig,\n } satisfies GraphPath)\n recordPathEdges(nodeIds, name)\n }\n\n for (const edge of edges) {\n const pathIds = edgeToPathsMap.get(edgeKey(edge.from, edge.to))\n if (pathIds && pathIds.size > 0) {\n edge.pathIds = [...pathIds]\n }\n }\n\n // Only walked when the file has paths to walk, so an rGFA \u2014 which has none at\n // all \u2014 pays nothing for it. `lengthOf` reads the segment table rather than\n // the nodes just built, because a path may name a segment no S line declared.\n const lengths = new Map(gfaGraph.nodes.map(n => [n.id, n.length]))\n const anchoring =\n gfaGraph.paths.length > 0 || gfaGraph.walks.length > 0\n ? surveyPaths(anchorablePaths(gfaGraph), id => lengths.get(id) ?? 0)\n : undefined\n\n return {\n name,\n nodes,\n edges,\n paths: paths.length > 0 ? paths : undefined,\n anchorPaths: anchoring?.anchorPaths,\n pathVisits: anchoring?.pathVisits,\n // rGFA's own tags win wherever they are present; pathAnchoring only ever\n // fills in for a graph that carries none.\n anchoredBy: nodes.some(n => n.stable) ? 'tags' : undefined,\n } satisfies Graph\n}\n", "import type { SubgraphRegion } from '../launchSubgraph/launchSubgraphView'\n\n// The members of a LinearGenomeView a hosted pane reads and drives.\n// Structural, because this plugin takes no runtime dependency on the LGV\n// plugin (see hoverSync/index.tsx).\nexport interface HostBlock {\n refName: string\n assemblyName: string\n start: number\n end: number\n offsetPx: number\n reversed?: boolean\n displayedRegionIndex?: number\n}\n\nexport interface LinearHost {\n id: string\n initialized: boolean\n bpPerPx: number\n offsetPx: number\n width: number\n displayedRegions: readonly { start: number; end: number }[]\n dynamicBlocks: { contentBlocks: readonly HostBlock[] }\n coarseDynamicBlocks: readonly HostBlock[]\n horizontalScroll: (distance: number) => number\n zoomTo: (bpPerPx: number, offset?: number) => number\n}\n\nexport function isLinearHost(view: unknown): view is LinearHost {\n if (typeof view !== 'object' || view === null) {\n return false\n }\n const v = view as Record<string, unknown>\n return (\n typeof v.horizontalScroll === 'function' &&\n typeof v.zoomTo === 'function' &&\n 'coarseDynamicBlocks' in v &&\n 'dynamicBlocks' in v\n )\n}\n\n// launchSubgraphView's widestBlock, restated rather than imported: that module\n// imports the model, which imports this.\nfunction widest(blocks: readonly HostBlock[]) {\n return blocks.reduce<HostBlock | undefined>(\n (best, b) => (best && best.end - best.start >= b.end - b.start ? best : b),\n undefined,\n )\n}\n\nexport interface HostWindow {\n refName: string\n assemblyName: string\n start: number\n end: number\n bpPerPx: number\n // the view's width in bp, which the margin is sized by: a block at a contig\n // end is clipped, the zoom is not\n span: number\n regionStart: number\n regionEnd: number\n}\n\n// x is reference bp on an anchored layout, so the graph shows what the linear\n// view shows when one bp is as many px in both and the cut's refName sits at\n// the same screen x. Undefined when the linear view shows no block of that\n// refName, which is a re-cut's job rather than the frame's.\nexport function hostFrame(view: LinearHost, region: SubgraphRegion) {\n const block = widest(\n view.dynamicBlocks.contentBlocks.filter(\n b =>\n b.refName === region.refName && b.assemblyName === region.assemblyName,\n ),\n )\n const { bpPerPx } = view\n return block && bpPerPx > 0\n ? {\n scale: 1 / bpPerPx,\n translateX: block.offsetPx - view.offsetPx - block.start / bpPerPx,\n }\n : undefined\n}\n\n// Read off the LIVE blocks even when the coarse ones woke the caller: those\n// name where the window was half a second ago (SyntenyFollow/CLAUDE.md).\nexport function hostWindow(view: LinearHost): HostWindow | undefined {\n const block = widest(view.dynamicBlocks.contentBlocks)\n const displayed =\n block?.displayedRegionIndex === undefined\n ? undefined\n : view.displayedRegions[block.displayedRegionIndex]\n return block && view.bpPerPx > 0\n ? {\n refName: block.refName,\n assemblyName: block.assemblyName,\n start: block.start,\n end: block.end,\n bpPerPx: view.bpPerPx,\n span: view.bpPerPx * view.width,\n regionStart: displayed?.start ?? block.start,\n regionEnd: displayed?.end ?? block.end,\n }\n : undefined\n}\n\n// The window plus one window-width each side, clamped to the displayed\n// region, with the margins narrowed until the whole cut fits under `capBp`.\nexport function hostCut(window: HostWindow, capBp: number): SubgraphRegion {\n const visible = window.end - window.start\n const margin = Math.max(0, Math.min(window.span, (capBp - visible) / 2))\n return {\n refName: window.refName,\n assemblyName: window.assemblyName,\n start: Math.max(window.regionStart, Math.floor(window.start - margin)),\n end: Math.min(window.regionEnd, Math.ceil(window.end + margin)),\n }\n}\n\nexport function cutHolds(cut: SubgraphRegion | undefined, window: HostWindow) {\n return (\n cut?.refName === window.refName &&\n cut.assemblyName === window.assemblyName &&\n cut.start <= window.start &&\n window.end <= cut.end\n )\n}\n", "import { setConf } from '@jbrowse/core/configuration'\nimport { getSnapshot } from '@jbrowse/mobx-state-tree'\n\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\n// The graph's own off-ramp charcoal, for a segment with no reference\n// coordinate (GeometryBuilder's REFERENCE_RAMP_ALT_COLOR).\nexport const OFF_REFERENCE_COLOR = 'rgb(60,65,72)'\n\n// The reference-position ramp as a `color` expression a linear track can run:\n// hue 0 to 300 over the domain by the feature's reference midpoint, the same\n// function of the same two numbers the graph paints its nodes with. `rank` is\n// what RgfaTabixAdapter puts on a feature; a track carrying none reads\n// `undefined > 0` as false and stays on the ramp.\nexport function referencePositionColor({\n start,\n end,\n}: {\n start: number\n end: number\n}) {\n const mid = \"(get(feature,'start')+get(feature,'end'))/2\"\n const hue = `min(300, max(0, (${mid} - ${start}) / ${end - start} * 300))`\n return `jexl:get(feature,'rank')>0 ? '${OFF_REFERENCE_COLOR}' : 'hsl(' + ${hue} + ',70%,50%)'`\n}\n\nexport interface LaneDisplay {\n type: string\n configuration: AnyConfigurationModel\n}\n\ninterface LaneTrack {\n configuration: { trackId: string }\n displays: LaneDisplay[]\n}\n\ninterface LaneView {\n id: string\n tracks?: LaneTrack[]\n}\n\nfunction isLaneView(view: unknown): view is LaneView {\n return (\n typeof view === 'object' &&\n view !== null &&\n 'id' in view &&\n Array.isArray((view as { tracks?: unknown }).tracks)\n )\n}\n\n// The display a graph was cut from: the track by id in the linear view it is\n// paired with, or in any linear view holding it when the graph was never\n// paired (a hand-written session), the same rule the hover sync uses.\nexport function sourceLaneDisplay(\n views: unknown[],\n connectedViewId: string | undefined,\n trackId: string,\n) {\n for (const view of views) {\n if (\n isLaneView(view) &&\n (connectedViewId === undefined || view.id === connectedViewId)\n ) {\n const track = view.tracks?.find(t => t.configuration.trackId === trackId)\n const display = track?.displays[0]\n if (display?.type === 'LinearBasicDisplay') {\n return display\n }\n }\n }\n return undefined\n}\n\n// Idempotent, since the autorun that calls this re-fires on every domain read.\n// A host at 5.0.0-beta.9 or later keeps `color` as a channel object whose\n// `value` holds the expression; earlier hosts keep the bare string.\nexport function paintSourceLane(display: LaneDisplay, color: string) {\n const { color: current } = getSnapshot(display.configuration) as {\n color?: unknown\n }\n const painted =\n typeof current === 'object' && current !== null && 'value' in current\n ? current.value\n : current\n if (painted !== color) {\n setConf(display, 'color', color)\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Graph, NodeSegment } from '../types'\n\ntype Positions = Record<string, NodeSegment[]>\n\n// Turn a finished force layout so the reference runs left to right: the\n// rotation that best maps backbone midpoints onto their reference coordinate\n// (a one-dimensional orthogonal Procrustes fit, weighted by segment length),\n// then a reflection so the off-reference mass sits below the axis. A rigid\n// transform, so the drawing FMMM made is unchanged; only its frame moves.\n//\n// About the length-weighted centroid, and translated back, so a drawing that is\n// already oriented comes back where it was rather than recentred on the origin.\nexport function orientToReference(graph: Graph, positions: Positions) {\n const backbone = graph.nodes.filter(\n node => isBackbone(node) && (positions[node.id]?.length ?? 0) > 0,\n )\n if (backbone.length < 2) {\n return positions\n }\n const points = backbone.map(node => {\n const segments = positions[node.id]!\n const mid = segments[Math.floor(segments.length / 2)]!\n return {\n x: mid.x,\n y: mid.y,\n r: node.stable!.start + node.length / 2,\n w: node.length,\n }\n })\n const weight = points.reduce((sum, p) => sum + p.w, 0)\n const cx = points.reduce((sum, p) => sum + p.x * p.w, 0) / weight\n const cy = points.reduce((sum, p) => sum + p.y * p.w, 0) / weight\n const cr = points.reduce((sum, p) => sum + p.r * p.w, 0) / weight\n // cross-covariance of (x, y) against (r, 0); only the first row is non-zero\n let sxr = 0\n let syr = 0\n for (const p of points) {\n sxr += p.w * (p.x - cx) * (p.r - cr)\n syr += p.w * (p.y - cy) * (p.r - cr)\n }\n // the rotation taking the direction (sxr, syr) onto +x\n const angle = -Math.atan2(syr, sxr)\n const c = Math.cos(angle)\n const s = Math.sin(angle)\n\n const out: Positions = {}\n for (const [id, segments] of Object.entries(positions)) {\n out[id] = segments.map(p => {\n const x = p.x - cx\n const y = p.y - cy\n return { x: cx + c * x - s * y, y: cy + s * x + c * y }\n })\n }\n\n let below = 0\n let above = 0\n for (const node of graph.nodes) {\n if (isBackbone(node)) {\n continue\n }\n for (const p of out[node.id] ?? []) {\n if (p.y > cy) {\n below++\n } else {\n above++\n }\n }\n }\n if (above > below) {\n for (const segments of Object.values(out)) {\n for (const p of segments) {\n p.y = 2 * cy - p.y\n }\n }\n }\n return out\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { buildNeighbors } from '../referenceSpan'\nimport { drawnNodeLength } from './drawnScale'\n\nimport type { LayoutScaling } from './drawnScale'\nimport type { Graph, GraphNode } from '../types'\n\n// A node as the engine takes it: an optional `x`/`y` is where FMMM starts the\n// node's chain (`addToOgdfGraph`, graphlayout.cpp).\nexport type LayoutNode = GraphNode & { x?: number; y?: number }\n\nexport interface Seed {\n x: number\n y: number\n}\n\n// y per BFS step off the backbone. FMMM keeps only the coarse shape of these\n// (OGDF applies the initial placement at its coarsest multilevel graph), so\n// the gap needs to say \"below the line\", not to place a lane.\nconst LANE_GAP = 30\n\n// Initial positions for a reference-anchored graph: the backbone end to end\n// along x at drawn length, each off-reference node at the midpoint of the node\n// a BFS from the backbone reached it from, a lane lower per step. Two alleles\n// off one anchor share a seed; FMMM separates them. Nodes no backbone reaches\n// spread along x below everything rather than piling on one point.\n//\n// Lengths come from `scaling.nodes`, not the graph's bp: under a compressing\n// spread a node's `length` crosses the RPC as drawn units times 1000 with a\n// matching `nodeLengthPerMegabase`, and a seed from bp would misplace every\n// chain against the one the engine builds.\nexport function referenceSeeds(\n graph: Graph,\n scaling: LayoutScaling,\n): Map<string, Seed> {\n const { opts } = scaling\n const drawn = new Map(\n scaling.nodes.map(node => [node.id, drawnNodeLength(opts, node.length)]),\n )\n const drawnOf = (id: string) => drawn.get(id) ?? opts.minimumNodeLength\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort((a, b) => a.stable.start - b.stable.start)\n\n const seeds = new Map<string, Seed>()\n let x = 0\n for (const node of backbone) {\n seeds.set(node.id, { x, y: 0 })\n x += drawnOf(node.id) + opts.edgeLength\n }\n\n const adjacent = buildNeighbors(graph)\n\n const depth = new Map(backbone.map(node => [node.id, 0]))\n // for-of over an array that grows as it goes: the iterator reads the live\n // length, so nodes pushed here are visited\n const queue = backbone.map(node => node.id)\n for (const id of queue) {\n const at = seeds.get(id)!\n const next = depth.get(id)! + 1\n for (const neighbour of adjacent.get(id) ?? []) {\n if (depth.has(neighbour)) {\n continue\n }\n depth.set(neighbour, next)\n seeds.set(neighbour, { x: at.x + drawnOf(id) / 2, y: LANE_GAP * next })\n queue.push(neighbour)\n }\n }\n\n let unreached = 0\n for (const node of graph.nodes) {\n if (!seeds.has(node.id)) {\n seeds.set(node.id, {\n x: (unreached++ * x) / graph.nodes.length,\n y: LANE_GAP * 3,\n })\n }\n }\n return seeds\n}\n\nexport function seededNodes(\n graph: Graph,\n scaling: LayoutScaling,\n): LayoutNode[] {\n const seeds = referenceSeeds(graph, scaling)\n return scaling.nodes.map(node => ({ ...node, ...seeds.get(node.id) }))\n}\n", "import type { Feature } from '@jbrowse/core/util'\n\nexport interface RepeatCall {\n bp: number\n // TRGT's `SD`. Zero means the allele is a copy of the one the reads did\n // support, so its length states nothing about a second haplotype; absent\n // where the genotyper writes no such field.\n spanningReads?: number\n}\n\n// A tandem repeat array as the walk rows need it: its span on the reference,\n// which is what the bars measure between, and its unit length, which is what\n// they tile by. Read off whatever repeat annotation the session has, since the\n// tools all state the same two facts under different names: VCF 4.5's\n// `<CNV:TR>` allele gives `RUL` and `RUS` (one entry per repeat sequence,\n// grouped by `RN`), UCSC simpleRepeat and TRF give `period` and the consensus\n// `sequence`, TRGT gives `MOTIFS` and `TRID`, ExpansionHunter `RU` and `REPID`,\n// HipSTR and GangSTR `PERIOD`, vamos its `motifs`. A VCF record's INFO is\n// searched as well as its top level, and a list's first entry is the unit the\n// bars tile by: for a compound array that is the first repeat sequence, which\n// is the expanding one at every such locus in the common catalogues.\nexport interface RepeatArray {\n key: string\n name: string\n refName: string\n start: number\n end: number\n unit: number\n motif?: string\n // Each sample's genotyped alleles, from a genotyper's per-sample `AL`\n // (TRGT); absent from a catalogue.\n calls?: Record<string, RepeatCall[]>\n}\n\nexport const REPEAT_ADAPTER_TYPES = new Set([\n 'BedAdapter',\n 'BedTabixAdapter',\n 'BigBedAdapter',\n 'VcfAdapter',\n 'VcfTabixAdapter',\n])\n\nconst REPEAT_TRACK_HINT =\n /repeat|tandem|\\bstr\\b|vntr|trf|trgt|vamos|expansion|microsat/i\n\nexport function pickRepeatTrack<\n T extends { trackId: string; name?: string; adapterType: string },\n>(tracks: T[], named: string) {\n if (named) {\n return tracks.find(t => t.trackId === named)\n }\n const candidates = tracks.filter(t => REPEAT_ADAPTER_TYPES.has(t.adapterType))\n return candidates.find(t =>\n REPEAT_TRACK_HINT.test(`${t.trackId} ${t.name ?? ''}`),\n )\n}\n\ntype FeatureLike = Feature | Record<string, unknown>\n\nfunction own(f: FeatureLike, name: string): unknown {\n return typeof (f as Feature).get === 'function'\n ? (f as Feature).get(name)\n : (f as Record<string, unknown>)[name]\n}\n\n// A TRGT repeat catalogue is a BED whose name column packs the record:\n// `ID=HTT;MOTIFS=CAG,CCG;STRUC=(CAG)nCAACAG(CCG)n`. Read as fields when it\n// has that shape.\nconst PACKED_NAME = /^\\w+=[^;]*(;\\w+=[^;]*)*$/\n\nfunction packedName(f: FeatureLike) {\n const name = own(f, 'name')\n if (typeof name !== 'string' || !PACKED_NAME.test(name)) {\n return undefined\n }\n return Object.fromEntries(\n name.split(';').map(pair => {\n const i = pair.indexOf('=')\n return [pair.slice(0, i), pair.slice(i + 1)]\n }),\n )\n}\n\n// A field at the feature's top level, in a VCF record's INFO, or in a packed\n// name column, in that order.\nfunction field(f: FeatureLike, name: string): unknown {\n const top = own(f, name)\n if (top !== undefined) {\n return top\n }\n const info = own(f, 'INFO') as Record<string, unknown> | undefined\n return info?.[name] ?? packedName(f)?.[name]\n}\n\nconst MOTIF_FIELDS = [\n 'RUS',\n 'MOTIFS',\n 'RU',\n 'motifs',\n 'motif',\n 'sequence',\n 'consensus',\n]\nconst PERIOD_FIELDS = [\n 'RUL',\n 'period',\n 'PERIOD',\n 'consensusSize',\n 'unit',\n 'unitLength',\n]\nconst NAME_FIELDS = ['TRID', 'REPID', 'VARID', 'ID', 'name', 'id']\n\n// The first entry of a list-valued field, whether it arrived parsed or as\n// the comma-joined text a VCF INFO or a BED column holds; VCF's missing \".\"\n// counts as absent.\nfunction first(f: FeatureLike, names: string[]) {\n for (const name of names) {\n const value = field(f, name)\n const one = String((Array.isArray(value) ? value[0] : value) ?? '')\n .split(',')[0]!\n .trim()\n if (one !== '' && one !== '.') {\n return one\n }\n }\n return undefined\n}\n\nconst IUPAC = /^[ACGTURYSWKMBDHVN]+$/i\n\n// The unit in bp: a stated period, else the length of the first motif.\nexport function repeatUnitOf(f: FeatureLike) {\n const period = Number(first(f, PERIOD_FIELDS))\n if (Number.isFinite(period) && period > 0) {\n return Math.round(period)\n }\n const motif = first(f, MOTIF_FIELDS)\n return motif && IUPAC.test(motif) ? motif.length : undefined\n}\n\n// A per-sample list field, parsed or as the comma-joined text a VCF column\n// holds. A missing entry stays missing: read as 0, an absent `SD` would make\n// every allele of every genotyper that writes no such field unspanned.\nfunction numbers(value: unknown) {\n const raw: unknown[] = Array.isArray(value)\n ? value\n : String(value ?? '').split(',')\n return raw.map(one => {\n const n =\n one === '' || one === null || one === undefined ? NaN : Number(one)\n return Number.isFinite(n) ? n : undefined\n })\n}\n\nfunction callsOf(f: FeatureLike) {\n const samples = own(f, 'samples') as\n Record<string, Record<string, unknown>> | undefined\n const called: Record<string, RepeatCall[]> = {}\n for (const [sample, fields] of Object.entries(samples ?? {})) {\n const reads = numbers(fields.SD)\n const alleles = numbers(fields.AL).flatMap((bp, i) =>\n bp !== undefined && bp > 0\n ? [reads[i] === undefined ? { bp } : { bp, spanningReads: reads[i] }]\n : [],\n )\n if (alleles.length > 0) {\n called[sample] = alleles\n }\n }\n return Object.keys(called).length > 0 ? called : undefined\n}\n\nexport function repeatArraysFrom(features: FeatureLike[]): RepeatArray[] {\n const arrays: RepeatArray[] = []\n for (const f of features) {\n const start = field(f, 'start') as number\n const end = field(f, 'end') as number\n const unit = repeatUnitOf(f)\n if (!(end > start) || unit === undefined) {\n continue\n }\n const refName = field(f, 'refName') as string\n const motif = first(f, MOTIF_FIELDS)\n const stated = first(f, NAME_FIELDS)\n arrays.push({\n key: `${refName}:${start}-${end}`,\n name:\n (stated && !PACKED_NAME.test(stated) ? stated : undefined) ??\n (motif && motif.length <= 12\n ? `(${motif})n`\n : `${refName}:${(start + 1).toLocaleString()}-${end.toLocaleString()}`),\n refName,\n start,\n end,\n unit,\n motif,\n calls: callsOf(f),\n })\n }\n return arrays.sort((a, b) => a.start - b.start)\n}\n", "import { pathOrigin } from './pathAnchoring'\n\nimport type { Graph } from './types'\n\n// One walk lifted out of the drawing: the nodes it visits and the links it\n// takes, so the geometry can fade everything else, plus what it carries\n// through the window against the reference walk.\nexport interface WalkHighlight {\n name: string\n nodeIds: Set<string>\n edgeIndexes: Set<number>\n steps: number\n bp: number\n referenceBp?: number\n}\n\nexport function walkHighlight(\n graph: Graph,\n name: string,\n): WalkHighlight | undefined {\n const path = graph.paths?.find(p => p.name === name)\n if (!path) {\n return undefined\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const edgeAt = new Map<string, number>()\n graph.edges.forEach((e, i) => {\n edgeAt.set(`${e.from}>${e.to}`, i)\n })\n const nodeIds = new Set(path.nodeIds)\n const edgeIndexes = new Set<number>()\n for (let i = 1; i < path.nodeIds.length; i++) {\n const a = path.nodeIds[i - 1]!\n const b = path.nodeIds[i]!\n const index = edgeAt.get(`${a}>${b}`) ?? edgeAt.get(`${b}>${a}`)\n if (index !== undefined) {\n edgeIndexes.add(index)\n }\n }\n const bpOf = (ids: string[]) =>\n ids.reduce((sum, id) => sum + (byId.get(id)?.length ?? 0), 0)\n // `referencePath` is the anchor name, which pathOrigin has stripped of the\n // range suffix odgi leaves on a P record's name\n const reference = graph.referencePath\n ? graph.paths!.find(p => pathOrigin(p.name).name === graph.referencePath)\n : undefined\n return {\n name,\n nodeIds,\n edgeIndexes,\n steps: path.nodeIds.length,\n bp: bpOf(path.nodeIds),\n referenceBp:\n reference && reference !== path ? bpOf(reference.nodeIds) : undefined,\n }\n}\n", "import { isBackbone } from '../GraphGenomeView/anchoredNodes'\n\nimport type { GraphNode } from '../GraphGenomeView/types'\n\n// What a LinearGenomeView writes to `session.hovered` on every mousemove (see\n// LinearGenomeViewContainer): the bp under the cursor, plus the feature under it\n// if the topmost track had one. Nothing names the source VIEW, so a graph view\n// filters by whether the position falls in the region it was cut from \u2014 but the\n// position does name the source ASSEMBLY, and that turns out to be the half\n// that matters.\nexport interface LgvHover {\n refName: string\n // The 0-based base under the pointer, the convention `stable.start` and the\n // cut region are in. Not the LGV's own `coord`, which is 1-based for display.\n coord: number\n // The assembly the hovered view is showing. `hoverPosition` is a\n // `PxToBpResult`, i.e. the displayed region the pointer landed in spread flat,\n // so this is that region's own `assemblyName`.\n //\n // Read because refName alone does not identify a locus in a session holding\n // several assemblies, which is every session these graphs are launched in. A\n // PanSN name reduces to a bare contig \u2014 the E. coli pangenome loads five\n // strains each with one refName `chr`, and HPRC's hg38 and a GenArk haplotype\n // both call it `chr6` \u2014 so a synteny stack's rows all answer to the same\n // name at unrelated coordinates. Without this the hover on ANY row lit up a\n // node in the graph, and the node then published its own reference interval\n // back into every linear view.\n //\n // Optional because the channel is typed `unknown` and read structurally: a\n // hover that states no assembly is taken at its word rather than dropped.\n assemblyName?: string\n featureName?: string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\ninterface FeatureLike {\n get: (key: string) => unknown\n}\n\nfunction isFeatureLike(value: unknown): value is FeatureLike {\n return isRecord(value) && typeof value.get === 'function'\n}\n\n// A display hovers either a Feature, read with `get`, or a plain hit item with\n// a `name` of its own, which is what the canvas feature display hands over.\n// Reading only the first left the exact segment match dead on that display.\nfunction hoveredName(feature: unknown) {\n const name = isFeatureLike(feature)\n ? feature.get('name')\n : isRecord(feature)\n ? feature.name\n : undefined\n return typeof name === 'string' ? name : undefined\n}\n\n// `hovered` is typed `unknown` on the session by design \u2014 it is a shared channel\n// every view writes its own shape to \u2014 so this reads the LGV's shape out of it\n// structurally rather than asserting a type onto it.\nexport function readLgvHover(hovered: unknown): LgvHover | undefined {\n let result: LgvHover | undefined\n if (isRecord(hovered) && isRecord(hovered.hoverPosition)) {\n const { refName, coord, coord0, assemblyName } = hovered.hoverPosition\n if (typeof refName === 'string' && typeof coord === 'number') {\n result = {\n refName,\n // `coord` is 1-based; a host that states the 0-based `coord0` is\n // taken at its word. Read as 0-based, the last base of every node lit\n // the next one, and a 1 bp node could not be reached at all.\n coord: typeof coord0 === 'number' ? coord0 : coord - 1,\n assemblyName:\n typeof assemblyName === 'string' ? assemblyName : undefined,\n featureName: hoveredName(hovered.hoverFeature),\n }\n }\n }\n return result\n}\n\n// Whether a hover is one this graph can answer: the same assembly, the same\n// stable sequence, and inside the window the cut was made for.\n//\n// The assembly is checked FIRST and is the whole reason this is not just a\n// coordinate test. See LgvHover.assemblyName: a graph cut from K12 and a\n// synteny row on Sakai both call their sequence `chr`, so on refName and\n// coordinate alone every row of the stack matched and the graph highlighted a\n// K12 node at Sakai's offset. A hover that names no assembly still passes,\n// which is what a hand-written channel writer and the older LGVs get.\nexport function hoverInRegion(\n hover: LgvHover,\n region: { refName: string; assemblyName: string; start: number; end: number },\n) {\n return (\n (hover.assemblyName === undefined ||\n hover.assemblyName === region.assemblyName) &&\n hover.refName === region.refName &&\n hover.coord >= region.start &&\n hover.coord < region.end\n )\n}\n\n// The graph node a linear-view hover points at.\n//\n// The feature under the cursor is the exact answer when the graph's own segments\n// track is what's hovered: an RgfaTabixAdapter feature's name is the segment id,\n// which is `GraphNode.name` (`GraphNode.id` carries a strand suffix, so the\n// match has to be on name). Any other track \u2014 genes, bubbles \u2014 supplies only a\n// coordinate, which still identifies the backbone segment covering it: the\n// backbone tiles the reference exactly once, whether rGFA's rank 0 declared it\n// or a reference path's own steps did.\nexport function nodeForLgvHover({\n hover,\n nodes,\n}: {\n hover: LgvHover\n nodes: GraphNode[]\n}) {\n let byName: string | undefined\n let byCoord: string | undefined\n for (const node of nodes) {\n if (node.name === hover.featureName) {\n byName = node.id\n } else if (\n byCoord === undefined &&\n isBackbone(node) &&\n hover.coord >= node.stable.start &&\n hover.coord < node.stable.start + node.length\n ) {\n byCoord = node.id\n }\n }\n // An exact segment match wins over the coordinate it happens to sit at.\n return byName ?? byCoord ?? null\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\n\nimport { locLabel, locString } from './contributors'\nimport { linearViewTarget } from './linearViewTarget'\n\nimport type { Contributor, GraphLocation } from './contributors'\nimport type { TrackScanSession } from '../launchSubgraph/subgraphTracks'\n\nexport interface GraphLaunchSession extends TrackScanSession {\n addView: (type: string, snapshot?: Record<string, unknown>) => { id: string }\n // The session's own loader for a view whose state model is registered\n // lazily (JBrowse 4's LinearSyntenyView); absent on an older core, where\n // addView is enough\n launchView?: (\n type: string,\n snapshot?: Record<string, unknown>,\n ) => Promise<{ id: string }>\n assemblyNames: string[]\n views: unknown[]\n // optional, as a hand-built session in a test has none\n notifyError?: (message: string, error?: unknown) => void\n}\n\n// A launch is a promise nothing waits on. Let go, a refusal moved nothing and\n// said nothing; this is where it is told to the user instead.\nfunction reportFailure(\n session: GraphLaunchSession,\n what: string,\n launch: Promise<unknown>,\n) {\n launch.catch((error: unknown) => {\n console.error(`[GraphGenomeView] ${what}`, error)\n session.notifyError?.(`${what}: ${error}`, error)\n })\n}\n\n// A node is often shorter than a line of text \u2014 a base-level allele can be a\n// handful of bp \u2014 and a linear view opened on exactly that span shows the span\n// and no context at all. Padding to a minimum window puts the node in the middle\n// of something readable.\nconst MIN_LAUNCH_SPAN_BP = 1000\n\nexport function paddedLocation(\n loc: GraphLocation,\n minSpan = MIN_LAUNCH_SPAN_BP,\n): GraphLocation {\n const pad = Math.max(0, Math.ceil((minSpan - (loc.end - loc.start)) / 2))\n return { ...loc, start: Math.max(0, loc.start - pad), end: loc.end + pad }\n}\n\n// Restate the rank-0 contributor in the terms of the region the graph was cut\n// from. Two separate corrections, both required:\n//\n// - the *assembly*. A backbone segment's SN is the graph's own spelling of the\n// reference, which need not be the name of any loaded assembly: the HPRC\n// graph says `GRCh38#0#chr6` in a session whose assembly is `hg38`. Taking\n// the PanSN sample there resolves to nothing and the graph offers no way out\n// at all. `loadedRegion` is authoritative \u2014 the adapter resolved that\n// assembly to that stable sequence to cut the subgraph in the first place.\n// The backbone's `haplotype` (`GRCh38#0`) goes with it, since\n// resolveLocationAssembly tries that before the sample.\n// - the *locus*. The cut region is what the user framed in the linear view, and\n// it is the span every other panel of a synteny launch is compared against;\n// the union of backbone segments differs from it whenever the window ends\n// inside a bubble.\n//\n// A subgraph is cut on one reference, so there is at most one rank-0\n// contributor. Must run before contributors are resolved against the session's\n// assemblies, or the reference is filtered out by the name this replaces.\nexport function withReferenceRegion(\n contributors: Contributor[],\n region:\n | { refName: string; assemblyName: string; start: number; end: number }\n | undefined,\n): Contributor[] {\n return region\n ? contributors.map(c =>\n c.rank === 0\n ? {\n ...c,\n sample: region.assemblyName,\n haplotype: undefined,\n refName: region.refName,\n start: region.start,\n end: region.end,\n }\n : c,\n )\n : contributors\n}\n\n// Show a location in a linear view: move the one already on screen if there is\n// one to move, open a pane only if there isn't.\n//\n// This is the difference between \"flip to the linear view\" and \"accumulate\n// panes\". The pangenome sessions this ships in are a linear view above a graph,\n// and there the answer to \"where is this node?\" should be that the linear view\n// scrolls to it \u2014 see linearViewTarget for which view counts as the one to move.\n//\n// `tracks` is only applied to a view being created; a view already open keeps\n// whatever the user has turned on. Returns the id of the view that ended up\n// showing the location, so the graph can pair with it for the hover sync.\nexport function showInLinearView({\n session,\n location,\n assembly,\n connectedViewId,\n tracks = [],\n}: {\n session: GraphLaunchSession\n location: GraphLocation\n assembly: string\n connectedViewId?: string\n tracks?: string[]\n}) {\n const existing = linearViewTarget({\n views: session.views,\n connectedViewId,\n assemblyName: assembly,\n })\n let viewId: string\n if (existing) {\n reportFailure(\n session,\n `Could not show ${assembly} ${locLabel(location)}`,\n Promise.resolve(existing.navToLocString(locString(location), assembly)),\n )\n viewId = existing.id\n } else {\n viewId = session.addView('LinearGenomeView', {\n displayName: `${assembly} \u2014 ${locLabel(location)}`,\n init: { assembly, loc: locString(location), tracks },\n }).id\n }\n return viewId\n}\n\n// Mark where a node sits on the reference, in the linear view already on\n// screen, instead of scrolling that view to it. Review: \"need to be able to\n// just highlight lineargenomeview coords\".\n//\n// The other half of the pair above, and the half that survives being let go of:\n// hover sync draws a band for as long as the pointer is over the node, where a\n// highlight is written into the view's own list and stays until it is removed.\n// It also keeps the reader's frame \u2014 the interesting comparison at a pangenome\n// locus is usually the whole window, not the 200 bp the node occupies.\n//\n// Nothing to mark when no linear view on that assembly is ours to move (see\n// linearViewTarget); the caller offers the item only when there is.\nexport function highlightInLinearView({\n session,\n location,\n assembly,\n connectedViewId,\n}: {\n session: GraphLaunchSession\n location: GraphLocation\n assembly: string\n connectedViewId?: string\n}) {\n const target = linearViewTarget({\n views: session.views,\n connectedViewId,\n assemblyName: assembly,\n })\n target?.addToHighlights?.({\n refName: location.refName,\n start: location.start,\n end: location.end,\n assemblyName: assembly,\n })\n return target !== undefined\n}\n\n// Which assemblies a track config says it covers, for deciding whether a panel\n// can draw it at all. A trackId naming nothing in the session reads as covering\n// nothing, so a stale id drops the lane rather than adding one the panel would\n// fail to load.\nfunction assemblyNamesOfTrack(\n session: GraphLaunchSession,\n trackId: string | undefined,\n) {\n if (trackId === undefined) {\n return []\n }\n const track = session.tracks.find(\n t => readConfObject(t, 'trackId') === trackId,\n )\n const names: unknown = track ? readConfObject(track, 'assemblyNames') : []\n return Array.isArray(names) ? names.filter(n => typeof n === 'string') : []\n}\n\n// One panel per contributing assembly, each framed on the locus that assembly\n// contributes here, with the graph's own reference panel on top.\n//\n// The panel loci come from the graph rather than from the synteny track: rGFA\n// states where each contributor's sequence sits on its own coordinates, so the\n// launch needs no mate discovery, no PAF lookup and no dialog. That is the whole\n// point of launching from the graph instead of from a linear view.\n//\n// Panels carry THE GRAPH'S OWN TRACK and nothing else, which is a narrower rule\n// than the single-view launch's (see launchTracks, which scans the session for\n// the assembly's annotation). Tried that here: a gene track per panel takes\n// about 160px of a row that is otherwise a ruler, so on five strains the\n// annotation is most of the viewport and the ribbons \u2014 the thing the launch\n// exists to draw \u2014 are squeezed into the gaps, and `collapseEmptyRows` is here\n// to prevent exactly that.\n//\n// The graph's own track does not have that shape. It goes only in the panels\n// whose assembly its config declares, which is where the graph HAS coordinates:\n// the HPRC segments track names two, so both its panels get one lane, while the\n// E. coli one names K12 alone and a five-strain launch adds a single lane rather\n// than five. And it is the lane that makes the band readable, since the ribbons\n// otherwise connect two bare rulers and the reader has nothing to match across\n// them.\nexport function launchSyntenyView({\n session,\n contributors,\n trackId,\n graphTrackId,\n}: {\n session: GraphLaunchSession\n contributors: Contributor[]\n trackId?: string\n graphTrackId?: string\n}) {\n const graphTrackAssemblies = assemblyNamesOfTrack(session, graphTrackId)\n // The panels the chosen track aligns, in the order given. A pairwise track is\n // offered beside an all-vs-all, two assemblies being a synteny view, and over\n // every contributor it sat on each level and could draw on one at most. A\n // track that states no assemblies keeps them all.\n const aligned = assemblyNamesOfTrack(session, trackId)\n const covered = contributors.filter(c => aligned.includes(c.sample))\n const panels = covered.length >= 2 ? covered : contributors\n const snapshot = {\n init: {\n views: panels.map(c => ({\n assembly: c.sample,\n loc: locString(c),\n ...(graphTrackId !== undefined &&\n graphTrackAssemblies.includes(c.sample)\n ? { tracks: [graphTrackId] }\n : {}),\n })),\n // One entry per LEVEL, not one for the track. `init.tracks` is 2D \u2014 the\n // gap between views[i] and views[i+1] \u2014 and a flat `[trackId]` is read as\n // the level-0 shorthand, so on a five-strain launch only the top band got\n // the alignment and the four below it opened as bare rulers. The same\n // all-vs-all track fills every level: it carries all the pairs.\n tracks: trackId ? panels.slice(1).map(() => [trackId]) : [],\n collapseEmptyRows: true,\n },\n }\n // launchView where the session has it: a synteny view's state model is\n // loaded on demand and addView throws until it is\n if (session.launchView) {\n reportFailure(\n session,\n 'Could not open the synteny view',\n session.launchView('LinearSyntenyView', snapshot),\n )\n } else {\n session.addView('LinearSyntenyView', snapshot)\n }\n}\n", "export interface NavigableLinearView {\n id: string\n assemblyNames: string[]\n navToLocString: (locString: string, assemblyName?: string) => unknown\n // The other way of answering \"where is this node\": mark it rather than scroll\n // to it. Optional, and not part of isLinearView's test \u2014 navigating is what\n // makes a view a target here, and a view that cannot be marked can still be\n // moved.\n addToHighlights?: (highlight: {\n refName: string\n start: number\n end: number\n assemblyName: string\n }) => unknown\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n// Reads `session.views` structurally, the same way graphViewHighlights does: the\n// members needed here are declared by LinearGenomeView, not by the\n// AbstractViewModel the session array is typed as.\nfunction isLinearView(view: unknown): view is NavigableLinearView {\n return (\n isRecord(view) &&\n view.type === 'LinearGenomeView' &&\n typeof view.id === 'string' &&\n typeof view.navToLocString === 'function' &&\n Array.isArray(view.assemblyNames)\n )\n}\n\n// The view a graph was launched from is not always in `session.views`: a row of\n// a LinearSyntenyView, LinearComparativeView or BreakpointSplitView sits in\n// that view's own `views[]`, so the candidates are the session's views plus one\n// level of rows.\nexport function withRows(views: unknown[]) {\n return views.flatMap(view =>\n isRecord(view) && Array.isArray(view.views)\n ? [view, ...view.views]\n : [view],\n )\n}\n\n// The linear view a \"show me this\" from the graph should move, rather than\n// opening a pane beside it. Three cases, in order:\n//\n// - the view this graph is paired with (`connectedViewId`), which is either\n// the view the graph was launched from or the one it later opened itself.\n// Explicit, so it wins.\n// - failing that, the only linear view in the session showing this assembly.\n// The pangenome sessions this view ships in are a linear view above a graph\n// with no pairing recorded, and there \"the linear view\" is unambiguous \u2014 the\n// same reasoning hoverSync's isConnected already applies to highlights.\n// - two or more candidates and no pairing: nothing. Guessing which of the\n// user's views to scroll is worse than opening one that is ours to move.\n//\n// Assembly-gated throughout: a linear view on a different assembly cannot show\n// this location at all.\nexport function linearViewTarget({\n views,\n connectedViewId,\n assemblyName,\n}: {\n views: unknown[]\n connectedViewId: string | undefined\n assemblyName: string\n}) {\n const candidates = withRows(views)\n .filter(view => isLinearView(view))\n .filter(view => view.assemblyNames.includes(assemblyName))\n const connected = candidates.find(view => view.id === connectedViewId)\n return connected ?? (candidates.length === 1 ? candidates[0] : undefined)\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\n\nimport type { TrackScanSession } from '../launchSubgraph/subgraphTracks'\n\nconst ANNOTATION_TRACK_TYPE = 'FeatureTrack'\n\n// What to turn on in a linear view the graph opens on `assemblyName`.\n//\n// A launch used to carry the graph's own track and nothing else, and that track\n// is configured for the reference alone \u2014 so `Launch view` \u2192 `CFT073 chr:\u2026`\n// landed on `No tracks active`, which answers \"where is this strain's sequence?\"\n// with an empty pane. What the session does hold for a contributing assembly is\n// that assembly's own annotation, and on a pangenome config that is one track\n// per strain (`CFT073_genes`), which is exactly what makes the landing readable.\n//\n// Annotation tracks only, not everything configured for the assembly. A launch\n// is a jump to a locus, and an alignments or coverage track turned on behind the\n// user's back fetches data nobody asked for; a FeatureTrack at a 50 kb window is\n// a tabix range query. The graph's own segments track is a FeatureTrack too, so\n// the reference case still gets it from the same scan.\n//\n// `first` is the graph's own track when the launch is on the reference, so the\n// segments the graph drew sit at the top of the view rather than wherever config\n// order puts them.\nexport function launchTracks({\n session,\n assemblyName,\n first,\n}: {\n session: TrackScanSession\n assemblyName: string\n first?: string\n}) {\n const found: string[] = []\n for (const track of session.tracks) {\n const type: unknown = readConfObject(track, 'type')\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n type === ANNOTATION_TRACK_TYPE &&\n Array.isArray(assemblyNames) &&\n assemblyNames.includes(assemblyName) &&\n typeof trackId === 'string' &&\n trackId !== first\n ) {\n found.push(trackId)\n }\n }\n return first ? [first, ...found] : found\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { getTrackName } from '@jbrowse/core/util/tracks'\n\nimport type { TrackScanSession } from '../launchSubgraph/subgraphTracks'\n\nexport interface LaunchableSyntenyTrack {\n trackId: string\n name: string\n // how many of the requested assemblies this track covers, so a whole-set\n // all-vs-all sorts above a pairwise alignment covering two of them\n coverage: number\n}\n\nconst SYNTENY_TRACK_TYPE = 'SyntenyTrack'\n\n// Synteny datasets in the session that align at least two of `assemblyNames`.\n//\n// Track type rather than adapter name: SyntenyTrack is the core track type every\n// synteny adapter is configured under, so this does not go stale when an adapter\n// is added or renamed the way naming PAF adapters would.\n//\n// Two is the floor because that is the floor for a synteny view; more is better,\n// and an all-vs-all covering every contributing assembly is what makes the\n// multi-panel launch worth offering at all.\nexport function launchableSyntenyTracks(\n session: TrackScanSession,\n assemblyNames: string[],\n) {\n const wanted = new Set(assemblyNames)\n const found: LaunchableSyntenyTrack[] = []\n for (const track of session.tracks) {\n const type: unknown = readConfObject(track, 'type')\n const trackAssemblies: unknown = readConfObject(track, 'assemblyNames')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n type === SYNTENY_TRACK_TYPE &&\n typeof trackId === 'string' &&\n Array.isArray(trackAssemblies)\n ) {\n const coverage = trackAssemblies.filter(name => wanted.has(name)).length\n if (coverage >= 2) {\n found.push({ trackId, name: getTrackName(track, session), coverage })\n }\n }\n }\n return found.sort((a, b) => b.coverage - a.coverage)\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { getTrackName } from '@jbrowse/core/util/tracks'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nexport interface SubgraphTrack {\n trackId: string\n name: string\n // The assembly a cut has to be on; see graphReferenceAssembly.\n referenceAssembly: string | undefined\n // The lanes the cut is for; undefined is every haplotype.\n haplotypes: string[] | undefined\n}\n\nexport interface TrackScanSession {\n tracks: AnyConfigurationModel[]\n assemblies: AnyConfigurationModel[]\n}\n\n// The linear view a launch is offered from, as far as the lanes go.\nexport interface LaunchingView {\n tracks: readonly {\n configuration: AnyConfigurationModel\n displays: readonly unknown[]\n }[]\n}\n\ninterface LaneSelectingDisplay {\n laneSelection: readonly string[] | undefined\n hiddenLanes: readonly string[]\n}\n\n// Whether an adapter declares it can cut a local subgraph. Discovery is by\n// declared capability, not adapter name: the old launcher hardcoded\n// `GfaTabixAdapter`/`GfaServerAdapter`, which is exactly what left it dead when\n// those were removed.\n//\n// `getAdapterType` throws for a type that was never registered, and a session\n// can hold tracks whose plugin isn't loaded, so registration is checked first \u2014\n// a session-wide scan reaches tracks a single display's menu never would.\nexport function adapterCanCutSubgraph(\n pluginManager: PluginManager,\n adapterType: unknown,\n) {\n return (\n typeof adapterType === 'string' &&\n pluginManager.adapterTypes.has(adapterType) &&\n pluginManager\n .getAdapterType(adapterType)\n .adapterCapabilities.includes('getSubgraph')\n )\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(item => typeof item === 'string')\n}\n\nfunction isLaneSelecting(display: unknown): display is LaneSelectingDisplay {\n return (\n typeof display === 'object' &&\n display !== null &&\n 'laneSelection' in display &&\n 'hiddenLanes' in display\n )\n}\n\n// The assemblies the track names after its reference, which is what core's\n// MultiWaySyntenyDisplay opens a lane-declaring source on.\nexport function trackLanes(track: AnyConfigurationModel) {\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n const lanes = isStringArray(assemblyNames) ? assemblyNames.slice(1) : []\n return lanes.length > 0 ? lanes : undefined\n}\n\n// A graph is cut on the first assembly its track names, the rule\n// GbzBaseSyntenyAdapter states for its anchor. Every other assembly the track\n// names holds its sequence as rank>0 alleles off that reference's backbone, so\n// a window on one has no reference coordinates to frame the cut by.\nexport function graphReferenceAssembly(track: AnyConfigurationModel) {\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n return isStringArray(assemblyNames) ? assemblyNames[0] : undefined\n}\n\nexport function offReferenceProblem(\n referenceAssembly: string | undefined,\n assemblyName: string,\n) {\n return referenceAssembly === undefined || referenceAssembly === assemblyName\n ? undefined\n : `The graph is cut on its reference, ${referenceAssembly}: open it from a view of ${referenceAssembly}`\n}\n\n// The lanes the track draws in `view`: the ones in force there less any the\n// reader hid. A track not on screen there has its own assemblies to go by.\nfunction lanesToCut(\n track: AnyConfigurationModel,\n trackId: string,\n view: LaunchingView | undefined,\n) {\n const shown = view?.tracks\n .find(t => readConfObject(t.configuration, 'trackId') === trackId)\n ?.displays.find(isLaneSelecting)\n return shown\n ? shown.laneSelection?.filter(name => !shown.hiddenLanes.includes(name))\n : trackLanes(track)\n}\n\nexport function subgraphTrack(\n track: AnyConfigurationModel,\n trackId: string,\n session: TrackScanSession,\n view?: LaunchingView,\n): SubgraphTrack {\n return {\n trackId,\n name: getTrackName(track, session),\n referenceAssembly: graphReferenceAssembly(track),\n haplotypes: lanesToCut(track, trackId, view),\n }\n}\n\n// Tracks anywhere in the session that can cut a subgraph on `assemblyName`, so\n// the launch can be offered from a linear view that doesn't have the graph track\n// in it. The entry point no longer has to be the graph track's own menu \u2014 which\n// was the whole problem: a user browsing genes had no way in.\nexport function subgraphTracks(\n pluginManager: PluginManager,\n session: TrackScanSession,\n assemblyName: string,\n view?: LaunchingView,\n) {\n const found: SubgraphTrack[] = []\n for (const track of session.tracks) {\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n const adapter: unknown = readConfObject(track, 'adapter')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n Array.isArray(assemblyNames) &&\n assemblyNames.includes(assemblyName) &&\n typeof trackId === 'string' &&\n typeof adapter === 'object' &&\n adapter !== null &&\n 'type' in adapter &&\n adapterCanCutSubgraph(pluginManager, adapter.type)\n ) {\n found.push(subgraphTrack(track, trackId, session, view))\n }\n }\n return found\n}\n", "import type modelFactory from './GraphGenomeView/model'\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\nimport type { SnapshotIn } from '@jbrowse/mobx-state-tree'\n\n// GraphGenomeView has no assembly/track resolution \u2014 every field is a plain\n// persisted view prop, so the launch spec forwards straight onto the view\n// snapshot. `afterAttach` loads `gfaLocation`, or refetches the\n// `loadedTrackId`/`loadedRegion` pair, once the view is created.\n//\n// **Taken off the model rather than listed by hand**, which is the whole point.\n// This named four props (`gfaLocation`, `colorScheme`, `linearLayout`,\n// `drawPaths`) while the extension point spreads whatever it is handed \u2014 so the\n// runtime accepted every prop and the DECLARED contract accepted a third of\n// them. The docs' own figure specs launch with thirteen: `loadedTrackId`,\n// `loadedRegion`, `layoutMode`, `colorDomain`, `paneHeight`, `referencePath`,\n// `layoutQuality`, `maxRegionBp` and `bubbleSpread` among them, none of which\n// this said were allowed. It carries a `#extensionPoint` tag, so the published\n// API docs said so too.\n//\n// Derived, so adding a view prop widens the launch with it. `type` is the\n// dispatch key rather than a setting \u2014 loadSessionSpec strips it before calling\n// this \u2014 and is the one field a caller may not supply.\nexport type LaunchGraphGenomeViewArgs = Partial<\n Omit<SnapshotIn<ReturnType<typeof modelFactory>>, 'type'>\n> & {\n session: AbstractSessionModel\n}\n\ndeclare module '@jbrowse/core/PluginManager' {\n interface ExtensionPointRegistry {\n 'LaunchView-GraphGenomeView': {\n args: LaunchGraphGenomeViewArgs\n result: LaunchGraphGenomeViewArgs\n }\n }\n}\n\nexport default function LaunchGraphGenomeViewF(pluginManager: PluginManager) {\n /** #extensionPoint LaunchView-GraphGenomeView | async | Programmatically launch a graph genome view */\n pluginManager.addToExtensionPoint('LaunchView-GraphGenomeView', args => {\n const { session, ...spec } = args\n session.addView('GraphGenomeView', spec)\n return args\n })\n}\n", "import { lazy } from 'react'\n\nimport DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'\n\nimport { configSchemaFactory } from './configSchema'\nimport { stateModelFactory } from './model'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function LinearGraphDisplayF(pluginManager: PluginManager) {\n pluginManager.addDisplayType(() => {\n const configSchema = configSchemaFactory()\n return new DisplayType({\n name: 'LinearGraphDisplay',\n displayName: 'Graph',\n configSchema,\n stateModel: stateModelFactory(configSchema),\n trackType: ['FeatureTrack', 'SyntenyTrack'],\n viewType: 'LinearGenomeView',\n ReactComponent: lazy(() => import('./components/LinearGraphDisplay')),\n })\n })\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { trackHeightConfigSchemaFields } from '@jbrowse/display-kit/trackHeightConfigSchemaFields'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport { COLOR_SCHEME_VALUES } from '../GraphGenomeView/colorSchemes'\nimport { LAYOUT_MODE_VALUES } from '../GraphGenomeView/layoutModes'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config LinearGraphDisplay\n * The graph drawn as a track of the linear genome view. A layout whose x is\n * reference bp draws under the view's coordinates and re-cuts as the view\n * moves; force, ordered and walk rows draw in their own coordinates, fitted to\n * the track.\n */\nexport function configSchemaFactory() {\n return ConfigurationSchema(\n 'LinearGraphDisplay',\n {\n /**\n * #slot\n * the layout the track opens in\n */\n layoutMode: {\n type: 'stringEnum',\n model: types.enumeration('LayoutMode', LAYOUT_MODE_VALUES),\n defaultValue: 'auto',\n },\n /**\n * #slot\n */\n colorScheme: {\n type: 'stringEnum',\n model: types.enumeration('ColorScheme', COLOR_SCHEME_VALUES),\n defaultValue: 'auto',\n },\n ...trackHeightConfigSchemaFields({\n defaultHeight: 300,\n height:\n 'the most the track takes; a layout shorter than it takes only what it needs',\n }),\n },\n { explicitlyTyped: true, explicitIdentifier: 'displayId' },\n )\n}\n\nexport type LinearGraphDisplayConfigModel = ReturnType<\n typeof configSchemaFactory\n>\nexport type LinearGraphDisplayConfig = Instance<LinearGraphDisplayConfigModel>\n", "import { lazy } from 'react'\n\nimport { ConfigurationReference, getConf } from '@jbrowse/core/configuration'\nimport { BaseDisplay } from '@jbrowse/core/pluggableElementTypes'\nimport { getContainingView, getSession } from '@jbrowse/core/util'\nimport TrackHeightMixin from '@jbrowse/display-kit/TrackHeightMixin'\nimport { addDisposer, types } from '@jbrowse/mobx-state-tree'\nimport SettingsIcon from '@mui/icons-material/Settings'\nimport { autorun } from 'mobx'\n\nimport { COLOR_SCHEMES } from '../GraphGenomeView/colorSchemes'\nimport { isLinearHost } from '../GraphGenomeView/host'\nimport { LAYOUT_MODES } from '../GraphGenomeView/layoutModes'\nimport paneModelFactory from '../GraphGenomeView/model'\n\nimport type { LinearGraphDisplayConfigModel } from './configSchema'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\nconst GraphSettingsDialog = lazy(\n () => import('../GraphGenomeView/components/GraphSettingsDialog'),\n)\n\nexport function stateModelFactory(configSchema: LinearGraphDisplayConfigModel) {\n const Pane = paneModelFactory()\n return types\n .compose(\n 'LinearGraphDisplay',\n BaseDisplay,\n TrackHeightMixin(),\n types.model({\n type: types.literal('LinearGraphDisplay'),\n configuration: ConfigurationReference(configSchema),\n // The graph pane, the same model the standalone view is. Here the\n // linear view above is its host: x is that view's window for a layout\n // whose x is reference bp, and the cut is re-made when the window\n // leaves it (pane.host).\n pane: types.optional(Pane, () => ({\n type: 'GraphGenomeView' as const,\n })),\n // The config's layout and colour reach the pane once, so a choice made\n // in the track menu survives a reload.\n configured: types.optional(types.boolean, false),\n }),\n )\n .views(self => ({\n get host() {\n let view: unknown\n try {\n view = getContainingView(self)\n } catch {\n return undefined\n }\n return isLinearHost(view) ? view : undefined\n },\n get trackId(): string {\n return getConf(self.parentTrack, 'trackId')\n },\n }))\n .views(self => ({\n trackMenuItems(): MenuItem[] {\n const { pane } = self\n const { graph } = pane\n const walks = pane.walkChoices\n return [\n {\n label: 'Layout',\n subMenu: LAYOUT_MODES.map(mode => ({\n type: 'radio' as const,\n label: mode.label,\n checked: pane.layoutMode === mode.value,\n disabled: graph ? !mode.available(graph) : false,\n onClick: () => {\n void pane.switchLayout(mode.value)\n },\n })),\n },\n {\n label: 'Color',\n subMenu: COLOR_SCHEMES.map(scheme => ({\n type: 'radio' as const,\n label: scheme.label,\n checked: pane.colorScheme === scheme.value,\n onClick: () => {\n pane.setColorScheme(scheme.value)\n },\n })),\n },\n ...(walks.length > 0\n ? [\n {\n label: 'Walk',\n subMenu: [\n {\n type: 'radio' as const,\n label: 'None',\n checked: pane.highlightedPath === '',\n onClick: () => {\n pane.setHighlightedPath('')\n },\n },\n ...walks.map(walk => ({\n type: 'radio' as const,\n label: walk.label,\n checked: pane.highlightedPath === walk.name,\n onClick: () => {\n pane.setHighlightedPath(walk.name)\n },\n })),\n ],\n },\n ]\n : []),\n ...(pane.hostPlacesX\n ? []\n : [\n {\n label: 'Zoom in',\n onClick: () => {\n pane.zoom(1.5, pane.width / 2, pane.canvasHeight / 2)\n },\n },\n {\n label: 'Zoom out',\n onClick: () => {\n pane.zoom(1 / 1.5, pane.width / 2, pane.canvasHeight / 2)\n },\n },\n {\n label: 'Zoom to fit',\n onClick: () => {\n pane.zoomToFit()\n },\n },\n ]),\n {\n type: 'checkbox',\n label: 'Mark bubbles',\n checked: pane.showBubbles,\n onClick: () => {\n pane.setShowBubbles(!pane.showBubbles)\n },\n },\n {\n type: 'checkbox',\n label: 'Show deletion edges',\n checked: pane.showDeletionEdges,\n onClick: () => {\n pane.setShowDeletionEdges(!pane.showDeletionEdges)\n },\n },\n {\n type: 'checkbox',\n label: 'Genes on the backbone',\n checked: pane.showGenes,\n onClick: () => {\n pane.setShowGenes(!pane.showGenes)\n },\n },\n {\n label: 'Settings',\n icon: SettingsIcon,\n onClick: () => {\n getSession(self).queueDialog(onClose => [\n GraphSettingsDialog,\n { model: pane, open: true, onClose },\n ])\n },\n },\n ]\n },\n }))\n .actions(self => ({\n setConfigured() {\n self.configured = true\n },\n // The track's height is the pane's ceiling: a layout shorter than it\n // takes only what it needs, and a drag on the handle moves the ceiling.\n resizeHeight(distance: number) {\n const before = self.height\n const target = Math.max(before + distance, 40)\n self.pane.setPaneHeight(target)\n return target - before\n },\n }))\n .actions(self => ({\n afterAttach() {\n const { pane } = self\n if (!self.configured) {\n pane.setLayoutMode(getConf(self, 'layoutMode'))\n pane.setColorScheme(getConf(self, 'colorScheme'))\n pane.setPaneHeight(getConf(self, 'height'))\n self.setConfigured()\n }\n pane.adoptTrack(self.trackId)\n pane.startHosting()\n addDisposer(\n self,\n autorun(\n () => {\n const { host } = self\n if (host) {\n pane.setWidth(host.width)\n pane.pairWithLinearView(host.id)\n }\n },\n { name: 'LinearGraphDisplayHost' },\n ),\n )\n addDisposer(\n self,\n autorun(\n () => {\n const height = pane.canvasHeight\n if (self.height !== height) {\n self.setHeight(height)\n }\n },\n { name: 'LinearGraphDisplayHeight' },\n ),\n )\n },\n }))\n}\n\nexport type LinearGraphDisplayStateModel = ReturnType<typeof stateModelFactory>\nexport type LinearGraphDisplayModel = Instance<LinearGraphDisplayStateModel>\n", "import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'\n\nimport configSchema, { normalizeSnapshot } from './configSchema.ts'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function MinigraphBubbleAdapterF(pluginManager: PluginManager) {\n pluginManager.addAdapterType(\n () =>\n new AdapterType({\n name: 'MinigraphBubbleAdapter',\n displayName: 'Minigraph bubble BED adapter',\n normalizeSnapshot,\n configSchema,\n adapterMetadata: {\n category: 'Graph adapters',\n },\n getAdapterClass: () =>\n import('./MinigraphBubbleAdapter.ts').then(r => r.default),\n }),\n )\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config MinigraphBubbleAdapter\n * #trackType FeatureTrack\n * #fileFormat graph | Minigraph bubble BED | `gfatools bubble` output; HPRC publishes one ready-made, so no preprocessing at all\n * the tabix-indexed BED `gfatools bubble` writes for a minigraph graph: one row\n * per bubble, on the reference's own coordinates. Unlike\n * [RgfaTabixAdapter](/docs/config/rgfatabixadapter) this needs no index build of\n * your own, because HPRC publishes this file beside its minigraph graph.\n *\n * It shows *where* the graph varies and by how much, not the graph itself.\n *\n * #example\n * ```js\n * {\n * type: 'MinigraphBubbleAdapter',\n * uri: 'https://jbrowse.org/demos/hprc/hprc-v2.0-mc-grch38.bubbles.bed.gz',\n * assemblyNameToPanSN: { hg38: 'GRCh38' },\n * }\n * ```\n */\n\nexport function normalizeSnapshot(snap: Record<string, unknown>) {\n const { uri, baseUri, csi } = snap\n return typeof uri === 'string'\n ? {\n ...snap,\n bubblesLocation: { uri, baseUri },\n index: {\n indexType: csi ? 'CSI' : 'TBI',\n location: { uri: `${uri}.${csi ? 'csi' : 'tbi'}`, baseUri },\n },\n }\n : snap\n}\n\nconst MinigraphBubbleAdapter = ConfigurationSchema(\n 'MinigraphBubbleAdapter',\n {\n /**\n * #slot\n */\n bubblesLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.bb.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n index: ConfigurationSchema('MinigraphBubbleIndex', {\n /**\n * #slot index.indexType\n */\n indexType: {\n model: types.enumeration('IndexType', ['TBI', 'CSI']),\n type: 'stringEnum',\n defaultValue: 'TBI',\n },\n /**\n * #slot index.location\n */\n location: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.bb.bed.gz.tbi',\n locationType: 'UriLocation',\n },\n },\n }),\n /**\n * #slot\n * Maps a JBrowse assembly name to its PanSN sample prefix, for when they\n * differ. `gfatools bubble` names each row after the graph's stable\n * sequence, so a Minigraph-Cactus graph yields `GRCh38#0#chr6` and an\n * `hg38` assembly needs `{ hg38: 'GRCh38' }`; HPRC's published v1.0\n * minigraph bubbles are bare (`chr6`) and need nothing.\n */\n assemblyNameToPanSN: {\n type: 'frozen',\n defaultValue: {},\n },\n },\n {\n explicitlyTyped: true,\n\n /**\n * #preProcessSnapshot\n *\n * preprocessor to allow the minimal config\n * ```json\n * { \"type\": \"MinigraphBubbleAdapter\", \"uri\": \"graph.bb.bed.gz\" }\n * ```\n */\n preProcessSnapshot: normalizeSnapshot,\n },\n)\n\nexport type MinigraphBubbleAdapterConfig = Instance<\n typeof MinigraphBubbleAdapter\n>\n\nexport default MinigraphBubbleAdapter\n", "import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'\n\nimport configSchema, { normalizeSnapshot } from './configSchema.ts'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function RgfaTabixAdapterF(pluginManager: PluginManager) {\n pluginManager.addAdapterType(\n () =>\n new AdapterType({\n name: 'RgfaTabixAdapter',\n displayName: 'Indexed rGFA adapter',\n normalizeSnapshot,\n configSchema,\n // what the launch menus look for (launchSubgraph/subgraphTracks.ts)\n adapterCapabilities: ['getSubgraph'],\n adapterMetadata: {\n category: 'Graph adapters',\n },\n getAdapterClass: () =>\n import('./RgfaTabixAdapter.ts').then(r => r.default),\n }),\n )\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config RgfaTabixAdapter\n * #trackType FeatureTrack\n * #fileFormat graph | Indexed rGFA | Built by `scripts/build_rgfa_tabix.sh`; serves segments as features and extracts local subgraphs\n * two tabix-indexed BED projections of an rGFA (minigraph). rGFA tags every\n * segment with `SN`/`SO`/`SR`, so both files record coordinates the graph\n * already states rather than inventing any. Plain GFA (pggb, odgi,\n * Minigraph-Cactus) has no such tags and is not supported.\n *\n * The `uri` shorthand takes the prefix the build script was given and resolves\n * `<uri>.segs.bed.gz`, `<uri>.links.bed.gz` and their `.tbi` indexes. `coarse`\n * takes the same shorthand for a second pair at one node per bubble, built by\n * `build_bubble_tier.sh` in jbrowse-components, plus the zoom past which a\n * graph view following a linear view cuts it.\n *\n * #example\n * ```js\n * {\n * type: 'RgfaTabixAdapter',\n * uri: 'https://example.com/hprc.rgfa',\n * coarse: { uri: 'https://example.com/hprc.tier10000', aboveBpPerPx: 1000 },\n * }\n * ```\n */\n\nfunction tbi(uri: string, baseUri: unknown, csi: unknown) {\n return {\n indexType: csi ? 'CSI' : 'TBI',\n location: { uri: `${uri}.${csi ? 'csi' : 'tbi'}`, baseUri },\n }\n}\n\nfunction prefixLocations(snap: Record<string, unknown>) {\n const { uri, baseUri, csi } = snap\n return typeof uri === 'string'\n ? {\n ...snap,\n segmentsLocation: { uri: `${uri}.segs.bed.gz`, baseUri },\n segmentsIndex: tbi(`${uri}.segs.bed.gz`, baseUri, csi),\n linksLocation: { uri: `${uri}.links.bed.gz`, baseUri },\n linksIndex: tbi(`${uri}.links.bed.gz`, baseUri, csi),\n }\n : snap\n}\n\nexport function normalizeSnapshot(snap: Record<string, unknown>) {\n const { coarse, baseUri } = snap\n return prefixLocations(\n typeof coarse === 'object' && coarse !== null\n ? { ...snap, coarse: prefixLocations({ baseUri, ...coarse }) }\n : snap,\n )\n}\n\nconst indexSchema = (name: string, defaultUri: string) =>\n ConfigurationSchema(name, {\n /**\n * #slot\n */\n indexType: {\n model: types.enumeration('IndexType', ['TBI', 'CSI']),\n type: 'stringEnum',\n defaultValue: 'TBI',\n },\n /**\n * #slot\n */\n location: {\n type: 'fileLocation',\n defaultValue: { uri: defaultUri, locationType: 'UriLocation' },\n },\n })\n\nconst RgfaTabixCoarseTier = ConfigurationSchema('RgfaTabixCoarseTier', {\n /**\n * #slot\n * The linear view's bp per px past which a graph following it cuts this\n * pair. Unset means the track has no coarse tier. There is no default,\n * because the zoom a fine cut stays drawable to depends on the graph's\n * density: a minigraph graph averages ~7 kb per segment, a base-level pggb\n * graph ~17 bp.\n */\n aboveBpPerPx: { type: 'maybeNumber' },\n /**\n * #slot\n */\n segmentsLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.tier.segs.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n segmentsIndex: indexSchema(\n 'RgfaCoarseSegmentsIndex',\n '/path/to/graph.tier.segs.bed.gz.tbi',\n ),\n /**\n * #slot\n */\n linksLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.tier.links.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n linksIndex: indexSchema(\n 'RgfaCoarseLinksIndex',\n '/path/to/graph.tier.links.bed.gz.tbi',\n ),\n})\n\nconst RgfaTabixAdapter = ConfigurationSchema(\n 'RgfaTabixAdapter',\n {\n /**\n * #slot\n * one row per segment: `stableName start end segmentId rank`, as emitted by\n * `gfatools gfa2bed -m`\n */\n segmentsLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.segs.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n segmentsIndex: indexSchema(\n 'RgfaSegmentsIndex',\n '/path/to/graph.segs.bed.gz.tbi',\n ),\n /**\n * #slot\n * one row per link per endpoint, each carrying both endpoints in full so a\n * neighbour on another stable sequence needs no segment-id lookup\n */\n linksLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.links.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n linksIndex: indexSchema(\n 'RgfaLinksIndex',\n '/path/to/graph.links.bed.gz.tbi',\n ),\n /**\n * #slot\n * Maps a JBrowse assembly name to its PanSN sample prefix in the graph, for\n * when they differ. Defaults to identity: the assembly name is assumed to be\n * the PanSN sample name, and a graph whose stable names are bare (minigraph\n * writes `chr1`) needs nothing here at all.\n *\n * Minigraph-Cactus writes PanSN stable names, so HPRC's release-2 graph\n * calls the reference `GRCh38#0#chr1` and an `hg38` assembly needs\n * `{ hg38: 'GRCh38' }`. The prefix is what disambiguates: that same graph\n * also contains `CHM13#0#chr1`, so matching on the contig alone would\n * silently resolve to the wrong sample.\n */\n assemblyNameToPanSN: {\n type: 'frozen',\n defaultValue: {},\n },\n /**\n * #slot\n * The same graph at one node per bubble, which a graph view following a\n * linear view cuts once that view is zoomed out past `aboveBpPerPx`, and\n * which no bp cap applies to. `{ uri, aboveBpPerPx }`, where `uri` is a\n * prefix as the adapter's own is.\n */\n coarse: RgfaTabixCoarseTier,\n },\n {\n explicitlyTyped: true,\n\n /**\n * #preProcessSnapshot\n *\n * preprocessor to allow the minimal config\n * ```json\n * { \"type\": \"RgfaTabixAdapter\", \"uri\": \"graph.rgfa\" }\n * ```\n */\n preProcessSnapshot: normalizeSnapshot,\n },\n)\n\nexport type RgfaTabixAdapterConfig = Instance<typeof RgfaTabixAdapter>\n\nexport default RgfaTabixAdapter\n", "import { getSession } from '@jbrowse/core/util'\nimport { alpha } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport { graphViewHighlights } from './graphViewHighlights'\n\nimport type { HighlightRegion } from './graphViewHighlights'\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\nconst useStyles = makeStyles()(theme => ({\n highlight: {\n height: '100%',\n position: 'absolute',\n background: alpha(theme.palette.primary.main, 0.25),\n borderLeft: `2px solid ${theme.palette.primary.main}`,\n borderRight: `2px solid ${theme.palette.primary.main}`,\n pointerEvents: 'none',\n zIndex: 10,\n },\n}))\n\n// One band over the linear view for a hovered graph node. `getHighlightCoords`\n// is the LGV's own projection \u2014 it canonicalizes the refName and floors the\n// width to 3 px, so a pure insertion (a zero-length reference span) still shows\n// as a mark rather than vanishing.\nconst Highlight = observer(function Highlight({\n model,\n region,\n}: {\n model: LinearGenomeViewModel\n region: HighlightRegion\n}) {\n const { classes } = useStyles()\n const coords = model.getHighlightCoords(region)\n return coords ? (\n <div\n data-testid=\"graph-node-highlight\"\n className={classes.highlight}\n style={{ left: coords.left, width: coords.width }}\n />\n ) : null\n})\n\nconst GraphNodeHighlight = observer(function GraphNodeHighlight({\n model,\n}: {\n model: LinearGenomeViewModel\n}) {\n const { views } = getSession(model)\n return (\n <>\n {graphViewHighlights([...views], model.id, model.assemblyNames).map(\n ({ key, region }) => (\n <Highlight key={`graph-hover-${key}`} model={model} region={region} />\n ),\n )}\n </>\n )\n})\n\nexport default GraphNodeHighlight\n", "import { withRows } from '../launchFromGraph/linearViewTarget'\n\nexport interface HighlightRegion {\n refName: string\n start: number\n end: number\n assemblyName?: string\n}\n\nexport interface GraphViewHighlight {\n key: string\n region: HighlightRegion\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\nfunction readRegion(value: unknown): HighlightRegion | undefined {\n let region: HighlightRegion | undefined\n if (isRecord(value)) {\n const { refName, start, end, assemblyName } = value\n if (\n typeof refName === 'string' &&\n typeof start === 'number' &&\n typeof end === 'number'\n ) {\n region = {\n refName,\n start,\n end,\n assemblyName:\n typeof assemblyName === 'string' ? assemblyName : undefined,\n }\n }\n }\n return region\n}\n\n// Whether a graph view's highlights belong on a given linear view.\n// `connectedViewId` is written by the launch menu, so a launched pair is\n// explicit. A graph view without one \u2014 a hand-written session snapshot, or\n// `Add > Graph genome view` followed by a subgraph load \u2014 matches any linear\n// view, because the alternative is silently drawing nothing.\n//\n// So does one whose linear view has been closed. Held to an id nothing answers\n// to, it matched no view and its hover drew nowhere for the rest of the\n// session, the view it next opened included.\nfunction isConnected(\n view: Record<string, unknown>,\n linearViewId: string,\n liveViewIds: ReadonlySet<unknown>,\n) {\n const connectedViewId = view.connectedViewId\n return (\n connectedViewId === undefined ||\n connectedViewId === linearViewId ||\n !liveViewIds.has(connectedViewId)\n )\n}\n\n// ...and whether the view can mean anything by it. `getHighlightCoords`\n// canonicalizes a refName against the region's OWN assembly and then lays it\n// out against this view's displayed regions, without ever asking whether the\n// two are the same assembly \u2014 so a band handed to the wrong view is drawn, at\n// coordinates that belong to somebody else's genome.\n//\n// That is not hypothetical for an unpaired graph, which by the rule above\n// matches every linear view in the session. A synteny stack's rows are LGVs on\n// different assemblies, and a PanSN name reduces to a bare contig: the five\n// E. coli strains each have one refName `chr`, so a graph cut from K12 painted\n// its K12 interval across Sakai's row and CFT073's at their own offsets. With\n// the assembly checked the reference row keeps the band and the rest correctly\n// draw nothing.\n//\n// A region stating no assembly still passes \u2014 `readRegion` leaves it optional,\n// and the fallback is the behaviour every existing snapshot has.\nfunction isOnAssembly(\n region: HighlightRegion,\n assemblyNames: string[] | undefined,\n) {\n return (\n region.assemblyName === undefined ||\n assemblyNames === undefined ||\n assemblyNames.includes(region.assemblyName)\n )\n}\n\n// The highlights a linear view should draw for the graph views connected to it.\n// Reads `session.views` structurally: the members it needs are declared by\n// GraphGenomeView, not by the AbstractViewModel the session array is typed as.\nexport function graphViewHighlights(\n views: unknown[],\n linearViewId: string,\n // the drawing view's own assemblies, so a band is only drawn where its\n // coordinates mean something \u2014 see isOnAssembly\n linearAssemblyNames?: string[],\n): GraphViewHighlight[] {\n const highlights: GraphViewHighlight[] = []\n const liveViewIds = new Set(\n withRows(views).map(view => (isRecord(view) ? view.id : undefined)),\n )\n for (const view of views) {\n if (\n isRecord(view) &&\n view.type === 'GraphGenomeView' &&\n isConnected(view, linearViewId, liveViewIds)\n ) {\n const region = readRegion(view.hoverHighlight)\n if (region && isOnAssembly(region, linearAssemblyNames)) {\n highlights.push({\n key: typeof view.id === 'string' ? view.id : 'graph',\n region,\n })\n }\n }\n }\n return highlights\n}\n", "import GraphNodeHighlight from './GraphNodeHighlight'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\n// The graph-to-linear half of the hover sync. The reverse half needs no mount\n// point: an LGV already publishes its hover to `session.hovered`, and the graph\n// view's own autorun reads it (see GraphGenomeView/model.ts).\n//\n// Mounted through the extension point rather than imported by the LGV, so this\n// plugin never imports @jbrowse/plugin-linear-genome-view at runtime: it is not\n// one of JBrowse's shared globals, so a value import would bundle a second copy\n// of that whole plugin.\nconst POINT = 'LinearGenomeView-TracksContainerComponent'\n\nexport default function GraphHoverSyncF(pluginManager: PluginManager) {\n pluginManager.contributeToExtensionPoint(POINT, ({ model }) => (\n <GraphNodeHighlight key=\"graphgenomeview-hover-highlight\" model={model} />\n ))\n}\n", "// The name JBrowse knows this plugin by, which a config's `plugins` entry\n// states beside its `esmUrl`.\nexport const PLUGIN_NAME = 'GraphGenomeView'\n", "export const version = '4.0.0'\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["require_Plugin", "__commonJSMin", "exports", "module", "require_AdapterType", "__commonJSMin", "exports", "module", "require_configuration", "__commonJSMin", "exports", "module", "require_dataAdapterCache", "__commonJSMin", "exports", "module", "require_pluggableElementTypes", "__commonJSMin", "exports", "module", "require_ViewType", "__commonJSMin", "exports", "module", "require_withLaunchInput", "__commonJSMin", "exports", "module", "require_models", "__commonJSMin", "exports", "module", "require_RenderLifecycleMixin", "__commonJSMin", "exports", "module", "require_mobx", "__commonJSMin", "exports", "module", "require_DisplayType", "__commonJSMin", "exports", "module", "require_trackHeightConfigSchemaFields", "__commonJSMin", "exports", "module", "require_TrackHeightMixin", "__commonJSMin", "exports", "module", "import_Plugin", "import_util", "import_jsx_runtime", "BubbleChart_default", "_jsx", "import_AdapterType", "import_configuration", "import_mobx_state_tree", "GbzBaseSyntenyAdapter", "snap", "configSchema_default", "GbzBaseSyntenyAdapterF", "pluginManager", "AdapterType", "configSchema_default", "r", "import_dataAdapterCache", "import_pluggableElementTypes", "isSubgraphAdapter", "adapter", "GetSubgraph", "args", "adapterConfig", "region", "sessionId", "opts", "signal", "dataAdapter", "import_react", "import_pluggableElementTypes", "import_mobx_state_tree", "GraphAddTrackWidget", "GraphAddTrackWorkflowF", "pluginManager", "import_pluggableElementTypes", "cached", "loadBandage", "mod", "GraphComputeLayout", "args", "graph", "options", "statusCallback", "module", "loadBandage", "startTime", "result", "duration", "import_react", "import_ViewType", "import_withLaunchInput", "import_configuration", "import_models", "import_ui", "import_util", "import_io", "import_mobx_state_tree", "import_RenderLifecycleMixin", "import_canvas2dUtils", "import_mobx", "WHOLE_FRACTION", "NAMED_WALKS", "MIN_CHIPPED_BP", "bubbleHalos", "graph", "bubbles", "positions", "walkLabel", "name", "byName", "n", "halos", "bubble", "parts", "top", "nodeIds", "ends", "bubbleSegmentIds", "node", "line", "isBackbone", "svgPath", "p", "anchor", "s", "sharing", "route", "id", "routes", "rarest", "at", "far", "d", "routeText", "classifyBubble", "names", "shown", "more", "formatBp", "bubblesFromGraph", "graph", "isBackbone", "byId", "n", "order", "referenceOrder", "indexOf", "id", "i", "reach", "succ", "linked", "e", "a", "lo", "hi", "from", "to", "backbone", "b", "ends", "boundaries", "node", "ia", "ib", "walkIndex", "at", "bubbles", "start", "i0", "end", "i1", "interior", "best", "cur", "t", "add", "prev", "SATURATED_PATH_COUNT", "walked", "walkRoutes", "crossed", "routes", "walksLeave", "refStart", "refEnd", "chain", "chainBroken", "k", "partial", "fallback", "startId", "endId", "seen", "min", "max", "left", "p", "steps", "bp", "key", "route", "bubbleSubgraph", "graph", "segmentIds", "keep", "nodes", "n", "ids", "paths", "p", "id", "e", "GENE_ADAPTER_TYPES", "GENE_TRACK_HINT", "pickGeneTrack", "tracks", "named", "t", "candidates", "field", "f", "name", "exonsOf", "into", "children", "child", "merged", "intervals", "sorted", "a", "b", "out", "iv", "last", "geneModelsFrom", "features", "genes", "start", "end", "exons", "contig", "name", "genePins", "graph", "genes", "positions", "byContig", "contigOf", "node", "isBackbone", "refName", "entry", "nodes", "a", "b", "pins", "gene", "backbone", "parts", "at", "atDistance", "covered", "mid", "reach", "i", "firstNodeAtOrAfter", "nodeStart", "nodeEnd", "line", "exon", "stretch", "polylineSlice", "svgPath", "pinBp", "distance", "p", "parseTag", "tag", "tags", "nameEnd", "typeEnd", "name", "type", "val", "parseTags", "fields", "field", "stableCoordinate", "node", "SN", "SO", "SR", "GT", "LT", "parseWalkBody", "body", "segments", "strand", "start", "push", "end", "i", "ch", "parseGFA", "file", "graph", "rawLine", "line", "rest", "len", "seq", "tagfields", "gfa1", "first", "source", "target", "cigar", "strand1", "strand2", "sample", "hap", "contig", "path", "surveySegments", "gfaGraph", "canonical", "traversals", "claim", "id", "strand", "traverse", "link", "gfaPath", "segment", "walk", "seg", "anchorablePaths", "p", "pathOrigin", "w", "s", "carriedSamples", "gfaNode", "sm", "samples", "positive", "v", "statedDepth", "tags", "length", "count", "makeNode", "depth", "stableCoordinate", "convertGFAToGraph", "name", "nodes", "edges", "nodeId", "segmentId", "paths", "edgeToPathsMap", "edgeKey", "a", "recordPathEdges", "nodeIds", "i", "key", "edge", "pathIds", "lengths", "n", "anchoring", "surveyPaths", "isLinearHost", "view", "v", "widest", "blocks", "best", "b", "hostFrame", "region", "block", "bpPerPx", "hostWindow", "displayed", "hostCut", "window", "capBp", "visible", "margin", "cutHolds", "cut", "import_configuration", "import_mobx_state_tree", "OFF_REFERENCE_COLOR", "referencePositionColor", "start", "end", "hue", "isLaneView", "view", "sourceLaneDisplay", "views", "connectedViewId", "trackId", "display", "t", "paintSourceLane", "color", "current", "orientToReference", "graph", "positions", "backbone", "node", "isBackbone", "points", "segments", "mid", "weight", "sum", "p", "cx", "cy", "cr", "sxr", "syr", "angle", "c", "s", "out", "id", "x", "y", "below", "above", "LANE_GAP", "referenceSeeds", "graph", "scaling", "opts", "drawn", "node", "drawnNodeLength", "drawnOf", "id", "backbone", "isBackbone", "a", "b", "seeds", "x", "adjacent", "buildNeighbors", "depth", "queue", "at", "next", "neighbour", "unreached", "seededNodes", "REPEAT_ADAPTER_TYPES", "REPEAT_TRACK_HINT", "pickRepeatTrack", "tracks", "named", "t", "own", "f", "name", "PACKED_NAME", "packedName", "pair", "i", "field", "top", "MOTIF_FIELDS", "PERIOD_FIELDS", "NAME_FIELDS", "first", "names", "value", "one", "IUPAC", "repeatUnitOf", "period", "motif", "numbers", "n", "callsOf", "samples", "called", "sample", "fields", "reads", "alleles", "bp", "repeatArraysFrom", "features", "arrays", "start", "end", "unit", "refName", "stated", "a", "b", "walkHighlight", "graph", "name", "path", "p", "byId", "n", "edgeAt", "e", "i", "nodeIds", "edgeIndexes", "a", "b", "index", "bpOf", "ids", "sum", "id", "reference", "pathOrigin", "isRecord", "value", "isFeatureLike", "hoveredName", "feature", "name", "readLgvHover", "hovered", "result", "refName", "coord", "coord0", "assemblyName", "hoverInRegion", "hover", "region", "nodeForLgvHover", "nodes", "byName", "byCoord", "node", "isBackbone", "import_configuration", "isRecord", "value", "isLinearView", "view", "withRows", "views", "linearViewTarget", "connectedViewId", "assemblyName", "candidates", "reportFailure", "session", "what", "launch", "error", "MIN_LAUNCH_SPAN_BP", "paddedLocation", "loc", "minSpan", "pad", "withReferenceRegion", "contributors", "region", "c", "showInLinearView", "location", "assembly", "connectedViewId", "tracks", "existing", "linearViewTarget", "viewId", "locLabel", "locString", "highlightInLinearView", "target", "assemblyNamesOfTrack", "trackId", "track", "t", "names", "n", "launchSyntenyView", "graphTrackId", "graphTrackAssemblies", "aligned", "covered", "panels", "snapshot", "import_configuration", "ANNOTATION_TRACK_TYPE", "launchTracks", "session", "assemblyName", "first", "found", "track", "type", "assemblyNames", "trackId", "import_configuration", "import_tracks", "SYNTENY_TRACK_TYPE", "launchableSyntenyTracks", "session", "assemblyNames", "wanted", "found", "track", "type", "trackAssemblies", "trackId", "coverage", "name", "a", "b", "import_configuration", "import_tracks", "isStringArray", "value", "item", "graphReferenceAssembly", "track", "assemblyNames", "isStringArray", "offReferenceProblem", "referenceAssembly", "assemblyName", "MAX_CANVAS_HEIGHT", "MIN_CANVAS_HEIGHT", "VARIANT_MAP_HEIGHT", "SEGMENTS_SUFFIX", "bubblePrefix", "adapterConfig", "uri", "FIT_PADDING", "HOVER_BRIGHTEN", "SELECT_BRIGHTEN", "VIEWPORT_DEBOUNCE_MS", "VIEWPORT_PANES_BUILT", "dependOn", "_values", "MAX_GRAPH_REGION_BP", "formatSpanBp", "bp", "DEFAULT_MAX_GRAPH_NODES", "MIN_ZOOM", "MAX_ZOOM", "clampZoom", "zoom", "axisScaleOf", "scale", "pixelRows", "viewportOf", "model", "padded", "v", "panes", "w", "h", "fittedTranslateY", "bounds", "usableHeight", "yScale", "leftoverY", "contains", "outer", "inner", "forceLayouts", "FORCE_LAYOUT_CACHE_SIZE", "forceLayoutsOf", "graph", "cache", "inheritForceLayouts", "from", "to", "remember", "key", "result", "stateModelFactory", "LAYOUT_MODE_VALUES", "COLOR_SCHEME_VALUES", "BUBBLE_SPREAD_VALUES", "NODE_WIDTH_VALUES", "self", "m", "modeUsesLayoutEngine", "n", "paths", "pathColorsLegible", "pathLegend", "walkHighlight", "bubblesFromGraph", "track", "t", "region", "GENE_ADAPTER_TYPES", "REPEAT_ADAPTER_TYPES", "pickRepeatTrack", "r", "buildNeighbors", "contigThickness", "nodeWidth", "nodeById", "mean", "meanDepth", "maxNodeWidthPx", "id", "node", "nodeWidthPx", "deletionEdges", "repeat", "bars", "walkRows", "samples", "rows", "b", "withCalls", "row", "i", "bubble", "classifyBubble", "positions", "genePins", "labels", "c", "bubbleHalos", "name", "computeReferenceRamp", "minX", "minY", "maxX", "maxY", "segments", "seg", "extent", "walkRowsExtent", "usableWidth", "ceiling", "floor", "pickGeneTrack", "d", "ramp", "nodeId", "neighbors", "span", "nodeReferenceSpan", "backbone", "backboneNodes", "contributingAssemblies", "backboneSpan", "assemblyManager", "panSN", "byPrefix", "asm", "prefix", "loaded", "sample", "resolveContributors", "withReferenceRegion", "linearViewTarget", "own", "nodeOwnLocation", "ownAssembly", "resolveLocationAssembly", "onReference", "paddedLocation", "above", "view", "isLinearHost", "host", "layout", "loadedRegion", "hostFrame", "error", "message", "ms", "strokeCount", "built", "quality", "linear", "mode", "reanchored", "anchorFromPaths", "draw", "show", "scheme", "spread", "width", "trackId", "px", "hops", "haplotypes", "viewId", "paired", "withRows", "edgeIdx", "panning", "session", "s", "tx", "ty", "factor", "centerX", "centerY", "newScale", "ratio", "fitX", "fitY", "translateX", "engaging", "scheduleViewportDirty", "requestPositionsDirtyFrame", "dx", "dy", "loadController", "callLayout", "scaling", "rpcManager", "sessionId", "anchored", "isBackbone", "seededNodes", "status", "forceLayoutKey", "computeLayout", "start", "local", "layoutModeByValue", "hit", "spreadFor", "merged", "mergeRuns", "duration", "layoutScaling", "drawn", "drawnNodeLength", "splitRuns", "oriented", "orientToReference", "liveRequest", "layoutInto", "request", "isLive", "signal", "computed", "e", "live", "parseAndLayout", "text", "keepSelection", "gfaGraph", "parseGFA", "anchorGraph", "convertGFAToGraph", "selected", "liveLoad", "beginLoad", "load", "loadedTrack", "loadBubbles", "features", "f", "trackFeatures", "what", "config", "loadGenes", "geneModelsFrom", "loadRepeats", "repeatArraysFrom", "doSubgraphLoad", "opts", "regionSize", "refusal", "offReferenceProblem", "graphReferenceAssembly", "fetchStart", "gfaText", "label", "locLabel", "cutFromLoadedTrack", "coarse", "loadWholeGFA", "source", "location", "stated", "sub", "bubbleSubgraph", "bubbleSegmentIds", "BUBBLE_KIND_NAMES", "seen", "tier", "cap", "visible", "cutHolds", "hostCut", "places", "frame", "a", "blocks", "hostWindow", "backend", "domain", "display", "sourceLaneDisplay", "paintSourceLane", "referencePositionColor", "hover", "readLgvHover", "hoverInRegion", "nodeForLgvHover", "firstViewport", "viewport", "hoveredNode", "hoveredEdge", "selectedNode", "nodes", "geometryStart", "viewportBounds", "batch", "buildGeometry", "dpr", "target", "showInLinearView", "launchTracks", "highlightInLinearView", "launchSyntenyView", "launchableSyntenyTracks", "items", "item", "graphLaunchMenuItems", "graphLaunchKeys", "GraphGenomeViewF", "pluginManager", "ViewType", "stateModelFactory", "LaunchGraphGenomeViewF", "pluginManager", "args", "session", "spec", "import_react", "import_DisplayType", "import_configuration", "import_trackHeightConfigSchemaFields", "import_mobx_state_tree", "configSchemaFactory", "LAYOUT_MODE_VALUES", "COLOR_SCHEME_VALUES", "import_react", "import_configuration", "import_pluggableElementTypes", "import_util", "import_TrackHeightMixin", "import_mobx_state_tree", "import_mobx", "GraphSettingsDialog", "stateModelFactory", "configSchema", "Pane", "TrackHeightMixin", "self", "view", "isLinearHost", "pane", "graph", "walks", "LAYOUT_MODES", "mode", "COLOR_SCHEMES", "scheme", "walk", "Settings_default", "onClose", "distance", "before", "target", "host", "height", "LinearGraphDisplayF", "pluginManager", "configSchema", "configSchemaFactory", "DisplayType", "stateModelFactory", "import_AdapterType", "import_configuration", "import_mobx_state_tree", "normalizeSnapshot", "snap", "uri", "baseUri", "csi", "MinigraphBubbleAdapter", "configSchema_default", "MinigraphBubbleAdapterF", "pluginManager", "AdapterType", "normalizeSnapshot", "configSchema_default", "r", "import_AdapterType", "import_configuration", "import_mobx_state_tree", "tbi", "uri", "baseUri", "csi", "prefixLocations", "snap", "normalizeSnapshot", "coarse", "indexSchema", "name", "defaultUri", "RgfaTabixCoarseTier", "RgfaTabixAdapter", "configSchema_default", "RgfaTabixAdapterF", "pluginManager", "AdapterType", "normalizeSnapshot", "configSchema_default", "r", "import_util", "import_material", "import_mobx_react", "import_mui", "isRecord", "value", "readRegion", "region", "refName", "start", "end", "assemblyName", "isConnected", "view", "linearViewId", "liveViewIds", "connectedViewId", "isOnAssembly", "assemblyNames", "graphViewHighlights", "views", "linearAssemblyNames", "highlights", "withRows", "import_jsx_runtime", "useStyles", "theme", "Highlight", "model", "region", "classes", "coords", "GraphNodeHighlight", "views", "graphViewHighlights", "key", "GraphNodeHighlight_default", "import_jsx_runtime", "POINT", "GraphHoverSyncF", "pluginManager", "model", "GraphNodeHighlight_default", "PLUGIN_NAME", "version", "GraphGenomeViewPlugin", "Plugin", "PLUGIN_NAME", "version", "pluginManager", "GraphGenomeViewF", "LinearGraphDisplayF", "LaunchGraphGenomeViewF", "GraphHoverSyncF", "RgfaTabixAdapterF", "MinigraphBubbleAdapterF", "GbzBaseSyntenyAdapterF", "GraphAddTrackWorkflowF", "GraphComputeLayout", "GetSubgraph", "BubbleChart_default", "session", "e"]
|
|
4
|
+
"sourcesContent": ["module.exports = JBrowseExports[\"@jbrowse/core/Plugin\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/AdapterType\"];", "module.exports = JBrowseExports[\"@jbrowse/core/configuration\"];", "module.exports = JBrowseExports[\"@jbrowse/core/data_adapters/dataAdapterCache\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/ViewType\"];", "module.exports = JBrowseExports[\"@jbrowse/core/util/withLaunchInput\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/models\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/RenderLifecycleMixin\"];", "module.exports = JBrowseExports[\"mobx\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/DisplayType\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/trackHeightConfigSchemaFields\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/TrackHeightMixin\"];", "import Plugin from '@jbrowse/core/Plugin'\nimport { isAbstractMenuManager } from '@jbrowse/core/util'\nimport BubbleChartIcon from '@mui/icons-material/BubbleChart'\n\nimport GbzBaseSyntenyAdapterF from './GbzBaseSyntenyAdapter/index'\nimport GetSubgraph from './GetSubgraph'\nimport GraphAddTrackWorkflowF from './GraphAddTrackWorkflow/index'\nimport GraphComputeLayout from './GraphComputeLayout'\nimport GraphGenomeViewF from './GraphGenomeView/index'\nimport LaunchGraphGenomeViewF from './LaunchGraphGenomeView'\nimport LinearGraphDisplayF from './LinearGraphDisplay/index'\nimport MinigraphBubbleAdapterF from './MinigraphBubbleAdapter/index'\nimport RgfaTabixAdapterF from './RgfaTabixAdapter/index'\nimport GraphHoverSyncF from './hoverSync/index'\nimport { PLUGIN_NAME } from './pluginName'\nimport { version } from './version'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\n\nexport default class GraphGenomeViewPlugin extends Plugin {\n name = PLUGIN_NAME\n version = version\n\n install(pluginManager: PluginManager) {\n GraphGenomeViewF(pluginManager)\n LinearGraphDisplayF(pluginManager)\n LaunchGraphGenomeViewF(pluginManager)\n GraphHoverSyncF(pluginManager)\n RgfaTabixAdapterF(pluginManager)\n MinigraphBubbleAdapterF(pluginManager)\n GbzBaseSyntenyAdapterF(pluginManager)\n GraphAddTrackWorkflowF(pluginManager)\n pluginManager.addRpcMethod(() => new GraphComputeLayout(pluginManager))\n pluginManager.addRpcMethod(() => new GetSubgraph(pluginManager))\n }\n\n // A throw from configure() takes the whole session to its error page, and a\n // menu entry is not worth that: hubs 1.0.9 did exactly this to every\n // jbrowse.org/ucsc launch by appending to a menu a released host defines as\n // a function. The view is still reachable without the entry.\n configure(pluginManager: PluginManager) {\n try {\n if (isAbstractMenuManager(pluginManager.rootModel)) {\n pluginManager.rootModel.appendToSubMenu(['Add'], {\n label: 'Graph genome view',\n icon: BubbleChartIcon,\n onClick: (session: AbstractSessionModel) => {\n session.addView('GraphGenomeView', {})\n },\n })\n }\n } catch (e) {\n console.warn('[GraphGenomeView] could not add the Add menu entry', e)\n }\n }\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon([/*#__PURE__*/_jsx(\"circle\", {\n cx: \"7.2\",\n cy: \"14.4\",\n r: \"3.2\"\n}, \"0\"), /*#__PURE__*/_jsx(\"circle\", {\n cx: \"14.8\",\n cy: \"18\",\n r: \"2\"\n}, \"1\"), /*#__PURE__*/_jsx(\"circle\", {\n cx: \"15.2\",\n cy: \"8.8\",\n r: \"4.8\"\n}, \"2\")], 'BubbleChart');", "import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'\n\nimport configSchema from './configSchema.ts'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function GbzBaseSyntenyAdapterF(pluginManager: PluginManager) {\n pluginManager.addAdapterType(\n () =>\n new AdapterType({\n name: 'GbzBaseSyntenyAdapter',\n displayName: 'gbz-base pangenome adapter',\n configSchema,\n // The same opened database serves the graph view's subgraph, so the\n // launch menu offers this track too (launchSubgraph/subgraphTracks).\n // headerLanes: getHeader declares every haplotype as a lane, which is\n // what makes MultiWaySyntenyDisplay read the header of an untiered\n // adapter and offer its lane picker over the whole graph.\n // lanePairsOnAnchor: a window of the anchor answers any two lanes'\n // alignment to each other, so the display fetches each adjacent pair\n // rather than composing it through the reference\n adapterCapabilities: [\n 'getSubgraph',\n 'headerLanes',\n 'lanePairsOnAnchor',\n ],\n adapterMetadata: {\n category: 'Synteny adapters',\n },\n getAdapterClass: () =>\n import('./GbzBaseSyntenyAdapter.ts').then(r => r.default),\n }),\n )\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config GbzBaseSyntenyAdapter\n * #trackType SyntenyTrack\n * #fileFormat synteny | gbz-base pangenome database | Haplotype alignments read from the graph at query time, no PAF conversion\n * Serves a pangenome graph stored as a gbz-base SQLite database (`.gbz.db`) as\n * haplotype-versus-reference alignments, the shape `MultiWaySyntenyDisplay`\n * draws as one lane per haplotype. A window on the reference is located on the\n * graph's reference path, every haplotype's walk through the nodes it covers\n * is recovered and named, and each haplotype becomes one alignment record with\n * a CIGAR, so the graph is read through HTTP range requests with no offline\n * conversion. The database has to carry the `HaplotypeSamples` and\n * `HaplotypeLengths` side tables that `gbz-haplotype-index` (shipped with\n * `@gmod/gbz-base`) writes into an upstream-built database; without them the\n * graph cannot say which haplotype a walk belongs to.\n *\n * Only the fine tier exists: a whole-chromosome view stays on a PIF's coarse\n * tier. A query on a haplotype lane answers nothing, so the multi-way display\n * composes its lane-to-lane links through the reference.\n *\n * #example\n * ```js\n * {\n * type: 'GbzBaseSyntenyAdapter',\n * uri: 'hprc-v2.gbz.db',\n * assemblyNames: ['hg38'],\n * assemblyNameToPanSN: { hg38: 'GRCh38#0' },\n * }\n * ```\n */\nconst GbzBaseSyntenyAdapter = ConfigurationSchema(\n 'GbzBaseSyntenyAdapter',\n {\n /**\n * #slot\n * The first entry is the anchor: the JBrowse assembly the graph's reference\n * sample is loaded as. Further entries are haplotypes loaded as JBrowse\n * assemblies under their PanSN prefix (`HG002#1`); one loaded under another\n * name only needs its `assemblyNameToPanSN` entry. A haplotype named in\n * neither is still a lane, labelled by its PanSN prefix.\n */\n assemblyNames: {\n type: 'stringArray',\n defaultValue: [],\n },\n /**\n * #slot\n * The `.gbz.db` written by `gbz-base construct` and augmented by\n * `gbz-haplotype-index`; read by HTTP range requests, so it can be large.\n */\n gbzDbLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.gbz.db',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n * A companion database written by `gbz-haplotype-index --output`, holding\n * the `HaplotypeSamples` and `HaplotypeLengths` tables for a graph database\n * that does not carry them itself, such as the one HPRC publishes. One\n * built with anchor rows (the tool's default) lets a fetch for a chosen\n * set of lanes walk only those haplotypes. Empty means the graph database\n * carries the tables.\n */\n haplotypeIndexLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n * How the graph view's subgraph is extended past the nodes the reference\n * window touches. `contained` adds every top-level snarl with both\n * boundary nodes in the window, which is what brings back the bubbles a\n * reference-only walk leaves out; a haplotype still breaks into a new W\n * line wherever it leaves the window's nodes (a 60 kb C4 window on the\n * HPRC graph: 2,703 nodes, 8,083 walks for 464 haplotypes). `overlapping`\n * also follows snarls that leave the window, so every haplotype is one\n * walk (464), at the price of every node in those snarls (4,184 there,\n * 24,547 across the KIV-2 repeat). `none` leaves only the bp `context`.\n */\n subgraphSnarls: {\n type: 'stringEnum',\n model: types.enumeration('SnarlOutput', [\n 'none',\n 'contained',\n 'overlapping',\n ]),\n defaultValue: 'contained',\n },\n /**\n * #slot\n * The graph sample the anchor window is located on. Empty means the sample\n * the anchor's PanSN prefix names, or the database's one reference sample\n * (`gbwt_reference_samples`) when the anchor names none; a database with\n * several reference samples then needs this set.\n */\n referenceSample: {\n type: 'string',\n defaultValue: '',\n },\n /**\n * #slot\n * Maps a JBrowse assembly name to its PanSN prefix in the graph, sample or\n * haplotype level (`{ hg38: 'GRCh38#0', 'HG002.1': 'HG002#1' }`). Defaults\n * to identity.\n */\n assemblyNameToPanSN: {\n type: 'frozen',\n defaultValue: {},\n },\n /**\n * #slot\n * Graph context around the reference walk, in bp, on top of\n * `subgraphSnarls`. Since `@gmod/gbz-base` joins the pieces of a walk that\n * leaves the window's nodes and comes back (the private stretch becomes the\n * record's insertion, the skipped reference its deletion), a lane's record\n * count is one per haplotype at any context; what context trades is nodes\n * read against pieces to identify and join. A window inside a snarl far\n * larger than itself (MHC class II on the HPRC graph, 90 kb) is 1.1M pieces\n * at 0 and 464 walks at 1000, three times faster; a window whose private\n * stretches are short bubbles costs about the same either way. For the\n * graph view's cut it is simply how far past the window the cut extends.\n */\n context: {\n type: 'number',\n defaultValue: 1000,\n advanced: true,\n },\n /**\n * #slot\n * The most graph nodes one window may load before the query fails rather\n * than reading a whole chromosome into the worker.\n */\n nodeLimit: {\n type: 'number',\n defaultValue: 100000,\n advanced: true,\n },\n },\n {\n explicitlyTyped: true,\n\n /**\n * #preProcessSnapshot\n *\n *\n * preprocessor to allow minimal config:\n * ```json\n * {\n * \"type\": \"GbzBaseSyntenyAdapter\",\n * \"uri\": \"graph.gbz.db\",\n * \"assemblyNames\": [\"hg38\"]\n * }\n * ```\n */\n preProcessSnapshot: snap => {\n return snap.uri\n ? {\n ...snap,\n gbzDbLocation: {\n uri: snap.uri,\n baseUri: snap.baseUri,\n },\n }\n : snap\n },\n },\n)\n\nexport type GbzBaseSyntenyAdapterConfig = Instance<typeof GbzBaseSyntenyAdapter>\n\nexport default GbzBaseSyntenyAdapter\n", "import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'\nimport { RpcMethodTypeWithRenameRegion } from '@jbrowse/core/pluggableElementTypes'\n\nimport type { RpcExecuteArgs } from '@jbrowse/core/rpc/RpcRegistry'\nimport type { Region } from '@jbrowse/core/util'\n\n// No `sessionId`, `signal` or `statusCallback` here: those belong to the\n// CALL, not to this payload, and core's `EntriesDeclaringCallLevelFields` fails\n// the build for a registry entry that declares one. `execute` still receives\n// them, because `RpcExecuteArgs` intersects `RpcCallContext` in.\n// `hops` is how far past the window a cut follows links, which only the rGFA\n// cut reads; the GBZ cut has its own bp `context` slot. `haplotypes` is the\n// set the cut is for, lane assembly names or PanSN prefixes, which only the GBZ\n// cut reads: it keeps those walks and the nodes they visit. Undefined or empty\n// is every haplotype. `tier` picks which of an rGFA track's two index pairs the\n// cut reads, the adapter's own or its `coarse` one; fine when absent.\nexport type SubgraphTier = 'fine' | 'coarse'\n\nexport interface SubgraphCutOptions {\n hops?: number\n haplotypes?: string[]\n tier?: SubgraphTier\n}\n\n// What an adapter's cut is handed: the payload, plus the call's own signal so\n// a cut the view has moved on from stops reading.\nexport interface SubgraphAdapterOptions extends SubgraphCutOptions {\n signal?: AbortSignal\n}\n\nexport interface GetSubgraphArgs {\n adapterConfig: Record<string, unknown>\n region: Region\n opts?: SubgraphCutOptions\n}\n\ndeclare module '@jbrowse/core/rpc/RpcRegistry' {\n interface RpcRegistry {\n GetSubgraph: {\n args: GetSubgraphArgs\n return: string\n }\n }\n}\n\n// Adapters that can cut a local subgraph out of a graph file implement this:\n// RgfaTabixAdapter and GbzBaseSyntenyAdapter today.\ninterface SubgraphAdapter {\n getSubgraph(region: Region, opts?: SubgraphAdapterOptions): Promise<string>\n}\n\nfunction isSubgraphAdapter(adapter: object): adapter is SubgraphAdapter {\n return (\n 'getSubgraph' in adapter &&\n typeof (adapter as SubgraphAdapter).getSubgraph === 'function'\n )\n}\n\n// RpcMethodTypeWithRenameRegion, not the plain RpcMethodType: the base class is\n// what maps `region.refName` from the assembly's spelling onto the adapter's own\n// before the call crosses to the worker. Without it a launch on an hg38 whose\n// contigs are bare (`6`, which is what every GRCh38 FASTA on jbrowse.org uses)\n// asked RgfaTabixAdapter for `GRCh38#0#6`, matched nothing, and opened a view\n// reading \"0 nodes, 0 edges\" with no error. The segments track drew the whole\n// time, because CoreGetFeatures renames and this did not, so the graph looked\n// broken while its own track looked fine. `assemblyNameToPanSN` covers the\n// sample half of a PanSN name only; the contig half is refName aliasing, which\n// the assembly already knows and this now consults.\nexport default class GetSubgraph extends RpcMethodTypeWithRenameRegion<'GetSubgraph'> {\n name = 'GetSubgraph' as const\n\n // Parameterized by the registry key, not left at the default `string`: a bare\n // RpcMethodTypeWithRenameRegion resolves RpcExecuteArgs to `unknown`, so\n // `execute` type-checks against nothing at all. `invoke` has already run\n // `deserializeArguments` by the time this is called, so it does not.\n async execute(args: RpcExecuteArgs<'GetSubgraph'>) {\n // `signal` arrives on every call and has to be named to be forwarded:\n // unnamed, a cut the view had replaced went on querying to the end, one\n // tabix read per off-reference segment per hop.\n const { adapterConfig, region, sessionId, opts, signal } = args\n\n const { dataAdapter } = await getAdapter(\n this.pluginManager,\n sessionId,\n adapterConfig,\n )\n if (isSubgraphAdapter(dataAdapter)) {\n return dataAdapter.getSubgraph(region, { ...opts, signal })\n }\n // An empty result is how the view and the launch menu detect \"this track\n // can't do subgraphs\" \u2014 see GraphGenomeView.loadFromTabixSubgraph.\n console.warn(\n `[GetSubgraph] ${dataAdapter.constructor.name} does not implement getSubgraph`,\n )\n return ''\n }\n}\n", "import { lazy } from 'react'\n\nimport { AddTrackWorkflowType } from '@jbrowse/core/pluggableElementTypes'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nconst GraphAddTrackWidget = lazy(() => import('./AddTrackWorkflow'))\n\nexport default function GraphAddTrackWorkflowF(pluginManager: PluginManager) {\n pluginManager.addAddTrackWorkflowType(\n () =>\n new AddTrackWorkflowType({\n name: 'Pangenome graph track',\n displayName: 'Add pangenome graph track',\n ReactComponent: GraphAddTrackWidget,\n stateModel: types.model({}),\n }),\n )\n}\n", "import { RpcMethodType } from '@jbrowse/core/pluggableElementTypes'\n\nimport loadBandage from './loadBandage'\n\nimport type { LayoutNode } from './GraphGenomeView/layout/referenceSeeds'\nimport type { Graph, LayoutResult } from './GraphGenomeView/types'\nimport type { RpcExecuteArgs } from '@jbrowse/core/rpc/RpcRegistry'\n\n// `sessionId` and `statusCallback` are the CALL's, not this payload's \u2014 core's\n// `EntriesDeclaringCallLevelFields` fails the build for a registry entry that\n// declares either, on the grounds that a field one method owns is a field the\n// other forty cannot be passed. `execute` still receives both, through\n// `RpcExecuteArgs`'s intersection with `RpcCallContext`.\nexport interface GraphComputeLayoutArgs {\n // a node may carry an `x`/`y` seed, which the engine reads as where its\n // chain starts (referenceSeeds.ts)\n graph: { nodes: LayoutNode[]; edges: Graph['edges'] }\n options: Record<string, unknown>\n}\n\ndeclare module '@jbrowse/core/rpc/RpcRegistry' {\n interface RpcRegistry {\n GraphComputeLayout: {\n args: GraphComputeLayoutArgs\n return: { result: LayoutResult; duration: number }\n }\n }\n}\n\nexport default class GraphComputeLayout extends RpcMethodType<'GraphComputeLayout'> {\n name = 'GraphComputeLayout' as const\n\n async execute(args: RpcExecuteArgs<'GraphComputeLayout'>) {\n const { graph, options, statusCallback } = args\n\n statusCallback?.('Loading layout engine')\n const module = await loadBandage()\n\n statusCallback?.('Computing layout')\n const startTime = performance.now()\n const result = module.computeLayout(graph, options)\n const duration = performance.now() - startTime\n\n return { result, duration }\n }\n}\n", "export type { BandageModule } from './bandage/bandage-layout'\n\nimport type { BandageModule } from './bandage/bandage-layout'\n\n// The Bandage engine is a ~425kb self-contained ES module (Emscripten\n// -sSINGLE_FILE=1, wasm inlined as base64). A plain dynamic import lets the\n// bundler emit it as a sibling chunk that the browser resolves relative to this\n// module's own url via import.meta.url \u2014 so it downloads only when a\n// force-directed layout is requested, and works identically on the main thread\n// and in the RPC worker with no url plumbing.\nlet cached: Promise<BandageModule> | undefined\n\nexport default function loadBandage() {\n cached ??= import('./bandage/bandage-layout')\n .then(mod => mod.default())\n .catch((e: unknown) => {\n // let a later attempt retry rather than caching the failure forever\n cached = undefined\n throw e\n })\n return cached\n}\n", "import { lazy } from 'react'\n\nimport ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'\nimport { defineLaunchKeys } from '@jbrowse/core/util/withLaunchInput'\n\nimport modelFactory from './model'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\n// This view has NO launch keys, and saying so is what turns the check on.\n//\n// A launch key is a spec field the launcher interprets rather than assigns \u2014 an\n// LGV's `assembly` and `loc`, resolved into `displayedRegions`. Every field of\n// a graph launch is a plain persisted prop instead (see\n// LaunchGraphGenomeView), so the registration is empty; what it buys is\n// `ViewType.acceptedKeys`, which is `undefined` until a view declares its\n// launch vocabulary, and which `loadSessionSpec` needs to report\n// `ignored unknown key(s)`.\n//\n// Without it a session spec's typo in a GraphGenomeView entry is silent: the\n// key lands in the snapshot, MST drops it, and the view opens on the import\n// form with nothing said. That surface is written by hand with no compiler\n// behind it \u2014 `sessionSpec()` takes a bare `object` \u2014 and the docs build two\n// dozen graph figures through it, `loadedRegion` and `loadedTrackId` among the\n// fields a misspelling would silently cost.\n//\n// Ignored by a host that predates `acceptedKeys`: its ViewType constructor\n// reads the options it knows and this is not one of them.\nconst graphLaunchKeys = defineLaunchKeys<object>()({})\n\nexport default function GraphGenomeViewF(pluginManager: PluginManager) {\n pluginManager.addViewType(() => {\n return new ViewType({\n name: 'GraphGenomeView',\n displayName: 'Graph genome view',\n stateModel: modelFactory(),\n launchKeys: graphLaunchKeys,\n ReactComponent: lazy(() => import('./components/GraphGenomeView')),\n })\n })\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'\nimport { pushLaunchViewMenuItem } from '@jbrowse/core/ui'\nimport {\n getContainingView,\n getSession,\n isSessionModelWithWidgets,\n statusMessageText,\n} from '@jbrowse/core/util'\nimport { openLocation } from '@jbrowse/core/util/io'\nimport { addDisposer, flow, isAlive, types } from '@jbrowse/mobx-state-tree'\nimport { RenderLifecycleMixin } from '@jbrowse/render-core/RenderLifecycleMixin'\nimport { getDpr } from '@jbrowse/render-core/canvas2dUtils'\nimport { autorun, reaction, untracked } from 'mobx'\n\nimport { backboneNodes, backboneSpan, isBackbone } from './anchoredNodes'\nimport { BUBBLE_SPREAD_VALUES, spreadFor } from './bubbleSpreads'\nimport { bubbleHalos } from './bubbles/bubbleHalos'\nimport { bubblesFromGraph } from './bubbles/bubblesFromGraph'\nimport {\n BUBBLE_KIND_NAMES,\n bubbleSegmentIds,\n classifyBubble,\n} from './bubbles/classifyBubble'\nimport { bubbleSubgraph } from './bubbles/popBubble'\nimport { COLOR_SCHEME_VALUES } from './colorSchemes'\nimport { deletionEdges } from './deletionEdges'\nimport {\n GENE_ADAPTER_TYPES,\n geneModelsFrom,\n pickGeneTrack,\n} from './genes/geneFeatures'\nimport { genePins } from './genes/genePins'\nimport { convertGFAToGraph } from './gfa/gfaConverter'\nimport { cutHolds, hostCut, hostFrame, hostWindow, isLinearHost } from './host'\nimport {\n paintSourceLane,\n referencePositionColor,\n sourceLaneDisplay,\n} from './laneRamp'\nimport { drawnNodeLength, layoutScaling } from './layout/drawnScale'\nimport { mergeRuns, splitRuns } from './layout/mergeRuns'\nimport { orientToReference } from './layout/orientToReference'\nimport { seededNodes } from './layout/referenceSeeds'\nimport { ROW_HEIGHT_PX } from './layout/rowSpacing'\nimport { walkRowsExtent } from './layout/walkRowLayout'\nimport { walkRows } from './layout/walkRows'\nimport {\n LAYOUT_MODE_VALUES,\n layoutModeByValue,\n modeUsesLayoutEngine,\n} from './layoutModes'\nimport {\n NODE_WIDTH_VALUES,\n maxNodeWidthPx,\n meanDepth,\n nodeWidthPx,\n} from './nodeWidths'\nimport { anchorFromPaths, anchorGraph } from './pathAnchoring'\nimport { pathColorsLegible, pathLegend } from './pathColors'\nimport { buildNeighbors, nodeReferenceSpan } from './referenceSpan'\nimport { buildGeometry, computeReferenceRamp } from './renderer/GeometryBuilder'\nimport {\n REPEAT_ADAPTER_TYPES,\n pickRepeatTrack,\n repeatArraysFrom,\n} from './repeats/repeatFeatures'\nimport { withCalls } from './repeats/walkCalls'\nimport { walkHighlight } from './walkHighlight'\nimport { parseGFA } from '../gfa-core/index'\nimport {\n hoverInRegion,\n nodeForLgvHover,\n readLgvHover,\n} from '../hoverSync/lgvHover'\nimport {\n contributingAssemblies,\n locLabel,\n nodeOwnLocation,\n resolveContributors,\n resolveLocationAssembly,\n} from '../launchFromGraph/contributors'\nimport { graphLaunchMenuItems } from '../launchFromGraph/graphMenuItems'\nimport {\n highlightInLinearView,\n launchSyntenyView,\n paddedLocation,\n showInLinearView,\n withReferenceRegion,\n} from '../launchFromGraph/launchFromGraph'\nimport { launchTracks } from '../launchFromGraph/launchTracks'\nimport { linearViewTarget, withRows } from '../launchFromGraph/linearViewTarget'\nimport { launchableSyntenyTracks } from '../launchFromGraph/syntenyTracks'\nimport {\n graphReferenceAssembly,\n offReferenceProblem,\n} from '../launchSubgraph/subgraphTracks'\n\nimport type { BubbleSpread } from './bubbleSpreads'\nimport type { ColorScheme, ResolvedColorScheme } from './colorSchemes'\nimport type { GeneModel } from './genes/geneFeatures'\nimport type { HostWindow, LinearHost } from './host'\nimport type { LayoutScaling } from './layout/drawnScale'\nimport type { LayoutModeValue } from './layoutModes'\nimport type { NodeWidth } from './nodeWidths'\nimport type { Renderer } from './renderer/types'\nimport type { RepeatArray } from './repeats/repeatFeatures'\nimport type { Graph, GraphNode, LayoutResult } from './types'\nimport type { SubgraphCutOptions, SubgraphTier } from '../GetSubgraph'\nimport type { NodeInk } from './util/hitDetection'\nimport type { MinigraphBubble } from '../MinigraphBubbleAdapter/bubbleLine'\nimport type { AxisScale } from './util/geometry'\nimport type { GraphLocation } from '../launchFromGraph/contributors'\nimport type { SubgraphRegion } from '../launchSubgraph/launchSubgraphView'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Feature } from '@jbrowse/core/util'\nimport type { FileLocation } from '@jbrowse/core/util/types'\n\n// Ceiling on the pane, and what it falls back to before there is a layout to\n// size against. A roughly square drawing (FMMM) hits this and keeps the\n// scrollable pane it has always had.\nconst MAX_CANVAS_HEIGHT = 600\n// Floor, so a window holding only backbone \u2014 one row, no height at all \u2014 still\n// leaves room to hover a node and read its tooltip.\nconst MIN_CANVAS_HEIGHT = 160\nconst VARIANT_MAP_HEIGHT = 340\n\nconst SEGMENTS_SUFFIX = '.segs.bed.gz'\n\n// The rGFA index prefix an adapter config names, from either spelling the\n// config schema accepts: the `uri` shorthand or an explicit segments location.\nfunction bubblePrefix(adapterConfig: Record<string, unknown>) {\n if (typeof adapterConfig.uri === 'string') {\n return adapterConfig.uri\n }\n const segments = adapterConfig.segmentsLocation as\n { uri?: string } | undefined\n const uri = segments?.uri\n return typeof uri === 'string' && uri.endsWith(SEGMENTS_SUFFIX)\n ? uri.slice(0, -SEGMENTS_SUFFIX.length)\n : undefined\n}\n// Gap between the drawing and the edge of the pane, on all four sides.\nconst FIT_PADDING = 40\nconst HOVER_BRIGHTEN = 1.4\nconst SELECT_BRIGHTEN = 1.6\nconst VIEWPORT_DEBOUNCE_MS = 150\nconst VIEWPORT_PANES_BUILT = 1\n\n// MobX tracks every observable read while a computed or autorun runs, so\n// passing values here registers them as dependencies without otherwise using\n// them.\nfunction dependOn(..._values: unknown[]) {}\n\n// Hard size cap for the single-mode graph view. Past it the view declines with a\n// \"zoom in\" message rather than switching to a degraded rendering mode \u2014 one mode\n// only, since the large-region case is a linear synteny view, not a graph.\n// (The `adr-027` citation this used to carry was stale: that number was reused\n// for wheel-input semantics after the large-mode ADR was removed in 9d8102f0b5.)\n//\n// This bounds the *fetch*, and it is the only cap that can be applied before one\n// happens. It is a poor proxy for cost, because cost tracks node count, so the\n// number is chosen against the density of the graphs it can actually guard: a\n// region is fetched through `getSubgraph`, and the rGFA cut is what this number\n// was chosen against (the GBZ cut is base-level and has its own `nodeLimit`,\n// which trips long before 5 Mb). Both measured rGFAs are sparse, and at 5 Mb\n// both land at or under the ~2k nodes that redraw in under 10 ms (see agent-docs/GRAPH_SCALE_AND_LOD.md):\n//\n// HPRC MC GRCh38 ~7,000 bp/segment whole 4.9 Mb MHC 1,173 nodes\n// ecoli minigraph 3,078 bp/segment whole 4.6 Mb genome 2,415 nodes\n//\n// Fetch cost does not scale with the window either: against the hosted HPRC\n// index a 4.9 Mb links query and a 100 kb one both cost ~1.3 s, being dominated\n// by HTTP setup. FMMM is the slowest consumer at this size and stays tolerable,\n// ~0.1-3 s for ~1k nodes depending on layout quality (bandage-layout-js), and it\n// runs off the main thread.\n//\n// A dense graph is what this cannot protect against, because bp per node varies\n// by orders of magnitude between graph types \u2014 a base-level pggb graph runs ~17\n// bp/node, where 5 Mb would be hundreds of thousands. Such a graph has no region\n// query to reach this check with today, and the node budget below is the cap that\n// catches it if one ever does.\nexport const MAX_GRAPH_REGION_BP = 5_000_000\n\n// The cap in a message, in the unit that reads: `5 Mb`, not `5000 kb`.\nexport function formatSpanBp(bp: number) {\n return bp >= 1_000_000\n ? `${+(bp / 1_000_000).toFixed(1)} Mb`\n : `${Math.round(bp / 1000)} kb`\n}\n\n// What the view will actually draw, checked once the graph is parsed and so\n// applying to a whole-file import as much as to a subgraph fetch. Measured on\n// bubble-chain graphs: ~1-2k nodes redraws in under 10 ms, 10k takes ~43 ms of\n// geometry and ~125k canvas draw calls per frame (single-digit fps while\n// panning), and 100k needs 632 ms and 75 MB of vertex buffers. So this is set\n// where the tab stops being usable rather than where it stops being smooth, and\n// it is a view prop rather than a constant so a session can raise it \u2014 the same\n// escape hatch strangepg gives with `-T N`.\nexport const DEFAULT_MAX_GRAPH_NODES = 20_000\n\n// The floor exists to keep a scale positive and finite, not to express a useful\n// zoom level, so it has to clear the smallest scale a real layout asks for. In\n// the reference-anchored layouts world units are bp: fitting a chromosome-scale\n// rGFA (250 Mbp) into ~720 px needs ~3e-6, and the old 0.001 floor clamped that\n// to 7x too wide \u2014 zoomToFit could not fit a whole-file import at all.\nconst MIN_ZOOM = 1e-6\nconst MAX_ZOOM = 100\n\nfunction clampZoom(zoom: number) {\n return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom))\n}\n\n// The zoom as a pair of axis scales. One expression, read by all three getters\n// below: they sit in one `.views()` block and so cannot reach each other through\n// `self`, which had each of them restating the `pixelRows ? 1 : scale` rule that\n// AxisScale exists to keep in one place.\nfunction axisScaleOf(scale: number, pixelRows: boolean): AxisScale {\n // pixelRows rides along rather than being derived downstream: the deletion\n // bow is capped in a row layout and not in the isotropic one, and `scaleY !==\n // scaleX` coincides at one zoom level. See AxisScale.\n return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows }\n}\n\ntype ViewportOwner = 'fit' | 'user' | 'host'\n\ninterface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\n// What the pane shows, in layout units.\nfunction viewportOf(model: {\n translateX: number\n translateY: number\n width: number\n scaleX: number\n scaleY: number\n canvasHeight: number\n}): Bounds {\n return {\n minX: -model.translateX / model.scaleX,\n minY: -model.translateY / model.scaleY,\n maxX: (model.width - model.translateX) / model.scaleX,\n maxY: (model.canvasHeight - model.translateY) / model.scaleY,\n }\n}\n\n// The window a geometry build covers: the pane plus a whole pane on every\n// side, so a pan has that far to go before the drawing runs out and a rebuild\n// is due. Drawing three panes' worth is cheap now that a redraw is a few\n// batched strokes; what it buys is that an ordinary pan never blanks its\n// margins for the debounce and never rebuilds at all.\nfunction padded(v: Bounds, panes: number): Bounds {\n const w = (v.maxX - v.minX) * panes\n const h = (v.maxY - v.minY) * panes\n return {\n minX: v.minX - w,\n minY: v.minY - h,\n maxX: v.maxX + w,\n maxY: v.maxY + h,\n }\n}\n\n// Centre the leftover, but only when there IS leftover. A row layout fits on x\n// alone, so its drawing is routinely taller than the pane \u2014 that is the case\n// rowSpacing.ts means by \"reached by panning\" \u2014 and centring an overflow splits\n// the loss across both ends: at 41 rows the top row landed 100 px above the\n// pane. The top row is the reference backbone, i.e. the axis the whole layout\n// exists to put under the linear view, so it is the one row that must not be\n// the first thing to go. Pinned at the padding it opens where a fitting drawing\n// opens, and the rows past the ceiling are below, which is the direction a\n// reader already scrolls a track in.\nfunction fittedTranslateY(\n bounds: { minY: number; h: number },\n usableHeight: number,\n yScale: number,\n) {\n const leftoverY = usableHeight - bounds.h * yScale\n return FIT_PADDING - bounds.minY * yScale + Math.max(0, leftoverY) / 2\n}\n\nfunction contains(outer: Bounds, inner: Bounds) {\n return (\n inner.minX >= outer.minX &&\n inner.maxX <= outer.maxX &&\n inner.minY >= outer.minY &&\n inner.maxY <= outer.maxY\n )\n}\n\n// Force layouts already computed for a graph, keyed by the view props the\n// engine reads. Every layout mode is local and costs a millisecond except this\n// one, which is seconds: measured on the committed engine, a 1,201-node bubble\n// chain is 0.6 s proportional, 4.3 s at 'Wide bubbles' and 3.4 s at the highest\n// quality.\n//\n// Worth keeping because a session revisits the same layout. The two drawings\n// answer different questions and the README's own figure is one subgraph in\n// both, so Anchored <-> Force is a round trip a reader makes repeatedly \u2014 and\n// every control in the settings dialog calls `recomputeLayout` on change\n// whether or not the current mode reads what it changed, which made picking a\n// reference path in force mode a multi-second re-run of an identical layout.\n// Both are hits.\n//\n// Entries are the live position objects rather than copies, which a node drag\n// mutates in place (see moveNode), so a layout comes back arranged the way it\n// was left rather than snapping back.\n//\n// Weak on the graph so nothing has to invalidate it: a graph is rebuilt by\n// every load and dropped by `clearGraph`, and its layouts go with it.\nconst forceLayouts = new WeakMap<Graph, Map<string, LayoutResult>>()\n\n// Enough to hold the settings a comparison moves between; past that the oldest\n// goes, since a big graph's positions are megabytes.\nconst FORCE_LAYOUT_CACHE_SIZE = 4\n\nfunction forceLayoutsOf(graph: Graph) {\n let cache = forceLayouts.get(graph)\n if (!cache) {\n cache = new Map()\n forceLayouts.set(graph, cache)\n }\n return cache\n}\n\n// Re-anchoring builds a new Graph, and the force layout of it is the same\n// drawing: `anchorFromPaths` rewrites each node's `stable` and `samples` and\n// touches neither the ids, the lengths, the depths nor the edges, which are all\n// the engine reads. So the layouts follow the graph they were computed for\n// rather than being thrown away with it.\nfunction inheritForceLayouts(from: Graph, to: Graph) {\n const cache = forceLayouts.get(from)\n if (cache && from !== to) {\n forceLayouts.set(to, cache)\n }\n}\n\nfunction remember(\n cache: Map<string, LayoutResult>,\n key: string,\n result: LayoutResult,\n) {\n if (cache.size >= FORCE_LAYOUT_CACHE_SIZE) {\n cache.delete(cache.keys().next().value!)\n }\n cache.set(key, result)\n}\n\nexport default function stateModelFactory() {\n return types\n .compose(\n 'GraphGenomeView',\n BaseViewModel,\n RenderLifecycleMixin(),\n types.model({\n type: types.literal('GraphGenomeView'),\n // FMMM's iteration budget, 0-4, the same scale Bandage's own settings\n // dialog exposes: 3+1 iterations at 0, 15+10 at 1, 30+20 at 2, 60+40 at\n // 3, 120+60 at 4 (BandageNG layout/graphlayoutworker.cpp).\n //\n // 2 because that is what the engine we vendor ships as its default\n // (`graphLayoutQuality = IntSetting(2, 0, 4)`, program/settings.cpp).\n // This was 1 for no recorded reason, i.e. one step BELOW upstream, and a\n // first sight of any graph is the default: review kept asking whether\n // the drawing could be iterated more (\"are you sure you can't iterate it\n // more times for better layout?\") of figures that were simply on a lower\n // setting than the tool they come from. Cost at the sizes this view\n // draws is milliseconds, and the header states the layout time so the\n // difference is visible rather than asserted.\n layoutQuality: types.optional(types.number, 2),\n linearLayout: types.optional(types.boolean, false),\n // Which drawing to use; the modes and their fallbacks are described in\n // LAYOUT_MODES. Default 'force' is the Bandage FMMM drawing, i.e. what\n // someone who has seen a pangenome graph before expects to see.\n //\n // The default used to be 'auto', the reference-anchored layout, on the\n // argument that lining the graph's x axis up with a linear view above it\n // is what this view can do and Bandage cannot. Docs review disagreed\n // twice, on figure after figure \u2014 \"the linear backbone is just\n // confusing\", \"default to showing the bandage graphs over linear\n // backbone in almost all cases\" \u2014 and the reason is that the anchored\n // drawing looks like a track rather than a graph: a bubble collapses onto\n // the reference axis, so the alternative alleles read as short bars\n // hanging under a line rather than as two routes through the same locus.\n // The anchored modes stay one dropdown click away, and are what the\n // hover-sync and one-row-per-strain figures select deliberately.\n layoutMode: types.optional(\n types.enumeration(LAYOUT_MODE_VALUES),\n 'force',\n ),\n // 'auto' rather than a colour, resolved by `effectiveColorScheme`. See\n // COLOR_SCHEMES for why the default is not 'uniform' any more. A session\n // that names a scheme keeps it \u2014 this only decides what an unstated one\n // opens as.\n colorScheme: types.optional(\n types.enumeration(COLOR_SCHEME_VALUES),\n 'auto',\n ),\n // How far the force layout opens a bubble, which on a variation graph is\n // the difference between a legible drawing and a rope. See\n // BUBBLE_SPREADS; no effect on the reference-anchored layouts, which\n // place a node from its coordinates rather than from a force sim.\n bubbleSpread: types.optional(\n types.enumeration(BUBBLE_SPREAD_VALUES),\n 'auto',\n ),\n // Node thickness by depth, Bandage's own device; see NODE_WIDTHS.\n nodeWidth: types.optional(\n types.enumeration(NODE_WIDTH_VALUES),\n 'depth',\n ),\n // Whether the node layouts draw each bubble as a halo along its nodes\n // with a label that opens it. The variant map draws glyphs instead.\n showBubbles: types.optional(types.boolean, true),\n showDeletionEdges: types.optional(types.boolean, false),\n // The session's genes drawn onto the backbone: exons along the nodes\n // that carry them, names pinned at their midpoints. See genes/.\n showGenes: types.optional(types.boolean, true),\n // Which track the genes come from; empty picks the assembly's\n // annotation track (pickGeneTrack).\n geneTrackId: types.optional(types.string, ''),\n // Which track the walk rows read tandem repeat arrays from; empty\n // picks a track whose name says repeats (pickRepeatTrack).\n repeatTrackId: types.optional(types.string, ''),\n // The array the walk rows measure between and tile by, as\n // RepeatArray.key; empty measures the whole window untiled.\n repeatKey: types.optional(types.string, ''),\n // Samples whose walks the walk rows show, by the name before the\n // haplotype number; undefined shows every walk the cut holds.\n walkRowSamples: types.maybe(types.frozen<string[]>()),\n // One walk, by its path name, lifted out of the drawing: its nodes and\n // links keep their ink and the rest fades. Empty lifts none.\n highlightedPath: types.optional(types.string, ''),\n // Which of a general GFA's paths the anchored layouts put on x. A path\n // GFA's names are arbitrary and none of them is marked as the\n // reference, so this is a choice; empty means \"infer\", which is the\n // assembly a subgraph was cut against, and the first path in the file\n // for a whole-file import. No effect on an rGFA, whose segments carry\n // their own coordinates. See pathAnchoring.ts.\n referencePath: types.optional(types.string, ''),\n // Whether the toolbar spells out `fetch 12371ms \u00B7 layout 4ms \u00B7 geom 9ms`.\n // Off by default because it was in the toolbar of every published graph\n // figure, where a machine's fetch time is noise a reader has to skip\n // past. The numbers themselves are always on the element as `data-*`\n // attributes, which is what browser tests assert against, so hiding the\n // text costs no coverage.\n showPerf: types.optional(types.boolean, false),\n contigThickness: types.optional(types.number, 6),\n connectorThickness: types.optional(types.number, 2),\n darkMode: types.optional(types.boolean, false),\n scale: types.optional(types.number, 1),\n translateX: types.optional(types.number, 0),\n translateY: types.optional(types.number, 0),\n drawPaths: types.optional(types.boolean, false),\n // Ceiling on the drawing pane, in css px, for a session or a figure that\n // wants a shorter one than the drawing asks for. Unset means the built-in\n // MAX_CANVAS_HEIGHT, which is what every existing session gets.\n //\n // Why this is a knob rather than a smaller default: the pane is as tall\n // as the drawing's own aspect ratio (see canvasHeight), which is right \u2014\n // a graph that is twice as tall as it is wide should not be squeezed\n // into a strip. But one very long node in a cut of short ones makes that\n // aspect ratio all arc: the HPRC CHM13 figure draws a 142 kb allele\n // among sub-kb backbone segments, pins the 600 px ceiling and spends\n // most of it on the loop, with the chain squashed along the bottom edge.\n // Lowering the ceiling there scales the whole drawing down, which is\n // exactly the trade that figure wants and the wrong default for a graph\n // whose height is carrying information.\n paneHeight: types.maybe(types.number),\n // Raise to draw a bigger graph than the default budget allows; see\n // DEFAULT_MAX_GRAPH_NODES for what the numbers cost.\n maxGraphNodes: types.optional(types.number, DEFAULT_MAX_GRAPH_NODES),\n // The bp ceiling on a cut, which is a PROXY for node count and only a\n // good one at fine granularity: on a base-level index 5 Mb is already\n // thousands of segments, so refusing by span is refusing by cost. A\n // level-of-detail tier breaks the proxy \u2014 a whole 249 Mb human\n // chromosome is 474 nodes off the hosted HPRC tier \u2014 so a session that\n // knows which granularity it is pointed at can raise this. The real\n // backstop stays `maxGraphNodes`, which counts what actually came back.\n //\n // The launch MENUS keep the constant deliberately (launchSubgraphView):\n // a user rubber-banding 249 Mb over a fine index should still be told\n // no, because nothing there has said the track is coarse.\n maxRegionBp: types.optional(types.number, MAX_GRAPH_REGION_BP),\n loadedTrackId: types.optional(types.string, ''),\n loadedRegion: types.maybe(\n types.frozen<{\n refName: string\n assemblyName: string\n start: number\n end: number\n }>(),\n ),\n // The haplotypes the cut is for, lane assembly names or PanSN\n // prefixes, beside the region so a re-cut asks for the same set;\n // undefined is every haplotype. Only the GBZ cut reads it.\n subgraphHaplotypes: types.maybe(types.frozen<string[]>()),\n // How far the cut follows links past the segments the region's own\n // links name, defaulting to one hop because at 0 the drawing is wrong\n // rather than merely sparse. A detour that leaves the backbone before\n // the window and rejoins after it has its interior indexed only under\n // its own stable sequence, which no query on the reference reaches, so an\n // arm that bypasses 21 kb of reference arrives as a 43 bp fragment: the\n // one bubble draws as two unrelated small insertions, and nothing on\n // screen says they are the same event. One hop closes those, and that is\n // the whole of what a reader means by seeing the local graph.\n //\n // The cost is queries rather than nodes, and a hop only follows alleles\n // (offReference in RgfaTabixAdapter, which is gfabase's stop-at-cutpoints\n // rule read off the rank tag), so it is bounded by the off-reference\n // segments the cut already reached and it does not walk the backbone out\n // of the window. Measured: the E. coli paa locus goes 14 segments at 0 to\n // 17 at 1 and stays at 17 at 2, so the cut closes and stays closed.\n // HPRC's amylase window goes 63 to 78 to 92, because there the alleles\n // have alleles; wall-clock is flat across all three, the remote index\n // dominating. 0 stays available for a graph where even that is too much.\n //\n // Still not a graph-aware cut: a complete one needs the bubble\n // decomposition (`gfatools view -R`, whose bubble rows state their own\n // member segments) rather than a frontier, which is the follow-up noted\n // in the adapter.\n subgraphContext: types.optional(types.number, 1),\n // Whole-GFA source loaded on attach \u2014 lets a GraphGenomeView be\n // instantiated declaratively from a session/config snapshot.\n gfaLocation: types.maybe(types.frozen<FileLocation>()),\n // The reference span the reference-position ramp runs over. A graph cut\n // from a track already states one as `loadedRegion`; a file-loaded graph\n // has no region at all, and this is how its snapshot can still put a\n // linear track beside it on the same ramp \u2014 both painted from one pair\n // of numbers rather than from the file's own measured extent.\n colorDomain: types.maybe(\n types.frozen<{ start: number; end: number }>(),\n ),\n // The linear view this graph was launched from, so a hovered node can\n // draw its reference span there and vice versa. Written by the launch\n // menu; see hoverSync/.\n connectedViewId: types.maybe(types.string),\n // Whether the cut came from the source track's `coarse` pair\n // (RgfaTabixAdapter), so a restored session re-makes the cut it saved.\n // No bp cap applies to that pair; maxGraphNodes counts what came back.\n coarseCut: types.optional(types.boolean, false),\n }),\n )\n .volatile(() => ({\n graph: undefined as Graph | undefined,\n layoutResult: undefined as LayoutResult | undefined,\n // The bubble index rows over the cut window, when the source track has\n // one beside its segments. Undefined for a graph with no index, which the\n // variant map draws from `derivedBubbles` instead.\n indexBubbles: undefined as MinigraphBubble[] | undefined,\n // the genes over the cut window, read once per cut from the gene track\n geneFeatures: undefined as GeneModel[] | undefined,\n // the tandem repeat arrays over the cut window, from the repeat track\n repeatArrays: undefined as RepeatArray[] | undefined,\n // The graphs the open bubble was popped out of, outermost first, each\n // with what closing back to it restores without a refetch. A stack so a\n // popped superbubble can be mapped and popped again.\n popStack: [] as {\n graph: Graph\n layoutMode: LayoutModeValue\n label: string\n indexBubbles: MinigraphBubble[] | undefined\n }[],\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n error: undefined as unknown,\n isLoading: false,\n loadCanceled: false,\n statusMessage: '',\n hoveredNode: null as string | null,\n hoveredEdge: null as number | null,\n selectedNode: null as string | null,\n viewportDirty: 0,\n // Bumped when the layout's positions are mutated IN PLACE, which only a\n // node drag does. Separate from `viewportDirty` because that one also\n // fires on pan and zoom, and the things keyed off this \u2014 the two hit\n // indexes and the label placement's per-layout caches \u2014 do not depend on\n // the transform at all. Rebuilding a 12k-edge hit index because the user\n // panned cost ~14 ms of the first mousemove after every gesture.\n positionsVersion: 0,\n // Bumped per upload, so the hover autorun re-states its highlights\n // against the batch the renderer now holds.\n geometryVersion: 0,\n // The zoom and the window the current batch was built for; a pan that\n // stays inside it needs no rebuild.\n builtViewport: undefined as { scale: number; bounds: Bounds } | undefined,\n draggingNode: null as string | null,\n // Dragging the background rather than a node. Lives here beside\n // draggingNode instead of in a component ref+state pair, so the two\n // mutually exclusive drag modes are one piece of state read from one place.\n isPanning: false,\n // Who places the view. 'fit' refits it to every new layout, 'user' leaves\n // it where a gesture or a restored session put it, and 'host' takes x\n // from the linear view the pane sits in. Not derived from\n // `isDefaultViewport`, which zoomToFit itself invalidates on its first\n // run \u2014 that made the fit fire once against not-yet-measured dimensions\n // and then never re-fit.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n viewportOwner: 'fit' as ViewportOwner,\n // the pane height a host holds, so a re-cut with more rows does not\n // move the pane while the linear view above it is being dragged\n hostPaneHeight: undefined as number | undefined,\n cutNote: undefined as string | undefined,\n recuts: 0,\n viewportDirtyTimer: undefined as\n ReturnType<typeof setTimeout> | undefined,\n // 0 is never a live rAF handle, so it doubles as \"nothing pending\"\n positionsDirtyFrame: 0,\n // Performance instrumentation, surfaced in GraphStats for browser tests\n // to assert against budgets. `fetchMs` is the GetSubgraph RPC round-trip,\n // `layoutMs` is the Bandage FMMM compute time reported by the\n // GraphComputeLayout RPC, `geometryMs` is the main-thread buildGeometry\n // pass and `geometryStrokeCount` the node strokes it produced.\n lastFetchMs: undefined as number | undefined,\n lastLayoutMs: undefined as number | undefined,\n lastGeometryMs: undefined as number | undefined,\n lastGeometryStrokeCount: undefined as number | undefined,\n }))\n .views(self => ({\n get nodeById() {\n if (self.graph) {\n const m = new Map<string, GraphNode>()\n for (const n of self.graph.nodes) {\n m.set(n.id, n)\n }\n return m\n }\n return undefined\n },\n // bp per node id, which is all the label overlay needs of a GraphNode.\n // Held here rather than rebuilt in the component: that render runs on\n // every pan, and the map is a fact about the graph (measured 6.9 ms an\n // allocation on a 30k-node cut).\n get nodeLengths() {\n const m = new Map<string, number>()\n for (const n of self.graph?.nodes ?? []) {\n m.set(n.id, n.length)\n }\n return m\n },\n get nodeCount() {\n return self.graph?.nodes.length ?? 0\n },\n get edgeCount() {\n return self.graph?.edges.length ?? 0\n },\n get pathCount() {\n return self.graph?.paths?.length ?? 0\n },\n get hasGraph() {\n return self.graph !== undefined\n },\n // The app-wide readiness contract (AppReadyMarker, @jbrowse/capture)\n // reads this: a declared source still fetching, or a graph whose\n // geometry has not been built yet.\n get showLoading() {\n return (\n self.error === undefined &&\n (self.isLoading ||\n ((self.graph !== undefined ||\n !!(self.loadedTrackId && self.loadedRegion) ||\n !!self.gfaLocation) &&\n self.lastGeometryStrokeCount === undefined))\n )\n },\n get canRetryLoad() {\n return !!(self.loadedTrackId && self.loadedRegion) || !!self.gfaLocation\n },\n // Only before anything is drawn: past that point a reload is superseded\n // by the next setting change, and stopping one midway would leave a new\n // graph under the previous graph's layout.\n get canCancelLoad() {\n return self.isLoading && !self.layoutResult\n },\n // Whether the drawing on screen is the FMMM engine's. False before there\n // is a graph, since nothing is drawn by anything yet.\n //\n // Read by the settings that only the engine looks at: the layout quality\n // and the bubble spread do nothing at all to an anchored drawing, which\n // places a node from its coordinates, and the guide says so in prose\n // precisely because the dialog did not. A control that silently does\n // nothing is worse than one that says it cannot \u2014 the same rule the\n // `drawPaths` switch already follows.\n get usesLayoutEngine() {\n return self.graph\n ? modeUsesLayoutEngine(self.layoutMode, self.graph)\n : false\n },\n // A rank-0 backbone to draw x against, whether the segments declared it\n // (rGFA) or a path walk derived it. Only a GFA with neither tags nor\n // paths has no backbone at all, and there force is the one honest layout.\n get canAnchorLayout() {\n return self.graph?.nodes.some(n => n.stable?.rank === 0) ?? false\n },\n // The paths this graph could be anchored on, for the picker. Empty for an\n // rGFA, which has no P/W records and needs none.\n get anchorPaths() {\n return self.graph?.anchorPaths ?? []\n },\n // Which haplotype each ribbon colour belongs to, in the order the file\n // states the paths \u2014 the same list and the same order the geometry keys\n // its colours off, so the key cannot name a colour that is not drawn.\n // Empty unless the ribbons are actually on: a colour key beside a drawing\n // with no colours in it is a legend for nothing.\n get pathLegend() {\n const paths = self.graph?.paths\n return self.drawPaths && paths && pathColorsLegible(paths.length)\n ? pathLegend(paths)\n : []\n },\n // Every walk the graph carries, named for a picker, whatever the count.\n get walkChoices() {\n const paths = self.graph?.paths\n return paths?.length ? pathLegend(paths) : []\n },\n // The lifted walk, or undefined when none is named or the graph on\n // screen does not carry the one that was.\n get walkHighlight() {\n return self.graph && self.highlightedPath\n ? walkHighlight(self.graph, self.highlightedPath)\n : undefined\n },\n // Whether the drawing is actually painted per path, which is not the same\n // question as whether the user asked for it: past MAX_PATH_COLORS the\n // colours say nothing and cost a stroke per path per edge. A bare getter\n // returning a resolved value, the same shape and for the same reason as\n // `effectiveColorScheme` \u2014 every consumer of a path colour reads this\n // one, so the geometry, the hit index and the key cannot disagree about\n // whether there are any. The switch in the settings dialog reads the raw\n // `drawPaths`, so the user's choice stays visible as the choice it is.\n get effectiveDrawPaths() {\n return (\n self.drawPaths && pathColorsLegible(self.graph?.paths?.length ?? 0)\n )\n },\n // The path x is currently drawn on, which is not necessarily the one\n // `referencePath` asked for: an unmatched name falls back rather than\n // leaving the graph unanchored, and the picker has to show what happened.\n get activeReferencePath() {\n return self.graph?.referencePath\n },\n // What to anchor on. An explicit choice wins; otherwise a graph cut from\n // a track is anchored on the assembly it was cut against, which is the\n // one the linear view beside it is showing.\n get preferredReferencePath() {\n return self.referencePath === ''\n ? self.loadedRegion?.assemblyName\n : self.referencePath\n },\n // The span the reference-position ramp runs over: what the snapshot\n // stated, else the region the graph was cut from. Undefined only for a\n // file-loaded graph that states neither, where the ramp falls back to the\n // drawn extent (computeReferenceRamp). A follow's re-cut moves\n // loadedRegion, so the ramp re-spans every new cut, and the lane above\n // with it.\n get rampDomain() {\n return self.colorDomain ?? self.loadedRegion\n },\n // The scheme the renderer actually paints with, which is the raw prop\n // unless it is 'auto'. A bare getter returns a resolved value (root\n // CLAUDE.md): every consumer of a colour reads this one, and the two\n // dropdowns read `colorScheme` so \"Auto\" stays visible as the choice it\n // is.\n //\n // 'auto' asks the GRAPH, not the layout. A hue along the reference is\n // meaningful whenever the segments have reference coordinates \u2014\n // `anchoredBy` is exactly that question \u2014 and a force-directed drawing of\n // an rGFA is the case that proves it: the layout has no reference axis and\n // the ramp still says where on the reference each node came from, which is\n // the only quantity a linear track beside it can be painted with too.\n get effectiveColorScheme(): ResolvedColorScheme {\n return self.colorScheme !== 'auto'\n ? self.colorScheme\n : self.graph?.anchoredBy\n ? 'reference-position'\n : 'uniform'\n },\n get nodePositions() {\n return self.layoutResult?.nodePositions\n },\n // Empty rather than undefined: every consumer maps over it, and a layout\n // with no row structure (FMMM) is a normal state, not a missing one.\n get rowLabels() {\n return self.layoutResult?.rowLabels ?? []\n },\n // Deletions the layout found inside an allele, empty for the same reason\n // rowLabels is: a layout drawn at sequence scale states none, because\n // there a node's drawn extent already IS its length. `deletions` beside\n // this one is the bare-edge kind, which every layout has.\n get alleleDeletions() {\n return self.layoutResult?.alleleDeletions ?? []\n },\n // Whether the current layout states y in screen px rather than in the same\n // units as x (LayoutResult.pixelRows). Everything that has to put the two\n // axes in one expression reads this through scaleX/scaleY.\n get pixelRows() {\n return self.layoutResult?.pixelRows ?? false\n },\n get zoomPercent() {\n return `${(self.scale * 100).toFixed(1)}%`\n },\n // True while the persisted transform is still the schema default, i.e.\n // neither a restored session nor the user has positioned this view yet,\n // so an incoming layout is free to zoom-to-fit.\n get isDefaultViewport() {\n return (\n self.scale === 1 && self.translateX === 0 && self.translateY === 0\n )\n },\n }))\n .views(self => ({\n get poppedFrom() {\n return self.popStack.at(-1)\n },\n // The bubbles the graph itself states, for a graph with no index: a GBZ\n // cut, a pggb file, the inside of a popped bubble.\n get derivedBubbles() {\n return self.graph ? bubblesFromGraph(self.graph) : []\n },\n }))\n .views(self => ({\n // What the variant map draws. Index rows win where both exist: gfatools\n // measured every allele, the layered order only bounds them.\n get bubbles() {\n return self.indexBubbles?.length\n ? self.indexBubbles\n : self.derivedBubbles\n },\n }))\n .views(self => ({\n // As written: a session track is a plain object until something\n // hydrates it, so a slot left at its default reads undefined here.\n get sourceAdapter() {\n const track = getSession(self).tracks.find(\n t => t.trackId === self.loadedTrackId,\n )\n return track\n ? (readConfObject(track, 'adapter') as Record<string, unknown>)\n : undefined\n },\n get assemblyTrackChoices() {\n const region = self.loadedRegion\n if (!region) {\n return []\n }\n return getSession(self)\n .tracks.filter(t =>\n (readConfObject(t, 'assemblyNames') as string[]).includes(\n region.assemblyName,\n ),\n )\n .map(t => ({\n trackId: t.trackId as string,\n name: readConfObject(t, 'name') as string,\n adapterType: (readConfObject(t, 'adapter') as { type: string })\n .type,\n }))\n },\n }))\n .views(self => ({\n get geneTrackChoices() {\n return self.assemblyTrackChoices.filter(t =>\n GENE_ADAPTER_TYPES.has(t.adapterType),\n )\n },\n // The session's feature tracks a repeat annotation could be, for the\n // picker; which one is read is pickRepeatTrack's choice.\n get repeatTrackChoices() {\n return self.assemblyTrackChoices.filter(t =>\n REPEAT_ADAPTER_TYPES.has(t.adapterType),\n )\n },\n // The arrays over the window that state a unit, for the Repeat picker.\n get repeatChoices() {\n return self.repeatArrays ?? []\n },\n }))\n .views(self => ({\n get repeatTrack() {\n return pickRepeatTrack(self.repeatTrackChoices, self.repeatTrackId)\n },\n get selectedRepeat() {\n return self.repeatChoices.find(r => r.key === self.repeatKey)\n },\n }))\n .views(self => ({\n get nodeNeighbors() {\n return self.graph ? buildNeighbors(self.graph) : undefined\n },\n // How far each node's ink reaches from its centreline, for the hit test:\n // the widths the geometry draws with, from the same function.\n get nodeInk(): NodeInk {\n const { contigThickness, nodeWidth, nodeById } = self\n const mean = self.graph ? meanDepth(self.graph) : 0\n return {\n maxHalfWidthPx: maxNodeWidthPx(contigThickness, nodeWidth) / 2,\n halfWidthPx: id => {\n const node = nodeById?.get(id)\n return node\n ? nodeWidthPx(node, contigThickness, nodeWidth, mean) / 2\n : contigThickness / 2\n },\n }\n },\n // Links that skip reference sequence, i.e. the deletions this graph\n // holds. Computed once per graph rather than per geometry rebuild: it is\n // a pass over the edges and the drawing rebuilds on every pan.\n // Walk rows state what each walk skips as its own bar length, so the arcs\n // over the backbone would only say it again, across the bars.\n get deletions() {\n return self.graph && self.layoutMode !== 'walkrows'\n ? deletionEdges(self.graph)\n : []\n },\n // One bar per haplotype walk on its own bp axis, for the walk-rows\n // overlay. Empty under every other layout.\n get walkRowBars() {\n if (self.layoutMode !== 'walkrows' || !self.graph) {\n return undefined\n }\n const repeat = self.selectedRepeat\n const bars = repeat\n ? walkRows(self.graph, repeat, repeat.unit)\n : walkRows(self.graph, self.loadedRegion)\n if (!bars) {\n return undefined\n }\n const samples = self.walkRowSamples\n const rows = samples\n ? bars.rows\n .filter(r => samples.includes(r.sample))\n .sort(\n (a, b) =>\n samples.indexOf(a.sample) - samples.indexOf(b.sample) ||\n a.label.localeCompare(b.label),\n )\n : bars.rows\n return { ...bars, rows: withCalls(rows, repeat?.calls) }\n },\n // The labels drawn beside the rows. Walk rows label from the bars\n // themselves, which follow the selected repeat and sample filter that\n // the layout, run once per cut, cannot.\n get drawnRowLabels() {\n const bars = this.walkRowBars\n return bars\n ? [bars.reference, ...bars.rows].map((row, i) => ({\n label: row.label,\n y: i * ROW_HEIGHT_PX,\n }))\n : self.rowLabels\n },\n // Each bubble in the window with what it is, for the variant map's\n // glyphs on the reference line.\n get bubbleGlyphs() {\n const bubbles = self.layoutMode === 'variants' ? self.bubbles : []\n return bubbles.map(bubble => ({\n bubble,\n ...classifyBubble(bubble),\n }))\n },\n // Exons and names on the backbone, in layout units. Reads\n // positionsVersion so a dragged node takes its exons with it.\n get genePins() {\n dependOn(self.positionsVersion)\n const positions = self.layoutResult?.nodePositions\n return self.showGenes &&\n self.layoutMode !== 'variants' &&\n self.layoutMode !== 'walkrows' &&\n self.graph &&\n self.geneFeatures &&\n positions\n ? genePins(self.graph, self.geneFeatures, positions)\n : []\n },\n // The same bubbles over every other layout, as halos along their nodes.\n // Reads positionsVersion so a dragged node takes its halo with it.\n get bubbleHalos() {\n dependOn(self.positionsVersion)\n const positions = self.layoutResult?.nodePositions\n if (\n !self.showBubbles ||\n self.layoutMode === 'variants' ||\n self.layoutMode === 'walkrows' ||\n !self.graph ||\n !positions\n ) {\n return []\n }\n const labels = new Map(self.walkChoices.map(c => [c.name, c.label]))\n return bubbleHalos(\n self.graph,\n self.bubbles,\n positions,\n name => labels.get(name) ?? name,\n )\n },\n // Every node's midpoint on the reference plus the interval the hue ramps\n // over \u2014 what `reference-position` paints from, and undefined under every\n // other scheme so the walk is never run for a colouring that ignores it.\n //\n // Here for the same reason `deletions` is, only more so: deriving it is a\n // bounded neighbour walk PER NODE (referenceMidpoints), it depends on\n // nothing the transform touches, and it was being redone inside every\n // geometry rebuild \u2014 1.6 ms at 1.5k nodes, 45 ms at 30k, on a pass that\n // reruns on each debounced pan and each drag frame. `reference-position`\n // is also what `auto` resolves to on any anchored graph, so this was the\n // default path rather than an opt-in one.\n get referenceRamp() {\n return self.effectiveColorScheme === 'reference-position' && self.graph\n ? computeReferenceRamp(self.graph, self.rampDomain)\n : undefined\n },\n // The assemblies this view is showing, which is the interface every\n // assembly-aware piece of the app looks for \u2014 `viewTitle` first among\n // them, which falls back through it and otherwise names the view\n // \"Untitled view\". Every published HPRC figure showed that.\n // `launchSubgraphView` only avoids it by writing `displayName`, so a\n // declaratively-instantiated view (a session snapshot, a figure spec) got\n // the fallback.\n //\n // A whole-file import has none: its stable names are a GFA's business and\n // need not name anything the session has loaded. Empty rather than\n // undefined, matching what an LGV with no displayed regions reports.\n get assemblyNames() {\n const region = self.loadedRegion\n return region ? [region.assemblyName] : []\n },\n // Screen px per layout unit, per axis. `scale` is the x zoom and the whole\n // of what a user's wheel moves; y is a second question the layout answers\n // for itself.\n //\n // On a row layout the pitch is already in screen px (rowSpacing.ts), so\n // scaleY is 1 and stays 1: rows are a track's row height, unchanged by\n // zoom, and the backbone under them zooms the way the ruler above it does.\n // On an isotropic layout (FMMM) x and y are one simulation space and one\n // number scales both, exactly as before.\n //\n // Everything that mixes the two axes takes their ratio as `yToX`\n // (scaleY / scaleX) \u2014 see computeEdgeCurves.\n get scaleX() {\n return axisScaleOf(self.scale, self.pixelRows).scaleX\n },\n get scaleY() {\n return axisScaleOf(self.scale, self.pixelRows).scaleY\n },\n // The pair, for everything that draws or hit-tests. Handed over as one\n // value on purpose: every consumer needs both, and passing them separately\n // let a caller supply the x scale and default y, which compiles and draws\n // a wrong picture. See AxisScale.\n get axisScale() {\n return axisScaleOf(self.scale, self.pixelRows)\n },\n // Extent of the drawing in layout units, shared by the pane height and\n // zoomToFit. On a reference-bp layout x is the cut window rather than\n // how far the drawing reaches: an allele anchored far outside it is a\n // fact about the graph, not a reason to draw the window at 6% of the\n // frame. A popped bubble fits to what it drew. Walk rows reach as far as\n // the bars on screen, which a repeat pick or a sample filter narrows\n // after the layout ran.\n get layoutBounds() {\n let bounds:\n { minX: number; minY: number; w: number; h: number } | undefined\n if (self.layoutResult) {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const segments of Object.values(\n self.layoutResult.nodePositions,\n )) {\n for (const seg of segments) {\n minX = Math.min(minX, seg.x)\n minY = Math.min(minY, seg.y)\n maxX = Math.max(maxX, seg.x)\n maxY = Math.max(maxY, seg.y)\n }\n }\n const region =\n self.layoutResult.referenceAxis && self.popStack.length === 0\n ? self.loadedRegion\n : undefined\n if (region && region.end > region.start) {\n minX = region.start\n maxX = region.end\n }\n const bars = this.walkRowBars\n const extent =\n bars && self.layoutResult.extent\n ? walkRowsExtent(bars)\n : self.layoutResult.extent\n if (extent) {\n maxX = Math.max(maxX, extent.maxX)\n maxY = Math.max(maxY, extent.maxY)\n }\n bounds = { minX, minY, w: maxX - minX, h: maxY - minY }\n }\n return bounds\n },\n // The pane is as tall as the drawing, rather than a fixed box the drawing\n // floats in the middle of.\n //\n // A row layout says how tall it is outright: rows are px, so the drawing's\n // height is the row count times the pitch and this is a sum, not a\n // derivation. It used to be derived from the drawing's ASPECT RATIO\n // against the x-fit scale, because y was in bp and a height in px did not\n // exist until a scale was chosen \u2014 which is also why a ceiling had to be\n // put on the row pitch to stop tall graphs from binding the fit on the\n // wrong axis. Both are gone with the unit.\n //\n // An isotropic layout still has no height of its own and keeps the aspect\n // derivation. It reads neither `scale` nor the height it is replacing, so\n // zoomToFit consumes this without feeding back into it.\n get canvasHeight() {\n if (\n self.viewportOwner === 'host' &&\n self.hostPaneHeight !== undefined\n ) {\n return self.hostPaneHeight\n }\n const bounds = this.layoutBounds\n const usableWidth = self.width - FIT_PADDING * 2\n // `paneHeight` replaces the built-in ceiling rather than adding a\n // second clamp under it, and the floor still wins: a pane shorter than\n // MIN_CANVAS_HEIGHT leaves no room to hover a node and read its\n // tooltip, which is the reason that floor exists.\n const ceiling = Math.max(\n MIN_CANVAS_HEIGHT,\n self.paneHeight ?? MAX_CANVAS_HEIGHT,\n )\n if (!bounds) {\n return ceiling\n }\n if (self.pixelRows) {\n // The variant map's drawing is one line; its glyphs and labels are\n // painted above it by the overlay and need the room a row layout\n // would give to rows.\n const floor =\n self.layoutMode === 'variants'\n ? VARIANT_MAP_HEIGHT\n : MIN_CANVAS_HEIGHT\n return Math.min(ceiling, Math.max(floor, bounds.h + FIT_PADDING * 2))\n }\n return bounds.w > 0 && usableWidth > 0\n ? Math.min(\n ceiling,\n Math.max(\n MIN_CANVAS_HEIGHT,\n (bounds.h * usableWidth) / bounds.w + FIT_PADDING * 2,\n ),\n )\n : ceiling\n },\n }))\n .views(self => ({\n get geneTrack() {\n return pickGeneTrack(self.geneTrackChoices, self.geneTrackId)\n },\n // Which of `graph.edges` are deletions, and what each one bypasses, keyed\n // the way the geometry and the hit index address an edge. One map per\n // graph rather than per rebuild, since both of those take it on every\n // pan.\n get deletionEdgeIndexes() {\n return new Map(self.deletions.map(d => [d.edgeIndex, d.bypassed]))\n },\n get hiddenEdgeIndexes() {\n return new Set(\n self.showDeletionEdges ? [] : self.deletions.map(d => d.edgeIndex),\n )\n },\n // The interval the reference-position ramp runs over, for the key beside\n // the drawing, and undefined when no key should be drawn. Two tutorials\n // carry \"red to magenta is left to right of the cut window\" as a sentence\n // in prose because nothing on screen said it.\n //\n // Read off the ramp the drawing is actually painted with rather than\n // re-derived from the domain, so the key cannot come to describe a\n // different interval from the hues beside it \u2014 and so a graph that states\n // no domain, whose interval has to be measured, does not pay for a second\n // neighbour walk to say what the first one already worked out.\n get referenceRampDomain() {\n const ramp = self.referenceRamp\n return ramp\n ? { start: ramp.start, end: ramp.start + ramp.span }\n : undefined\n },\n // The reference interval of the hovered node, for a connected linear view\n // to highlight. Only a graph cut from a track has one: a whole-file import\n // has no region, and its stable names need not name anything in a loaded\n // assembly.\n get hoverHighlight() {\n let result:\n | {\n refName: string\n assemblyName: string\n start: number\n end: number\n }\n | undefined\n const region = self.loadedRegion\n const nodeId = self.hoveredNode\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n if (region && nodeId !== null && nodeById && neighbors) {\n const span = nodeReferenceSpan({ nodeId, nodeById, neighbors })\n if (span) {\n result = {\n refName: region.refName,\n assemblyName: region.assemblyName,\n ...span,\n }\n }\n }\n return result\n },\n }))\n .views(self => ({\n // Every assembly this graph names a segment from, with the locus each one\n // contributes here. rGFA's SN tag is what makes this knowable: the graph\n // states its own contributors, so the view can offer a way out to each of\n // them without consulting an alignment.\n //\n // Gaps larger than the backbone span split a contributor's segments into\n // separate loci and the widest wins, so a sample that also contributes\n // sequence from a distant duplication is launched at the locus on screen\n // rather than at the union of the two.\n get contributingAssemblies() {\n const graph = self.graph\n const backbone = graph ? backboneNodes(graph) : []\n return graph\n ? contributingAssemblies(graph, {\n maxGap: backbone.length > 0 ? backboneSpan(backbone) : Infinity,\n })\n : []\n },\n }))\n .views(self => ({\n // A node's PanSN sample as an assembly this session can open, or\n // undefined. The graph's spelling and the assembly's need not agree:\n // HPRC writes `CHM13` where the assembly is UCSC's `hs1`. The track the\n // graph was cut from states that pairing in its `assemblyNameToPanSN`,\n // so its map is read first, then `assemblyManager`'s names and aliases.\n //\n // `has` before `get`, deliberately: `get` reports an unknown name to\n // `Core-handleUnrecognizedAssembly`, which asks every installed plugin to\n // go supply it, and this is a probe run for hundreds of haplotypes per\n // graph.\n get assemblyResolver() {\n const { assemblyManager } = getSession(self)\n const panSN = self.sourceAdapter?.assemblyNameToPanSN as\n Record<string, string> | undefined\n const byPrefix = new Map(\n Object.entries(panSN ?? {}).map(([asm, prefix]) => [prefix, asm]),\n )\n const loaded = (name: string | undefined) =>\n name !== undefined && assemblyManager.has(name)\n ? (assemblyManager.get(name)?.name ?? name)\n : undefined\n return (sample: string) =>\n loaded(byPrefix.get(sample)) ?? loaded(sample)\n },\n }))\n .views(self => ({\n // The contributors a view can actually be opened on: those naming an\n // assembly this session has loaded. Every strain of an E. coli pangenome\n // demo is its own assembly, so all of them resolve; an HPRC graph names\n // hundreds of haplotypes that no session loads, so only the reference\n // does.\n get launchableAssemblies() {\n return resolveContributors(\n withReferenceRegion(self.contributingAssemblies, self.loadedRegion),\n self.assemblyResolver,\n )\n },\n // Whether there is a linear view this graph may draw a highlight into \u2014\n // the paired one, or the session's only one on the reference assembly.\n // Read by the node menu, which offers the item only when it would land\n // somewhere.\n get canHighlightInLinearView() {\n const region = self.loadedRegion\n return (\n region !== undefined &&\n linearViewTarget({\n views: [...getSession(self).views],\n connectedViewId: self.connectedViewId,\n assemblyName: region.assemblyName,\n }) !== undefined\n )\n },\n // Where one node can be opened: on its own assembly, and on the reference\n // the graph was cut against. Both are padded to a readable window \u2014 a\n // base-level allele is a few bp, and a linear view framed on exactly that\n // shows no context at all.\n nodeLaunchTargets(nodeId: string) {\n const node = self.nodeById?.get(nodeId)\n const own = node ? nodeOwnLocation(node) : undefined\n const ownAssembly = own\n ? resolveLocationAssembly(self.assemblyResolver, own)\n : undefined\n const region = self.loadedRegion\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n const span =\n region && nodeById && neighbors\n ? nodeReferenceSpan({ nodeId, nodeById, neighbors })\n : undefined\n const onReference =\n region && span\n ? {\n sample: region.assemblyName,\n haplotype: undefined,\n refName: region.refName,\n ...span,\n }\n : undefined\n return {\n own:\n own && ownAssembly\n ? { location: paddedLocation(own), assembly: ownAssembly }\n : undefined,\n reference:\n region && onReference\n ? {\n location: paddedLocation(onReference),\n assembly: region.assemblyName,\n }\n : undefined,\n // Unpadded: a mark is of the node, the same span its hover band\n // draws. The padding above is room to read a view opened on it.\n highlight:\n region && onReference\n ? { location: onReference, assembly: region.assemblyName }\n : undefined,\n }\n },\n }))\n .views(self => ({\n // The linear view's zoom past which a follow cuts the source track's\n // coarse pair, or undefined for a track with none.\n get coarseAboveBpPerPx() {\n const coarse = self.sourceAdapter?.coarse as\n { aboveBpPerPx?: unknown } | undefined\n const above = coarse?.aboveBpPerPx\n return typeof above === 'number' ? above : undefined\n },\n }))\n .views(self => ({\n get cutTier(): SubgraphTier {\n return self.coarseCut && self.coarseAboveBpPerPx !== undefined\n ? 'coarse'\n : 'fine'\n },\n }))\n .views(self => ({\n get regionCapBp() {\n return self.cutTier === 'coarse' ? Infinity : self.maxRegionBp\n },\n }))\n .views(self => ({\n // The linear view this pane draws inside, when a display hosts it: x is\n // that view's window and the cut is re-made when the window leaves it.\n // A pane that is a view of its own has none.\n get host(): LinearHost | undefined {\n let view: unknown\n try {\n view = getContainingView(self)\n } catch {\n return undefined\n }\n return view !== self && isLinearHost(view) ? view : undefined\n },\n }))\n .views(self => ({\n // Whether the host places x. Only a layout whose x is reference bp can\n // take the window's transform; force, ordered and walk rows draw in\n // their own coordinates inside the track, and a popped bubble is a\n // picture of its own.\n get hostPlacesX() {\n const { host, loadedRegion: region, layoutResult: layout } = self\n return (\n host !== undefined &&\n host.initialized &&\n region !== undefined &&\n layout?.referenceAxis === true &&\n self.popStack.length === 0 &&\n !host.dynamicBlocks.contentBlocks.some(\n b => b.refName === region.refName && b.reversed,\n )\n )\n },\n }))\n .views(self => ({\n get hostFrame() {\n const { host, loadedRegion } = self\n return self.hostPlacesX && host && loadedRegion\n ? hostFrame(host, loadedRegion)\n : undefined\n },\n }))\n .actions(self => ({\n setError(error: unknown) {\n self.error = error\n self.isLoading = false\n },\n setStatusMessage(message: string) {\n self.statusMessage = message\n },\n setFetchMs(ms: number) {\n self.lastFetchMs = ms\n },\n setLayoutMs(ms: number) {\n self.lastLayoutMs = ms\n },\n setGeometryMetrics(\n ms: number,\n strokeCount: number,\n built: { scale: number; bounds: Bounds },\n ) {\n self.lastGeometryMs = ms\n self.lastGeometryStrokeCount = strokeCount\n self.builtViewport = built\n self.geometryVersion++\n },\n setLayoutQuality(quality: number) {\n self.layoutQuality = quality\n },\n setLinearLayout(linear: boolean) {\n self.linearLayout = linear\n },\n setLayoutMode(mode: LayoutModeValue) {\n self.layoutMode = mode\n },\n // Re-anchor in place rather than re-parsing: the coordinate walk is\n // already recorded on the graph, and only which path counts as rank 0\n // changes. The caller recomputes the layout, the same way it does after\n // setLayoutMode. An rGFA is left alone \u2014 its coordinates are not derived.\n setReferencePath(name: string) {\n self.referencePath = name\n const graph = self.graph\n if (graph?.anchoredBy === 'paths') {\n const reanchored = anchorFromPaths(graph, name)\n inheritForceLayouts(graph, reanchored)\n self.graph = reanchored\n }\n },\n setDrawPaths(draw: boolean) {\n self.drawPaths = draw\n },\n setShowPerf(show: boolean) {\n self.showPerf = show\n },\n setColorScheme(scheme: ColorScheme) {\n self.colorScheme = scheme\n },\n setBubbleSpread(spread: BubbleSpread) {\n self.bubbleSpread = spread\n },\n setNodeWidth(width: NodeWidth) {\n self.nodeWidth = width\n },\n setShowBubbles(show: boolean) {\n self.showBubbles = show\n },\n setShowDeletionEdges(show: boolean) {\n self.showDeletionEdges = show\n },\n setShowGenes(show: boolean) {\n self.showGenes = show\n },\n setGeneTrackId(trackId: string) {\n self.geneTrackId = trackId\n },\n setRepeatTrackId(trackId: string) {\n self.repeatTrackId = trackId\n },\n setRepeatKey(key: string) {\n self.repeatKey = key\n },\n setHighlightedPath(name: string) {\n self.highlightedPath = name\n },\n // Undefined restores the built-in ceiling. Nothing recomputes: the pane\n // reads canvasHeight and the drawing is placed by zoomToFit, which the\n // caller runs if it wants the drawing refitted into the new pane.\n setPaneHeight(px: number | undefined) {\n self.paneHeight = px\n if (self.viewportOwner === 'host') {\n self.hostPaneHeight = px\n }\n },\n // The track a hosted pane cuts from. The first settle makes the cut.\n adoptTrack(trackId: string) {\n if (self.loadedTrackId !== trackId) {\n self.loadedTrackId = trackId\n self.loadedRegion = undefined\n self.coarseCut = false\n self.graph = undefined\n self.layoutResult = undefined\n }\n },\n // The caller refetches \u2014 the number only describes how the next cut is\n // made, and the graph on screen was cut with the old one.\n setSubgraphContext(hops: number) {\n self.subgraphContext = hops\n },\n // Same contract: the caller refetches, since the set describes the next\n // cut and the graph on screen was cut for the old one.\n setWalkRowSamples(samples: string[] | undefined) {\n self.walkRowSamples = samples\n },\n setSubgraphHaplotypes(haplotypes: string[] | undefined) {\n self.subgraphHaplotypes = haplotypes\n },\n // Same contract as setSubgraphContext: describes how the NEXT cut is\n // gated, so the caller refetches. See the prop for why a session is\n // allowed to move this and the launch menus are not.\n setMaxRegionBp(bp: number) {\n self.maxRegionBp = bp\n },\n // Pair with a linear view for the hover sync, without ever repointing an\n // existing pairing: a graph launched *from* an LGV is already paired with\n // it, and stealing that would break the sync the user came in on. So a\n // launch out of the graph only claims the slot when it is empty.\n //\n // A slot naming a view that has been closed is empty too. Held, the view\n // the graph opened next never got its hover, since a highlight is drawn\n // only in the paired view.\n pairWithLinearView(viewId: string) {\n const paired = self.connectedViewId\n const stillOpen =\n paired !== undefined &&\n withRows([...getSession(self).views]).some(\n view => (view as { id?: unknown }).id === paired,\n )\n if (!stillOpen) {\n self.connectedViewId = viewId\n }\n },\n setHoveredNode(nodeId: string | null) {\n self.hoveredNode = nodeId\n },\n setHoveredEdge(edgeIdx: number | null) {\n self.hoveredEdge = edgeIdx\n },\n setSelectedNode(nodeId: string | null) {\n self.selectedNode = nodeId\n },\n setDraggingNode(nodeId: string | null) {\n self.draggingNode = nodeId\n },\n setPanning(panning: boolean) {\n self.isPanning = panning\n },\n stopDragging() {\n self.draggingNode = null\n self.isPanning = false\n },\n // Everything that names a part of the current graph. Reset when the graph\n // is replaced, and by clearGraph.\n clearInteractionState() {\n self.hoveredNode = null\n self.hoveredEdge = null\n self.selectedNode = null\n self.draggingNode = null\n self.isPanning = false\n },\n showNodeDetails(nodeId: string) {\n const node = self.nodeById?.get(nodeId)\n const nodeById = self.nodeById\n const neighbors = self.nodeNeighbors\n if (node && nodeById && neighbors) {\n const session = getSession(self)\n const region = self.loadedRegion\n // refName/start/end are what makes the widget show a location rather\n // than a bare id, and they are the same span the linear view\n // highlights on hover.\n const span = region\n ? nodeReferenceSpan({ nodeId, nodeById, neighbors })\n : undefined\n if (isSessionModelWithWidgets(session)) {\n session.showWidget(\n session.addWidget('BaseFeatureWidget', 'baseFeature', {\n featureData: {\n id: node.id,\n name: node.name,\n length: node.length,\n depth: node.depth,\n rank: node.stable?.rank,\n stableName: node.stable?.refName,\n // The assembly that contributed this segment, split off the\n // stable name. On an HPRC graph this is the only thing that\n // says which of 400-odd haplotypes an allele came from.\n contributingAssembly: nodeOwnLocation(node)?.sample,\n // and the haplotype, where the graph's names state one:\n // the assembly to load to open this node on its own\n // coordinates\n contributingHaplotype: nodeOwnLocation(node)?.haplotype,\n // Every assembly that traverses it, which is a different\n // question and one only a path GFA can answer. Absent on an\n // rGFA rather than approximated, so the two are never\n // confused: there `contributingAssembly` is first-seen, not\n // carriage.\n carriedBy: node.samples?.join(', '),\n ...(region && span\n ? {\n refName: region.refName,\n assemblyName: region.assemblyName,\n start: span.start,\n end: span.end,\n }\n : {}),\n },\n }),\n )\n }\n }\n },\n\n // A gesture on a hosted graph moves the linear view, and the frame\n // clock brings x back here; y stays the pane's own.\n setTransform(s: number, tx: number, ty: number) {\n const { host } = self\n if (self.viewportOwner === 'host' && host) {\n host.horizontalScroll(self.translateX - tx)\n } else {\n self.viewportOwner = 'user'\n self.scale = clampZoom(s)\n self.translateX = tx\n }\n self.translateY = ty\n },\n zoom(factor: number, centerX: number, centerY: number) {\n const { host } = self\n if (self.viewportOwner === 'host' && host) {\n host.zoomTo(host.bpPerPx / factor, centerX)\n return\n }\n self.viewportOwner = 'user'\n const newScale = clampZoom(self.scale * factor)\n const ratio = newScale / self.scale\n self.scale = newScale\n self.translateX = centerX - (centerX - self.translateX) * ratio\n // y only follows when it is on the same scale. A row layout's y is\n // screen px (scaleY === 1), so nothing about it changes as x zooms \u2014\n // moving translateY by the ratio there would slide the rows off under\n // the cursor while their pitch stayed put.\n if (!self.pixelRows) {\n self.translateY = centerY - (centerY - self.translateY) * ratio\n }\n },\n setViewportDirty() {\n self.viewportDirty++\n },\n // The positions themselves moved, as opposed to the window onto them.\n setPositionsDirty() {\n self.positionsVersion++\n },\n zoomToFit() {\n // A layout is routinely degenerate on one axis: an anchored window\n // holding only backbone segments puts every node on row 0. So each axis\n // constrains the scale only when it has extent, and only a layout with\n // no extent at all is unfittable. Requiring extent on both axes left\n // that window at scale 1 with the graph off-screen entirely, since x\n // there is reference bp.\n const bounds = self.layoutBounds\n const usableWidth = self.width - FIT_PADDING * 2\n const usableHeight = self.canvasHeight - FIT_PADDING * 2\n // A host owns x, so a fit while hosted places the rows only.\n if (self.viewportOwner === 'host') {\n if (bounds && usableHeight > 0) {\n self.translateY = fittedTranslateY(\n bounds,\n usableHeight,\n self.scaleY,\n )\n }\n return\n }\n // Nothing to fit into before the canvas is measured. The autorun re-runs\n // once width lands, so skipping here beats computing a negative scale\n // and persisting that transform into the session snapshot.\n if (\n bounds &&\n usableWidth > 0 &&\n usableHeight > 0 &&\n // A row layout needs x extent specifically: y cannot stand in for it\n // there, having no scale left to solve for.\n (bounds.w > 0 || (!self.pixelRows && bounds.h > 0))\n ) {\n // A row layout fits on x ALONE, which is the point of it: the pane is\n // as tall as the rows are and the rows are as tall as they need to be,\n // so there is no height to fit into and nothing that can make the\n // backbone narrower than the pane it is supposed to line up with. That\n // is the bug this axis change is for \u2014 past ~12 rows the drawing was\n // taller than it was wide, the vertical axis bound the fit, and the\n // backbone stopped matching the linear view above it.\n //\n // An isotropic layout still fits on whichever axis binds, and the\n // leftover on the other is split evenly.\n const fitX = bounds.w > 0 ? usableWidth / bounds.w : Infinity\n const fitY = bounds.h > 0 ? usableHeight / bounds.h : Infinity\n const newScale = clampZoom(\n self.pixelRows ? fitX : Math.min(fitX, fitY),\n )\n self.scale = newScale\n self.translateX =\n FIT_PADDING -\n bounds.minX * newScale +\n (usableWidth - bounds.w * newScale) / 2\n // scaleY, which a row layout pins at 1\n self.translateY = fittedTranslateY(\n bounds,\n usableHeight,\n self.pixelRows ? 1 : newScale,\n )\n }\n },\n clearPerfMetrics() {\n self.lastFetchMs = undefined\n self.lastLayoutMs = undefined\n self.lastGeometryMs = undefined\n self.lastGeometryStrokeCount = undefined\n self.builtViewport = undefined\n },\n }))\n .actions(self => ({\n // The frame clock. Unclamped, since clampZoom's floor is there to keep a\n // scale finite and 1 / bpPerPx already is; clamped, a whole-chromosome\n // window would stop lining up.\n hostTransform(scale: number, translateX: number) {\n const engaging = self.viewportOwner !== 'host'\n if (engaging) {\n self.hostPaneHeight = self.canvasHeight\n self.viewportOwner = 'host'\n }\n self.scale = scale\n self.translateX = translateX\n if (engaging) {\n self.zoomToFit()\n }\n },\n // A drawing still on the reference stays where the host left it; one\n // whose x no longer means bp is refit.\n releaseHost() {\n if (self.viewportOwner === 'host') {\n self.hostPaneHeight = undefined\n if (self.layoutResult?.referenceAxis) {\n self.viewportOwner = 'user'\n } else {\n self.viewportOwner = 'fit'\n self.zoomToFit()\n }\n }\n },\n }))\n .actions(self => {\n // Pan/zoom can wait: the geometry on screen is still correct while the\n // gesture runs (the viewport bounds carry 20% overscan), so rebuilding is\n // deferred until the user settles.\n function scheduleViewportDirty() {\n clearTimeout(self.viewportDirtyTimer)\n self.viewportDirtyTimer = setTimeout(() => {\n if (isAlive(self)) {\n self.setViewportDirty()\n }\n }, VIEWPORT_DEBOUNCE_MS)\n }\n\n // A node drag cannot wait \u2014 the node only moves on screen when its\n // geometry is rebuilt \u2014 but a bump costs a full geometry rebuild plus\n // invalidation of both hit-detection indexes, and mousemove fires in\n // bursts well above the frame rate. Coalescing to the next frame keeps the\n // drag live while bounding that work to once per frame instead of once\n // per event.\n function requestPositionsDirtyFrame() {\n cancelAnimationFrame(self.positionsDirtyFrame)\n self.positionsDirtyFrame = requestAnimationFrame(() => {\n if (isAlive(self)) {\n self.setPositionsDirty()\n }\n })\n }\n\n return {\n // Moves the position objects IN PLACE, which is the one thing\n // jbrowse-components' upload invariant says not to do (\"per-region\n // upload values must be freshly constructed, never mutated \u2014 backends\n // diff by reference identity\"), so it is worth saying why this is not\n // that case and what it costs instead.\n //\n // Nothing here diffs by identity: the geometry is rebuilt wholesale and\n // handed over as one batch, so `nodePositions` identity is not a\n // protocol between the model and the backend the way a per-region map\n // is. What identity DOES drive is the fit: the zoom-to-fit autorun and\n // `layoutBounds` \u2014 and so `canvasHeight` \u2014 key off `layoutResult`, and\n // they mean \"a new LAYOUT arrived\", not \"a node moved\". Publishing a\n // fresh positions record per frame of a drag would refit the drawing\n // and resize the pane under the cursor, sixty times a second.\n //\n // So the change is announced by `positionsVersion` instead, and every\n // cache that would otherwise trust identity takes it as a key. The cost\n // of that trade is that the version is a thing to remember: a caller\n // that mutates here and does not bump it gets stale curves, stale hit\n // boxes and stale labels, silently. Everything downstream of it is\n // reached from this one action.\n moveNode(nodeId: string, dx: number, dy: number) {\n const positions = self.layoutResult?.nodePositions\n if (positions?.[nodeId]) {\n for (const seg of positions[nodeId]) {\n seg.x += dx\n seg.y += dy\n }\n requestPositionsDirtyFrame()\n }\n },\n scheduleViewportDirty,\n }\n })\n .actions(self => {\n let loadController: AbortController | undefined\n\n // `scaling.nodes` rather than the graph's own: under a compressing\n // drawn-length law a node's `length` crosses this boundary as a drawn\n // length, which is all the engine ever reads it as.\n //\n // An anchored graph also carries a seed per node, the backbone along x,\n // and asks the engine not to rotate components: FMMM then keeps the\n // reference's coarse shape instead of curling it into a C\n // (docs/layout-experiments.md, experiment 2).\n function callLayout(graph: Graph, scaling: LayoutScaling) {\n const session = getSession(self)\n const { rpcManager } = session\n // Stable grouping key for the layout RPC; a view has no display-level\n // rpcSessionId. `rpcManager.call` injects sessionId into the args.\n const sessionId = 'graph'\n const anchored = graph.nodes.some(isBackbone)\n return rpcManager.call(sessionId, 'GraphComputeLayout', {\n graph: {\n nodes: anchored ? seededNodes(graph, scaling) : scaling.nodes,\n edges: graph.edges,\n },\n options: {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n ...scaling.opts,\n ...(anchored ? { rotateComponents: false } : {}),\n },\n signal: loadController?.signal,\n // A StatusCallback takes an RpcStatus, not a string \u2014 it may be a\n // bare label, a phase, or a phase that threw. `statusMessageText` is\n // core's reader for the human-facing line.\n statusCallback: status => {\n self.setStatusMessage(statusMessageText(status) ?? '')\n },\n }) as Promise<{ result: LayoutResult; duration: number }>\n }\n\n // The engine's inputs, and only those: the graph, plus what `callLayout`\n // puts in `options`. The reference path is there because the seeds are a\n // function of it; the colour scheme and the anchored modes' own settings\n // are absent because none of them reaches the engine.\n function forceLayoutKey(graph: Graph) {\n const anchored = graph.nodes.some(isBackbone)\n return `${self.layoutQuality}|${self.linearLayout}|${self.bubbleSpread}|${anchored}|${graph.referencePath ?? ''}`\n }\n\n // Single dispatch point for every layout mode. A mode that returns a\n // result computed it locally; one that returns undefined can't draw this\n // graph and hands off to the remote FMMM engine, which is also how\n // 'force' is expressed. See LAYOUT_MODES.\n function* computeLayout(graph: Graph) {\n const start = performance.now()\n const local = layoutModeByValue(self.layoutMode).run(\n graph,\n self.loadedRegion,\n self.host ? self.layoutResult?.sampleRows : undefined,\n )\n if (local) {\n return { result: local, duration: performance.now() - start }\n }\n const cache = forceLayoutsOf(graph)\n const key = forceLayoutKey(graph)\n const hit = cache.get(key)\n if (hit) {\n return { result: hit, duration: performance.now() - start }\n }\n // The engine lays out the runs, not the nodes: a base-level cut is\n // thousands of nodes in unbranching chains, and one chain per run is\n // the same drawing at a third of the time. Members take their share of\n // the run's polyline by drawn length, so the picture is per node again\n // before anything else sees it.\n const spread = spreadFor(self.bubbleSpread)\n const merged = mergeRuns(graph)\n const { result, duration } = (yield callLayout(\n merged.graph,\n layoutScaling(merged.graph, spread),\n )) as { result: LayoutResult; duration: number }\n const scaling = layoutScaling(graph, spread)\n const drawn = new Map(\n scaling.nodes.map(n => [\n n.id,\n drawnNodeLength(scaling.opts, n.length),\n ]),\n )\n const positions = splitRuns(\n result.nodePositions,\n merged.runs,\n id => drawn.get(id) ?? 0,\n )\n // Turned so the reference reads left to right, like the linear view\n // above it. Before `remember`, so the cache hands back the drawing as\n // it was shown.\n const oriented = {\n ...result,\n nodePositions: graph.nodes.some(isBackbone)\n ? orientToReference(graph, positions)\n : positions,\n }\n // Under the key read BEFORE the call: the settings that produced this\n // drawing are not necessarily the ones on screen now, and filing it\n // under the current ones would serve it up as a layout it is not.\n remember(cache, key, oriented)\n return { result: oriented, duration }\n }\n\n // Which layout request is the live one. A layout is async and nothing in\n // the UI waits for it, so several are routinely in flight: every control\n // in the settings dialog fires `recomputeLayout` on change, and a force\n // layout takes seconds where an anchored one is instant.\n //\n // The graph identity below is not enough to order them, because the\n // competing requests are usually layouts of the SAME graph. Left to\n // resolve order the last one to FINISH won rather than the last one\n // asked for, so switching away from a slow force layout \u2014 to an anchored\n // mode, a cheaper bubble spread, a lower quality \u2014 drew the abandoned\n // one over the top of the chosen one seconds later, with the dropdown\n // still naming the choice that was discarded.\n let liveRequest = 0\n\n // Applied under a guard because a layout is async and the user can load a\n // different graph, or ask for a different layout of it, while one is in\n // flight; the stale result must not land.\n function* layoutInto(graph: Graph) {\n const request = ++liveRequest\n const isLive = () => self.graph === graph && request === liveRequest\n const signal = loadController?.signal\n let computed\n try {\n computed = yield* computeLayout(graph)\n } catch (e) {\n // A discarded layout's failure is not the user's problem: the drawing\n // they asked for is on screen or on its way, and raising a banner\n // over it reports a graph as broken because a setting they moved on\n // from could not be drawn. An aborted one was discarded by the load\n // that replaced it, even when the graph is still the same object.\n if (!isLive() || signal?.aborted) {\n return false\n }\n throw e\n }\n const live = isLive()\n if (live) {\n self.layoutResult = computed.result\n self.setLayoutMs(computed.duration)\n }\n return live\n }\n\n // `keepSelection` for a re-cut of the same source: node ids survive one\n // where edge indexes do not, so the selection is found again by id.\n function* parseAndLayout(\n text: string,\n name: string,\n keepSelection = false,\n ) {\n self.setStatusMessage('Parsing GFA')\n const gfaGraph = parseGFA(text)\n // A general GFA states its coordinates only in its P/W lines, so the\n // walk that recovers them happens before anything reads `stable` \u2014\n // otherwise the anchored layouts see an unanchored graph and hand off\n // to force.\n const graph = anchorGraph(\n convertGFAToGraph(gfaGraph, name),\n self.preferredReferencePath,\n )\n // Checked here, between parsing and laying out, because this is the one\n // point both load paths pass through and it is upstream of everything\n // expensive: the layout, the geometry and the per-frame draw calls all\n // scale with this number. The whole-file import path had no cap at all,\n // so a chromosome-scale GFA would parse and then freeze the tab.\n if (graph.nodes.length === 0) {\n throw new Error(`No graph segments in ${name}`)\n }\n if (graph.nodes.length > self.maxGraphNodes) {\n throw new Error(\n `Graph too large to draw: ${graph.nodes.length.toLocaleString()} nodes (limit ${self.maxGraphNodes.toLocaleString()}). Zoom in to a smaller region, or raise maxGraphNodes on this view.`,\n )\n }\n const selected = keepSelection ? self.selectedNode : null\n self.graph = graph\n self.indexBubbles = undefined\n self.geneFeatures = undefined\n self.repeatArrays = undefined\n self.popStack = []\n // hoveredEdge is an index into graph.edges and hoveredNode/selectedNode\n // are ids, so all three address the graph being replaced here. Carrying\n // them over points the tooltip and the highlight at whatever now happens\n // to sit at that index.\n self.clearInteractionState()\n if (selected !== null && self.nodeById?.has(selected)) {\n self.selectedNode = selected\n }\n self.setStatusMessage('Computing layout')\n yield* layoutInto(graph)\n }\n\n // Which load is the live one, for the reason liveRequest orders layouts:\n // every change in the settings dialog re-cuts, a remote cut takes seconds,\n // and the last one to finish is not the last one asked for.\n let liveLoad = 0\n\n function beginLoad() {\n const load = ++liveLoad\n loadController?.abort()\n loadController = new AbortController()\n self.loadCanceled = false\n return {\n isLive: () => load === liveLoad,\n signal: loadController.signal,\n }\n }\n\n function loadedTrack() {\n return self.loadedTrackId\n ? getSession(self).tracks.find(t => t.trackId === self.loadedTrackId)\n : undefined\n }\n\n // The bubble index that the hosted HPRC build keeps beside its segments,\n // `<prefix>.bubbles.bed.gz`, read through the bubble adapter over the same\n // window. A graph whose source has no such file, the ordinary case for a\n // graph of one's own, keeps its derived bubbles, so a failure here is not\n // the graph's problem. A coarse cut reads none: its nodes are the\n // bubbles, and the index over its window can run to a chromosome's rows.\n function* loadBubbles(\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n isLive: () => boolean,\n ) {\n // The track config arrives as written, so the prefix is either the\n // `uri` shorthand or the segments location it expands to.\n const prefix = bubblePrefix(adapterConfig)\n if (\n adapterConfig.type !== 'RgfaTabixAdapter' ||\n prefix === undefined ||\n self.cutTier === 'coarse'\n ) {\n return\n }\n try {\n const features = (yield getSession(self).rpcManager.call(\n 'graph',\n 'CoreGetFeatures',\n {\n adapterConfig: {\n type: 'MinigraphBubbleAdapter',\n uri: `${prefix}.bubbles.bed.gz`,\n baseUri: adapterConfig.baseUri,\n assemblyNameToPanSN: adapterConfig.assemblyNameToPanSN,\n },\n regions: [region],\n },\n )) as Feature[]\n if (isLive()) {\n self.indexBubbles = features.map(f => ({\n refName: region.refName,\n start: f.get('start'),\n end: f.get('end'),\n segmentCount: f.get('segmentCount') as number,\n pathCount: (f.get('pathCount') as number | undefined) ?? 0,\n inversion: f.get('inversion') as boolean,\n shortestAlleleLength: f.get('shortestAlleleLength') as number,\n longestAlleleLength: f.get('longestAlleleLength') as number,\n segments: f.get('segments') as string,\n shortestAllele: undefined,\n longestAllele: undefined,\n }))\n }\n } catch (e) {\n console.warn('[GraphGenomeView] no bubble index for this graph', e)\n }\n }\n\n // A session track's features over the cut. Undefined when the session\n // has no such track or the read fails: an annotation is never the\n // graph's problem.\n function* trackFeatures(\n trackId: string | undefined,\n region: SubgraphRegion,\n what: string,\n ) {\n const session = getSession(self)\n const config = trackId\n ? session.tracks.find(t => t.trackId === trackId)\n : undefined\n if (!config) {\n return undefined\n }\n try {\n return (yield session.rpcManager.call('graph', 'CoreGetFeatures', {\n adapterConfig: readConfObject(config, 'adapter'),\n regions: [region],\n })) as Feature[]\n } catch (e) {\n console.warn(`[GraphGenomeView] no ${what} for this graph`, e)\n return undefined\n }\n }\n\n // The genes over the cut, from the session's annotation track for the\n // assembly, so the backbone can carry its exons and names.\n function* loadGenes(region: SubgraphRegion, isLive: () => boolean) {\n const features = yield* trackFeatures(\n self.geneTrack?.trackId,\n region,\n 'genes',\n )\n if (features && isLive()) {\n self.geneFeatures = geneModelsFrom(features)\n }\n }\n\n // The tandem repeat arrays over the cut, from the session's repeat\n // track, for the walk rows to measure between and tile by.\n function* loadRepeats(region: SubgraphRegion, isLive: () => boolean) {\n const features = yield* trackFeatures(\n self.repeatTrack?.trackId,\n region,\n 'repeats',\n )\n if (features && isLive()) {\n self.repeatArrays = repeatArraysFrom(features)\n }\n }\n\n // Inner loading logic shared by loadFromTabixSubgraph and refetchIfNeeded\n function* doSubgraphLoad(\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n opts: SubgraphCutOptions = {},\n ) {\n const { isLive, signal } = beginLoad()\n const track = loadedTrack()\n const regionSize = region.end - region.start\n // Past the size cap the graph view declines rather than degrading to a\n // non-graph rectangle rendering; large-region and full-genome\n // comparison is a linear synteny view instead.\n const refusal =\n (track &&\n offReferenceProblem(\n graphReferenceAssembly(track),\n region.assemblyName,\n )) ??\n (regionSize > self.regionCapBp\n ? `Region too large (${formatSpanBp(regionSize)}) \u2014 zoom in to view graph (max ${formatSpanBp(self.regionCapBp)})`\n : undefined)\n if (refusal !== undefined) {\n self.graph = undefined\n self.layoutResult = undefined\n self.isLoading = false\n self.error = new Error(refusal)\n return\n }\n self.isLoading = true\n self.error = undefined\n self.setStatusMessage('Fetching subgraph')\n try {\n const session = getSession(self)\n const { rpcManager } = session\n const sessionId = 'graph' // getRpcSessionId(self) no rpcSessionId getter\n const fetchStart = performance.now()\n const gfaText = (yield rpcManager.call(sessionId, 'GetSubgraph', {\n adapterConfig,\n region,\n opts,\n signal,\n })) as string\n if (!isLive()) {\n return\n }\n self.setFetchMs(performance.now() - fetchStart)\n if (!gfaText) {\n throw new Error(\n 'Adapter returned no GFA \u2014 region may be outside indexed data or the adapter does not implement getSubgraph',\n )\n }\n const label = locLabel(region)\n yield* parseAndLayout(gfaText, label, true)\n if (!isLive()) {\n return\n }\n // Three independent remote reads, each landing as it arrives: in\n // turn they held the overlay over a drawn graph for the sum of their\n // round trips.\n self.setStatusMessage('Reading annotations')\n yield Promise.all([\n flow(loadBubbles)(adapterConfig, region, isLive),\n flow(loadGenes)(region, isLive),\n flow(loadRepeats)(region, isLive),\n ])\n } catch (e) {\n if (isLive()) {\n console.error('[GraphGenomeView.loadFromTabixSubgraph]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.isLoading = false\n }\n }\n }\n\n // The cut behind a graph whose source is a track in this session, which is\n // what both a launch snapshot and a restored session carry. Silent when\n // there is no such pair, which is the whole-file case. A pair naming a\n // track the session lacks is reported, because otherwise the view sits on\n // an empty import form with nothing saying why.\n //\n // Nothing here saves and restores the transform: `viewportOwner` is\n // what protects a restored session's pan/zoom, and it gates the fit\n // autorun \u2014 the only thing in this flow that would otherwise move the\n // view. A save/restore pair around the load wrote back the values it had\n // just read.\n function* cutFromLoadedTrack() {\n const region = self.loadedRegion\n const track = loadedTrack()\n if (region && self.loadedTrackId && !track) {\n self.error = new Error(\n `The track this graph was cut from, \"${self.loadedTrackId}\", is not in this session`,\n )\n } else if (track && region) {\n // A hop past a coarse cut reaches nothing new: every bubble node's\n // two links are indexed under the backbone either side of it.\n const coarse = self.cutTier === 'coarse'\n yield* doSubgraphLoad(readConfObject(track, 'adapter'), region, {\n hops: coarse ? 0 : self.subgraphContext,\n haplotypes: self.subgraphHaplotypes,\n tier: coarse ? 'coarse' : undefined,\n })\n }\n }\n\n // Raises `isLoading` before any fetch, so a view waiting on a remote file\n // shows its loading state instead of the import form. Text already in\n // hand is parsed without yielding first.\n // `region` is the window a declared file was stated beside. Held through\n // the load rather than restored after it, so the parse anchors on that\n // assembly's path and a failed or canceled load can still be retried\n // with it.\n function* loadWholeGFA(\n name: string,\n source: string | ((signal: AbortSignal) => Promise<string>),\n region?: SubgraphRegion,\n ) {\n const { isLive, signal } = beginLoad()\n self.loadedTrackId = ''\n self.loadedRegion = region\n self.coarseCut = false\n self.isLoading = true\n self.error = undefined\n try {\n const text =\n typeof source === 'string'\n ? source\n : ((yield source(signal)) as string)\n if (isLive()) {\n yield* parseAndLayout(text, name)\n }\n } catch (e) {\n if (isLive()) {\n console.error('[GraphGenomeView.loadWholeGFA]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.isLoading = false\n }\n }\n return isLive()\n }\n\n return {\n // Back to the import form: drop the graph, everything derived from it,\n // and the source it came from. Leaving the source declared kept\n // `showLoading` true over the import form and had a reloaded session\n // cut the dismissed graph again. Any load in flight ends here too,\n // or it would land its graph afterwards.\n clearGraph() {\n loadController?.abort()\n loadController = undefined\n liveLoad++\n liveRequest++\n self.graph = undefined\n self.layoutResult = undefined\n self.loadedTrackId = ''\n self.loadedRegion = undefined\n self.coarseCut = false\n self.cutNote = undefined\n self.gfaLocation = undefined\n self.indexBubbles = undefined\n self.geneFeatures = undefined\n self.repeatArrays = undefined\n self.popStack = []\n self.error = undefined\n self.isLoading = false\n self.loadCanceled = false\n self.statusMessage = ''\n self.clearInteractionState()\n self.clearPerfMetrics()\n },\n cancelLoad() {\n if (self.canCancelLoad) {\n loadController?.abort()\n loadController = undefined\n liveLoad++\n liveRequest++\n self.graph = undefined\n self.isLoading = false\n self.statusMessage = ''\n self.loadCanceled = true\n }\n },\n loadGFA: flow(function* (text: string, name = 'Imported GFA') {\n yield* loadWholeGFA(name, text)\n }),\n loadGFAFromLocation: flow(function* (location: FileLocation) {\n self.setStatusMessage('Fetching GFA')\n const stated = self.loadedRegion\n const live = yield* loadWholeGFA(\n 'uri' in location\n ? (location.uri.split('/').pop() ?? 'GFA')\n : 'GFA',\n signal =>\n openLocation(location).readFile({ encoding: 'utf8', signal }),\n stated,\n )\n if (stated && live && self.graph) {\n yield* loadRepeats(stated, () => self.loadedRegion === stated)\n }\n }),\n loadFromTabixSubgraph: flow(function* (\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n opts: {\n trackId?: string\n } = {},\n ) {\n self.loadedTrackId = opts.trackId ?? ''\n self.loadedRegion = opts.trackId ? region : undefined\n self.coarseCut = false\n yield* doSubgraphLoad(adapterConfig, region, {\n hops: self.subgraphContext,\n haplotypes: self.subgraphHaplotypes,\n })\n }),\n // Cut on attach only \u2014 a graph already on screen is either the user's\n // own or one this just drew.\n refetchIfNeeded: flow(function* () {\n if (!self.graph) {\n yield* cutFromLoadedTrack()\n }\n }),\n // Cut the same region again, for a change to what the cut returns\n // rather than to how it is drawn (subgraphContext).\n reloadSubgraph: flow(function* () {\n yield* cutFromLoadedTrack()\n }),\n // Re-read one annotation track alone, for a track change or a graph\n // that came from a whole file beside a stated region. The graph, its\n // layout and any open bubble stay as they are. Live only while the\n // track it read is still the one chosen, so a slow read cannot land\n // over the pick that followed it.\n reloadRepeats: flow(function* () {\n const region = self.loadedRegion\n const trackId = self.repeatTrack?.trackId\n if (region) {\n yield* loadRepeats(\n region,\n () =>\n self.loadedRegion === region &&\n self.repeatTrack?.trackId === trackId,\n )\n }\n }),\n reloadGenes: flow(function* () {\n const region = self.loadedRegion\n const trackId = self.geneTrack?.trackId\n if (region) {\n yield* loadGenes(\n region,\n () =>\n self.loadedRegion === region &&\n self.geneTrack?.trackId === trackId,\n )\n }\n }),\n // Open one bubble: the graph becomes the segments the bubble row names,\n // drawn in the layout the reader is in, or force-directed from the\n // variant map. The graph it came from stays behind it, one click away,\n // and the popped graph gets its own derived bubbles, so a superbubble\n // opens progressively.\n popBubble: flow(function* (bubble: MinigraphBubble) {\n const graph = self.graph\n if (!graph) {\n return\n }\n const sub = bubbleSubgraph(graph, bubbleSegmentIds(bubble))\n if (sub.nodes.length === 0) {\n getSession(self).notify(\n 'None of the segments of this bubble are in the cut; widen the graph context to open it',\n 'info',\n )\n return\n }\n const { isLive } = beginLoad()\n self.popStack = [\n ...self.popStack,\n {\n graph,\n layoutMode: self.layoutMode,\n label: graph.name,\n indexBubbles: self.indexBubbles,\n },\n ]\n self.indexBubbles = undefined\n const label = `${BUBBLE_KIND_NAMES[classifyBubble(bubble).kind]} at ${bubble.refName}:${bubble.start.toLocaleString()}`\n self.graph = { ...sub, name: label }\n if (self.layoutMode === 'variants') {\n self.layoutMode = 'force'\n }\n self.clearInteractionState()\n self.viewportOwner = 'fit'\n self.isLoading = true\n try {\n if (yield* layoutInto(self.graph)) {\n self.isLoading = false\n }\n } catch (e) {\n if (isLive()) {\n self.error = e\n self.isLoading = false\n }\n }\n }),\n unpopBubble: flow(function* () {\n const from = self.poppedFrom\n if (!from) {\n return\n }\n self.popStack = self.popStack.slice(0, -1)\n self.graph = from.graph\n self.layoutMode = from.layoutMode\n self.indexBubbles = from.indexBubbles\n self.clearInteractionState()\n self.viewportOwner = 'fit'\n self.isLoading = true\n try {\n if (yield* layoutInto(from.graph)) {\n self.isLoading = false\n }\n } catch (e) {\n self.error = e\n self.isLoading = false\n }\n }),\n recomputeLayout: flow(function* () {\n const graph = self.graph\n if (!graph) {\n return\n }\n self.isLoading = true\n self.setStatusMessage('Computing layout')\n\n try {\n // A superseded request leaves the spinner to the request that\n // replaced it: clearing it here reports \"done\" while the layout the\n // user actually asked for is still being computed, which is the\n // common case when a cheap choice follows an expensive one.\n if (yield* layoutInto(graph)) {\n self.isLoading = false\n }\n } catch (e) {\n console.error('[GraphGenomeView.recomputeLayout]', e)\n self.error = e\n self.isLoading = false\n }\n }),\n }\n })\n .actions(self => ({\n // The settle clock. A pan the cut still holds fetches nothing; one past\n // its edge re-cuts the window plus a window-width each side, on the tier\n // the zoom asks for. Overlapping re-cuts are ordered by doSubgraphLoad's\n // liveLoad, so the latest window wins.\n settleOn(seen: HostWindow) {\n const above = self.coarseAboveBpPerPx\n const tier =\n above !== undefined && seen.bpPerPx > above ? 'coarse' : 'fine'\n const cap = tier === 'coarse' ? Infinity : self.maxRegionBp\n const visible = seen.end - seen.start\n self.cutNote =\n visible > cap\n ? `Holding the last cut: ${formatSpanBp(visible)} is past the ${formatSpanBp(cap)} a cut may span`\n : undefined\n if (\n self.cutNote !== undefined ||\n (tier === self.cutTier && cutHolds(self.loadedRegion, seen))\n ) {\n return\n }\n self.coarseCut = tier === 'coarse'\n self.loadedRegion = hostCut(seen, cap)\n self.recuts++\n void self.reloadSubgraph()\n },\n }))\n .actions(self => ({\n // The host's two clocks, started by the display that hosts the pane.\n // The frame clock moves x with every frame of the linear view and\n // fetches nothing; the settle clock wakes on its debounced blocks and\n // re-cuts only when the window has left the cut, whatever the layout.\n startHosting() {\n addDisposer(\n self,\n reaction(\n () => self.hostPlacesX,\n places => {\n if (!places) {\n self.releaseHost()\n }\n },\n { name: 'GraphHostPlacesX' },\n ),\n )\n addDisposer(\n self,\n reaction(\n () => self.hostFrame,\n frame => {\n if (frame) {\n self.hostTransform(frame.scale, frame.translateX)\n }\n },\n {\n equals: (a, b) =>\n a?.scale === b?.scale && a?.translateX === b?.translateX,\n fireImmediately: true,\n name: 'GraphHostFrame',\n },\n ),\n )\n addDisposer(\n self,\n reaction(\n () => self.host?.coarseDynamicBlocks,\n blocks => {\n const { host } = self\n const seen = blocks && host ? hostWindow(host) : undefined\n if (seen) {\n self.settleOn(seen)\n }\n },\n { fireImmediately: true, name: 'GraphHostSettle' },\n ),\n )\n },\n }))\n .actions(self => ({\n startRenderingBackend(backend: Renderer) {\n if (!self.autorunsInstalled) {\n // Autorun: paint the lane this graph was cut from in the graph's own\n // reference-position ramp, so a block above and its node below share\n // a hue with nothing configured. Follows the domain, so a re-cut or\n // an opened bubble moves the lane's ramp with the drawing.\n addDisposer(\n self,\n autorun(() => {\n const domain = self.rampDomain\n const trackId = self.loadedTrackId\n if (\n domain &&\n trackId &&\n self.effectiveColorScheme === 'reference-position'\n ) {\n const display = untracked(() =>\n sourceLaneDisplay(\n getSession(self).views,\n self.connectedViewId,\n trackId,\n ),\n )\n if (display) {\n paintSourceLane(display, referencePositionColor(domain))\n }\n }\n }),\n )\n\n // Autorun: keep the view fitted to the graph until the user moves it.\n // Reads layoutResult plus (via zoomToFit) width/canvasHeight, so it\n // re-fires \u2014 and re-fits \u2014 as the layout arrives and the canvas is\n // measured, rather than firing once against not-yet-known dimensions.\n // A manual pan/zoom (or a restored-session transform) makes the\n // viewport the user's, and a host makes it the linear view's.\n addDisposer(\n self,\n autorun(() => {\n if (\n self.layoutResult &&\n untracked(() => self.viewportOwner === 'fit')\n ) {\n self.zoomToFit()\n }\n }),\n )\n\n // Autorun: mirror a connected linear view's hover onto the graph. An\n // LGV writes `{hoverPosition, hoverFeature}` to session.hovered on\n // every mousemove; neither field names the source view, so the guard\n // is that the position lies in the region this graph was cut from.\n //\n // Only `hovered` is tracked \u2014 the graph reads are untracked, so a\n // geometry rebuild can't re-fire this and clobber a hover the canvas\n // itself set. Assigning an unchanged id doesn't notify, so a hover\n // that travels within one segment costs nothing downstream.\n addDisposer(\n self,\n autorun(() => {\n const hover = readLgvHover(getSession(self).hovered)\n untracked(() => {\n const region = self.loadedRegion\n const graph = self.graph\n if (region && graph) {\n self.setHoveredNode(\n hover && hoverInRegion(hover, region)\n ? nodeForLgvHover({ hover, nodes: graph.nodes })\n : null,\n )\n }\n })\n }),\n )\n\n // Reaction: drop a hover the pointer cannot still be over.\n //\n // A hover is set by a mousemove on the canvas, and here the drawing\n // moves under a STATIONARY cursor on three axes \u2014 the wheel zoom, the\n // toolbar's zoom and fit buttons, and a pan \u2014 none of which fire a\n // pointer event. Left alone the tooltip goes on naming the node that\n // used to be under the cursor, `hoverHighlight` goes on publishing its\n // span, and the paired linear view paints a band over the wrong\n // sequence until the mouse happens to move.\n //\n // This is jbrowse-components' `installClearHoverOnViewportChange`\n // (BaseLinearDisplay), whose three axes are bpPerPx / offsetPx /\n // scrollTop; a graph view's are its own transform. Its rule is worth\n // restating rather than just citing: a sticky canvas gets no\n // mousemove and no mouseleave for any of them. Not imported because\n // it lives in the LGV plugin and reads a containing LGV, and this\n // plugin deliberately takes no runtime dependency on that plugin\n // (see hoverSync/index.tsx).\n //\n // A `reaction`, not an autorun, for the reason stated there: the\n // effect touches hover state, and an autorun would re-enter itself.\n // Selection is untouched \u2014 a click is a choice, and the content\n // moving does not unmake it.\n addDisposer(\n self,\n reaction(\n () => `${self.scale}-${self.translateX}-${self.translateY}`,\n () => {\n self.setHoveredNode(null)\n self.setHoveredEdge(null)\n },\n { name: 'GraphClearHoverOnViewportChange' },\n ),\n )\n\n // Autorun: a zoom, or a pan that leaves the window the last build\n // covered, schedules a debounced rebuild. A pan inside it costs a\n // repaint and nothing else. Skips the first run.\n let firstViewport = true\n addDisposer(\n self,\n autorun(() => {\n const { scale } = self\n const viewport = viewportOf(self)\n if (firstViewport) {\n firstViewport = false\n return\n }\n const built = untracked(() => self.builtViewport)\n if (built?.scale === scale && contains(built.bounds, viewport)) {\n return\n }\n self.scheduleViewportDirty()\n }),\n )\n\n // Autorun: hover/select are draw-time colour overrides, stated whole\n // on every change, so there is nothing to restore and nothing to go\n // stale when a rebuild renumbers the batch. Tracks `geometryVersion`\n // because an upload drops the renderer's edge highlight.\n addDisposer(\n self,\n autorun(() => {\n const b = self.currentRenderingBackend as Renderer | undefined\n const hoveredNode = self.hoveredNode\n const hoveredEdge = self.hoveredEdge\n const selectedNode = self.selectedNode\n dependOn(self.geometryVersion)\n if (b) {\n const nodes = new Map<string, number>()\n if (selectedNode !== null) {\n nodes.set(selectedNode, SELECT_BRIGHTEN)\n }\n if (hoveredNode !== null && hoveredNode !== selectedNode) {\n nodes.set(hoveredNode, HOVER_BRIGHTEN)\n }\n b.setNodeHighlights(nodes)\n b.setEdgeHighlight(hoveredEdge, HOVER_BRIGHTEN)\n self.renderNow()\n }\n }),\n )\n }\n\n // The second argument is a SETUP THUNK, not the callbacks themselves:\n // anything it closes over lives exactly as long as the callbacks that\n // read it, which is what lets a per-backend memo work.\n self.attachRenderingBackend<Renderer>(backend, () => ({\n // Autorun: rebuild geometry when graph data or display options\n // change. scale/translate are untracked so they don't trigger a full\n // rebuild \u2014 only the debounced viewportDirty flag does.\n upload: (b: Renderer) => {\n b.resize(self.width, self.canvasHeight)\n const nodeById = self.nodeById\n if (self.nodePositions && self.graph && nodeById) {\n // The window moved (debounced pan/zoom), or the positions did (a\n // node drag, coalesced to a frame). Both change the drawing;\n // everything below reads them untracked or not at all.\n dependOn(self.viewportDirty, self.positionsVersion)\n const geometryStart = performance.now()\n const viewportBounds = untracked(() =>\n padded(viewportOf(self), VIEWPORT_PANES_BUILT),\n )\n const batch = buildGeometry({\n nodePositions: self.nodePositions,\n graph: self.graph,\n nodeById,\n colorScheme: self.effectiveColorScheme,\n contigThickness: self.contigThickness,\n connectorThickness: self.connectorThickness,\n drawPaths: self.effectiveDrawPaths,\n nodeWidth: self.nodeWidth,\n highlight: self.walkHighlight,\n // Untracked, so a zoom does not eagerly rebuild geometry \u2014 the\n // debounced viewportDirty bump drives the scale-dependent\n // rebuild (flatness, arrow visibility, viewport culling), same\n // as pan.\n axis: untracked(() => self.axisScale),\n linearLayout: self.linearLayout,\n viewportBounds,\n // Where each node sits on the reference and what interval the\n // hue spans. Held against the graph rather than derived here,\n // the same way `deletions` is and for the same reason \u2014 see\n // the `referenceRamp` view.\n referenceRamp: self.referenceRamp,\n deletions: self.deletionEdgeIndexes,\n hiddenEdges: self.hiddenEdgeIndexes,\n // Read tracked above; passed here so the shared edge-curve\n // cache can tell a drag from a pan.\n version: self.positionsVersion,\n })\n b.uploadGeometry(batch)\n self.setGeometryMetrics(\n performance.now() - geometryStart,\n batch.nodeStrokes.length,\n { scale: untracked(() => self.scale), bounds: viewportBounds },\n )\n return true\n }\n // Nothing reached the backend, so the autorun may skip the redraw\n // it would otherwise force. Safe despite the resize above: with no\n // positions there is nothing to draw, which is the same case\n // `render` below returns false for.\n return false\n },\n // Autorun: re-render on pan/zoom/darkMode without rebuilding geometry\n render: (b: Renderer) => {\n if (!self.nodePositions) {\n return false\n }\n // getDpr(), never a bare `devicePixelRatio`: it is capped at\n // MAX_DPR so this canvas costs what every other canvas in the app\n // costs on a 3x display (the square of the ratio, i.e. 9x the\n // pixels of 1x against the 4x everything else pays), and it is the\n // same read `syncCanvasSize` sizes the backing store with \u2014 two\n // call sites reading the global separately can disagree, and then\n // the geometry lands at a different scale from the canvas under it.\n const dpr = getDpr()\n b.updateTransform({\n scaleX: self.scaleX * dpr,\n scaleY: self.scaleY * dpr,\n translateX: self.translateX * dpr,\n translateY: self.translateY * dpr,\n // Handed over rather than read again by the backend: the\n // thicknesses in the vertex buffer are css px and are expanded\n // after this transform, so they need the same ratio the fields\n // above were already multiplied by. See TransformUniform.dpr.\n dpr,\n })\n b.render(self.darkMode ? [0.12, 0.12, 0.12, 1] : [1, 1, 1, 1])\n return true\n },\n }))\n },\n }))\n .actions(self => ({\n // A layout picked from a menu: the mode, then the drawing it makes.\n switchLayout(mode: LayoutModeValue) {\n self.setLayoutMode(mode)\n return self.recomputeLayout()\n },\n retryLoad() {\n if (self.loadedTrackId && self.loadedRegion) {\n void self.reloadSubgraph()\n } else if (self.gfaLocation) {\n void self.loadGFAFromLocation(self.gfaLocation)\n }\n },\n // A declaratively-instantiated view loads itself on attach, from either\n // declarative source: a whole-GFA `gfaLocation`, or the\n // `loadedTrackId`/`loadedRegion` pair the launch menu writes and a\n // reloaded session restores.\n //\n // This has to happen here rather than when the rendering backend starts:\n // the canvas only mounts once `hasGraph` is true, so a view whose graph\n // must be fetched would never fetch it.\n afterAttach() {\n // A restored session that already carries a non-default transform is\n // the user's own view \u2014 mark it so the fit autorun leaves it alone.\n if (!self.isDefaultViewport) {\n self.viewportOwner = 'user'\n }\n // loadGFAFromLocation leaves `gfaLocation` intact, so the source\n // round-trips through a session snapshot.\n if (self.gfaLocation && !self.graph) {\n void self.loadGFAFromLocation(self.gfaLocation)\n }\n void self.refetchIfNeeded()\n },\n }))\n .actions(self => ({\n // Move the linear view already on screen, or open one if there is none,\n // and pair with whichever it was. A view being created gets the session's\n // annotation for the assembly it opens on (see launchTracks), led by the\n // graph's own segments track when the launch is on the reference \u2014 the one\n // assembly that track is configured for.\n //\n // Only a view on the reference is paired with. A node's hover band is on\n // that assembly, so paired with a view on another the band fails that\n // view's assembly check and the reference view's id check, and is drawn\n // nowhere.\n showInLinearView(target: { location: GraphLocation; assembly: string }) {\n const session = getSession(self)\n const onReference = target.assembly === self.loadedRegion?.assemblyName\n const viewId = showInLinearView({\n session,\n location: target.location,\n assembly: target.assembly,\n connectedViewId: self.connectedViewId,\n tracks: launchTracks({\n session,\n assemblyName: target.assembly,\n first:\n self.loadedTrackId && onReference\n ? self.loadedTrackId\n : undefined,\n }),\n })\n if (onReference) {\n self.pairWithLinearView(viewId)\n }\n },\n // Mark the node's reference interval in the linear view beside the graph.\n // Not an action that opens anything: with no view to mark, the menu does\n // not offer it (see nodeLaunchMenuItems).\n highlightInLinearView(target: {\n location: GraphLocation\n assembly: string\n }) {\n highlightInLinearView({\n session: getSession(self),\n location: target.location,\n assembly: target.assembly,\n connectedViewId: self.connectedViewId,\n })\n },\n showSyntenyView(trackId: string) {\n launchSyntenyView({\n session: getSession(self),\n contributors: self.launchableAssemblies,\n trackId,\n graphTrackId: self.loadedTrackId || undefined,\n })\n },\n }))\n .views(self => ({\n // Synteny datasets that could fill the panels of a multi-genome launch.\n // Below two openable contributors there is nothing to compare, so the scan\n // is skipped rather than run and discarded.\n get syntenyLaunchTracks() {\n const samples = self.launchableAssemblies.map(c => c.sample)\n return samples.length >= 2\n ? launchableSyntenyTracks(getSession(self), samples)\n : []\n },\n }))\n .views(self => ({\n // The graph's way out, in the same shared \"Launch view\" submenu every\n // other view contributes to. Until this existed the triangle had two edges:\n // a linear view could open a graph or a synteny view of a locus, and the\n // graph could open nothing at all.\n menuItems(): MenuItem[] {\n const items: MenuItem[] = []\n for (const item of graphLaunchMenuItems({\n contributors: self.launchableAssemblies,\n syntenyTracks: self.syntenyLaunchTracks,\n onShowLinear: target => {\n self.showInLinearView(target)\n },\n onShowSynteny: trackId => {\n self.showSyntenyView(trackId)\n },\n })) {\n pushLaunchViewMenuItem(items, item)\n }\n return items\n },\n }))\n}\n\nexport type GraphGenomeViewModel = ReturnType<\n ReturnType<typeof stateModelFactory>['create']\n>\n", "import { bubbleSegmentIds, classifyBubble, formatBp } from './classifyBubble'\nimport { isBackbone } from '../anchoredNodes'\nimport { svgPath } from '../util/geometry'\n\nimport type { BubbleKind } from './classifyBubble'\nimport type {\n BubbleRoute,\n MinigraphBubble,\n} from '../../MinigraphBubbleAdapter/bubbleLine'\nimport type { Graph, NodeSegment } from '../types'\n\n// A bubble drawn over the graph itself: a wide translucent stroke along the\n// nodes inside it, in layout coordinates, so the same halo follows the nodes\n// through the force layout's loops as through the ordered layout's lenses. The\n// two backbone nodes a bubble hangs between are not inside it, and are left\n// out so neighbouring halos do not touch.\nexport interface BubbleHalo {\n bubble: MinigraphBubble\n kind: BubbleKind\n label: string\n // an SVG path in layout units; a drawing transform puts it on screen\n path: string\n // where the label goes: the highest point of the halo, in layout units\n top: NodeSegment\n members: number\n // the ids of those nodes, so a lifted walk can say which bubbles it enters\n nodeIds: string[]\n // the bubble is the whole drawing, as a popped bubble's own row is: its\n // label still names it, but a halo around everything marks nothing\n whole: boolean\n // each route the walks take through the bubble, the reference's own\n // included, named for the haplotypes that take it; a route with no steps, a\n // deletion, has nowhere to carry a chip\n routes: RouteLabel[]\n}\n\nexport interface RouteLabel {\n route: BubbleRoute\n at: NodeSegment\n text: string\n}\n\nconst WHOLE_FRACTION = 0.9\nconst NAMED_WALKS = 2\n// a SNP's routes are a dot each; chips are for loops a reader can see\nconst MIN_CHIPPED_BP = 1000\n\nexport function bubbleHalos(\n graph: Graph,\n bubbles: MinigraphBubble[],\n positions: Record<string, NodeSegment[]>,\n walkLabel: (name: string) => string = name => name,\n): BubbleHalo[] {\n const byName = new Map(graph.nodes.map(n => [n.name, n]))\n const halos: BubbleHalo[] = []\n for (const bubble of bubbles) {\n const parts: string[] = []\n let top: NodeSegment | undefined\n const nodeIds: string[] = []\n const ends: NodeSegment[] = []\n for (const name of bubbleSegmentIds(bubble)) {\n const node = byName.get(name)\n const line = node && positions[node.id]\n if (!node || !line?.length) {\n continue\n }\n if (\n isBackbone(node) &&\n (node.stable.start < bubble.start || node.stable.start >= bubble.end)\n ) {\n ends.push(line[Math.floor(line.length / 2)]!)\n continue\n }\n nodeIds.push(node.id)\n // a one-point line still needs a segment to stroke as a dot\n parts.push(svgPath(line.length === 1 ? [line[0]!, line[0]!] : line))\n for (const p of line) {\n if (!top || p.y < top.y) {\n top = p\n }\n }\n }\n if (!top) {\n continue\n }\n const anchor = ends.length\n ? {\n x: ends.reduce((s, p) => s + p.x, 0) / ends.length,\n y: ends.reduce((s, p) => s + p.y, 0) / ends.length,\n }\n : top\n // A chip goes on the stretch that is the route's own: among the nodes the\n // fewest other routes share, the point farthest from the bubble's ends.\n // Routes through a repeat array share most of their copies, and the far\n // point of a shared copy would put every chip on one loop.\n const sharing = new Map<string, number>()\n for (const route of bubble.routes ?? []) {\n for (const id of new Set(route.steps)) {\n sharing.set(id, (sharing.get(id) ?? 0) + 1)\n }\n }\n const routes: RouteLabel[] = []\n for (const route of bubble.routes ?? []) {\n if (bubble.longestAlleleLength < MIN_CHIPPED_BP) {\n break\n }\n if (route.steps.length === 0) {\n continue\n }\n const rarest = Math.min(...route.steps.map(id => sharing.get(id)!))\n let at: NodeSegment | undefined\n let far = -1\n for (const id of route.steps) {\n if (sharing.get(id) !== rarest) {\n continue\n }\n for (const p of positions[id] ?? []) {\n const d = Math.hypot(p.x - anchor.x, p.y - anchor.y)\n if (d > far) {\n far = d\n at = p\n }\n }\n }\n if (at) {\n routes.push({ route, at, text: routeText(route, walkLabel) })\n }\n }\n halos.push({\n bubble,\n ...classifyBubble(bubble),\n path: parts.join(''),\n top,\n members: nodeIds.length,\n nodeIds,\n whole: nodeIds.length >= WHOLE_FRACTION * graph.nodes.length,\n routes,\n })\n }\n return halos\n}\n\nfunction routeText(route: BubbleRoute, walkLabel: (name: string) => string) {\n const names = [...new Set(route.walks.map(walkLabel))].sort()\n const shown = names.slice(0, NAMED_WALKS).join(', ')\n const more =\n names.length > NAMED_WALKS ? ` +${names.length - NAMED_WALKS}` : ''\n return `${shown}${more} \u00B7 ${formatBp(route.bp)}`\n}\n", "import { SATURATED_PATH_COUNT } from '../../MinigraphBubbleAdapter/bubbleLine'\nimport { isBackbone } from '../anchoredNodes'\nimport { referenceOrder } from '../layout/orderedLayout'\n\nimport type {\n BubbleRoute,\n MinigraphBubble,\n} from '../../MinigraphBubbleAdapter/bubbleLine'\nimport type { Graph, GraphNode } from '../types'\n\n// Bubbles from the graph alone, off the reference order the ordered layout is\n// also drawn from (referenceOrder): a backbone node no link reaches across is a\n// bubble boundary, and whatever sorts between two consecutive boundaries is a\n// bubble. Needs a reference, not an index, so it works on a GBZ cut, a pggb\n// file and the inside of a popped superbubble alike.\n//\n// Read off the order itself, not off the layers the drawing puts it in. A\n// layer is where a node is drawn, and one nothing leads into is drawn in the\n// first layer wherever it sorts. That is every node a walk enters the cut at,\n// the ordinary shape of a GBZ cut whose context stops short of a haplotype's\n// walk: its link into the backbone then crossed every layer between, and the\n// bubbles on either side of those boundaries fused into one.\n//\n// Route statistics come from the walks when the graph has paths (exact, and\n// immune to a repeat array whose copies the order cannot direct), and from a\n// DP over the ordered DAG otherwise. A reversed stretch comes out as one\n// bubble whose interior runs against the reference, not as an inversion flag.\n\ninterface Routes {\n min: number\n max: number\n n: number\n}\n\nexport function bubblesFromGraph(graph: Graph): MinigraphBubble[] {\n if (!graph.nodes.some(isBackbone)) {\n return []\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const order = referenceOrder(graph)\n const indexOf = new Map(order.map((id, i) => [id, i]))\n // how many links reach across each place in the order, as a running sum of\n // where each one starts and stops\n const reach = new Int32Array(order.length + 1)\n // successors as a set: a repeat's back edge is the same DAG edge as its\n // forward one, and counting it twice doubles every route through the copy\n const succ = new Map<string, Set<string>>()\n const linked = new Set<string>()\n for (const e of graph.edges) {\n const a = indexOf.get(e.from)\n const b = indexOf.get(e.to)\n if (a === undefined || b === undefined || a === b) {\n continue\n }\n linked.add(`${e.from}>${e.to}`).add(`${e.to}>${e.from}`)\n const [lo, hi] = a < b ? [a, b] : [b, a]\n reach[lo + 1]!++\n reach[hi]!--\n const [from, to] = a < b ? [e.from, e.to] : [e.to, e.from]\n ;(succ.get(from) ?? succ.set(from, new Set()).get(from)!).add(to)\n }\n for (let i = 1; i < reach.length; i++) {\n reach[i]! += reach[i - 1]!\n }\n // Boundaries are nodes: every backbone node no link reaches across, plus the\n // window's first and last backbone node whatever reaches across them, since a\n // repeat array whose copies reach the last backbone node would otherwise\n // never close.\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort((a, b) => a.stable.start - b.stable.start)\n const ends = new Set([backbone[0]!.id, backbone.at(-1)!.id])\n const boundaries = backbone\n .map(node => [node, indexOf.get(node.id)!] as const)\n .filter(([node, i]) => ends.has(node.id) || reach[i] === 0)\n .sort(([a, ia], [b, ib]) => ia - ib || a.stable.start - b.stable.start)\n const walkIndex = graph.paths?.map(p => {\n const at = new Map<string, number>()\n p.nodeIds.forEach((id, i) => {\n if (!at.has(id)) {\n at.set(id, i)\n }\n })\n return at\n })\n\n const bubbles: MinigraphBubble[] = []\n for (let i = 0; i + 1 < boundaries.length; i++) {\n const [start, i0] = boundaries[i]!\n const [end, i1] = boundaries[i + 1]!\n const interior = order.slice(i0 + 1, i1)\n if (interior.length === 0) {\n continue\n }\n\n const best = new Map<string, Routes>([[start.id, { min: 0, max: 0, n: 1 }]])\n for (const id of [start.id, ...interior]) {\n const cur = best.get(id)\n if (!cur) {\n continue\n }\n for (const t of succ.get(id) ?? []) {\n const ti = indexOf.get(t)!\n if (ti > i1) {\n continue\n }\n const add = t === end.id ? 0 : byId.get(t)!.length\n const prev = best.get(t)\n best.set(t, {\n min: Math.min(prev?.min ?? Infinity, cur.min + add),\n max: Math.max(prev?.max ?? -Infinity, cur.max + add),\n n: Math.min(SATURATED_PATH_COUNT, cur.n + (prev?.n ?? 0)),\n })\n }\n }\n const walked =\n walkIndex && walkRoutes(graph, byId, walkIndex, start.id, end.id)\n const crossed = walked?.n ? walked : undefined\n const routes = crossed ?? best.get(end.id) ?? { min: 0, max: 0, n: 0 }\n // A walk that enters the bubble and never reaches its other end left the\n // cut: a GBZ cut of a repeat array at 1 kb of context splits each\n // haplotype's walk into pieces, and the routes seen are then a floor.\n const walksLeave = walked !== undefined && walked.left > 0\n\n const refStart = start.stable.start + start.length\n const refEnd = end.stable.start\n // The reference route exists in the cut only if every backbone pair inside\n // is linked; a flank the hop reached from an allele is not.\n const chain = [\n start.id,\n ...interior.filter(id => isBackbone(byId.get(id)!)),\n end.id,\n ]\n const chainBroken = chain.some(\n (id, k) => k > 0 && !linked.has(`${chain[k - 1]}>${id}`),\n )\n const partial = chainBroken || walksLeave\n const fallback = chainBroken && !crossed\n bubbles.push({\n refName: start.stable.refName,\n start: refStart,\n end: refEnd,\n segmentCount: interior.length + 2,\n pathCount: routes.n,\n // a real inversion test needs each walk's direction relative to the\n // reference; the first visit's strand is just whichever path anchored the\n // node\n inversion: false,\n shortestAlleleLength: fallback ? refEnd - refStart : routes.min,\n longestAlleleLength: fallback ? refEnd - refStart : routes.max,\n segments: [start, ...interior.map(id => byId.get(id)!), end]\n .map(n => n.name)\n .join(','),\n shortestAllele: undefined,\n longestAllele: undefined,\n partial,\n routes: crossed?.routes,\n })\n }\n return bubbles\n}\n\n// For every walk that passes both boundary nodes, the bp between them and the\n// step sequence, so routes are distinct sequences and lengths are the true\n// haplotype lengths. `left` counts the walks that pass one boundary and end\n// before the other.\n//\n// `byId` is the caller's: this runs once per bubble, and a map of every node\n// built here made the whole pass quadratic, seven seconds at 15k nodes.\nfunction walkRoutes(\n graph: Graph,\n byId: Map<string, GraphNode>,\n walkIndex: Map<string, number>[],\n startId: string,\n endId: string,\n): (Routes & { left: number; routes: BubbleRoute[] }) | undefined {\n const seen = new Map<string, BubbleRoute>()\n let min = Infinity\n let max = -Infinity\n let left = 0\n graph.paths!.forEach((p, k) => {\n const i0 = walkIndex[k]!.get(startId)\n const i1 = walkIndex[k]!.get(endId)\n if (i0 === undefined || i1 === undefined) {\n if (i0 !== undefined || i1 !== undefined) {\n left++\n }\n return\n }\n // Start to end, whichever way the walk crosses: a contig on the reverse\n // strand takes the same route, and read end-first it keyed as a second one.\n const steps = p.nodeIds.slice(Math.min(i0, i1) + 1, Math.max(i0, i1))\n if (i1 < i0) {\n steps.reverse()\n }\n let bp = 0\n for (const id of steps) {\n bp += byId.get(id)?.length ?? 0\n }\n const key = steps.join(',')\n const route = seen.get(key) ?? { steps, bp, walks: [] }\n route.walks.push(p.name)\n seen.set(key, route)\n min = Math.min(min, bp)\n max = Math.max(max, bp)\n })\n return seen.size || left\n ? { min, max, n: seen.size, left, routes: [...seen.values()] }\n : undefined\n}\n", "import type { Graph } from '../types'\n\n// The graph inside one bubble: the segments the bubble row names, with the\n// links among them. The row's list includes the two backbone segments the\n// bubble hangs between, so the cut keeps its anchors and every layout that\n// needs a backbone still has one. A walk keeps the steps it takes inside the\n// bubble, so the bubbles derived inside it still read their routes and\n// lengths off the haplotypes, and carriage still sets node width.\nexport function bubbleSubgraph(graph: Graph, segmentIds: string[]): Graph {\n const keep = new Set(segmentIds)\n const nodes = graph.nodes.filter(n => keep.has(n.name))\n const ids = new Set(nodes.map(n => n.id))\n const paths = graph.paths\n ?.map(p => ({ ...p, nodeIds: p.nodeIds.filter(id => ids.has(id)) }))\n .filter(p => p.nodeIds.length > 0)\n return {\n name: graph.name,\n nodes,\n edges: graph.edges.filter(e => ids.has(e.from) && ids.has(e.to)),\n ...(paths?.length ? { paths } : {}),\n anchorPaths: graph.anchorPaths,\n pathVisits: graph.pathVisits,\n anchoredBy: graph.anchoredBy,\n referencePath: graph.referencePath,\n }\n}\n", "import type { Feature } from '@jbrowse/core/util'\n\n// A gene as the pins need it: its name, its span on the reference, and the\n// exons of all its transcripts merged into one set of intervals.\nexport interface GeneModel {\n name: string\n refName: string\n start: number\n end: number\n strand: number\n exons: { start: number; end: number }[]\n}\n\n// The adapters whose tracks hold gene models, in the order the session's\n// tracks are searched when no track is named.\nexport const GENE_ADAPTER_TYPES = new Set([\n 'Gff3TabixAdapter',\n 'Gff3Adapter',\n 'GtfTabixAdapter',\n 'GtfAdapter',\n 'BigBedAdapter',\n 'NCListAdapter',\n])\n\nconst GENE_TRACK_HINT = /gene|refseq|gencode|ensembl|annotation/i\n\n// Which of the session's tracks to read genes from: the named one, else the\n// first gene-bearing track on the assembly whose name says it is annotation,\n// else the first gene-bearing track at all.\nexport function pickGeneTrack<\n T extends { trackId: string; name?: string; adapterType: string },\n>(tracks: T[], named: string) {\n if (named) {\n return tracks.find(t => t.trackId === named)\n }\n const candidates = tracks.filter(t => GENE_ADAPTER_TYPES.has(t.adapterType))\n return (\n candidates.find(t =>\n GENE_TRACK_HINT.test(`${t.trackId} ${t.name ?? ''}`),\n ) ?? candidates[0]\n )\n}\n\ntype FeatureLike = Feature | Record<string, unknown>\n\nfunction field(f: FeatureLike, name: string): unknown {\n return typeof (f as Feature).get === 'function'\n ? (f as Feature).get(name)\n : (f as Record<string, unknown>)[name]\n}\n\nfunction exonsOf(f: FeatureLike, into: { start: number; end: number }[]) {\n const type = field(f, 'type')\n if (type === 'exon') {\n into.push({\n start: field(f, 'start') as number,\n end: field(f, 'end') as number,\n })\n }\n const children = field(f, 'subfeatures') as FeatureLike[] | undefined\n for (const child of children ?? []) {\n exonsOf(child, into)\n }\n}\n\nfunction merged(intervals: { start: number; end: number }[]) {\n const sorted = [...intervals].sort((a, b) => a.start - b.start)\n const out: { start: number; end: number }[] = []\n for (const iv of sorted) {\n const last = out.at(-1)\n if (last && iv.start <= last.end) {\n last.end = Math.max(last.end, iv.end)\n } else {\n out.push({ ...iv })\n }\n }\n return out\n}\n\n// Genes from a track's features: every top-level feature, named by the first\n// of gene_name, name and id it carries, with the exons found anywhere under it\n// merged. A feature with no exons is one exon, its whole span.\nexport function geneModelsFrom(features: FeatureLike[]): GeneModel[] {\n const genes: GeneModel[] = []\n for (const f of features) {\n const name =\n (field(f, 'gene_name') as string | undefined) ??\n (field(f, 'name') as string | undefined) ??\n (field(f, 'id') as string | undefined)\n const start = field(f, 'start') as number\n const end = field(f, 'end') as number\n if (!name || !(end > start)) {\n continue\n }\n const exons: { start: number; end: number }[] = []\n exonsOf(f, exons)\n genes.push({\n name,\n refName: field(f, 'refName') as string,\n start,\n end,\n strand: (field(f, 'strand') as number | undefined) ?? 0,\n exons: exons.length ? merged(exons) : [{ start, end }],\n })\n }\n return genes\n}\n", "import { firstNodeAtOrAfter, isBackbone } from '../anchoredNodes'\nimport { polylineSlice } from '../layout/mergeRuns'\nimport { svgPath } from '../util/geometry'\n\nimport type { GeneModel } from './geneFeatures'\nimport type { AnchoredNode } from '../anchoredNodes'\nimport type { Graph, NodeSegment } from '../types'\n\n// A gene drawn onto the graph: its exons as stretches of the backbone nodes\n// that carry them, in layout units, and one point on the backbone to pin its\n// name to. Only the backbone has coordinates, so an allele shows no exon even\n// where a haplotype's own annotation would put one.\nexport interface GenePin {\n gene: GeneModel\n // an SVG path of the exon stretches, in layout units\n exons: string\n // where the name goes: the backbone point at the gene's midpoint, or the\n // nearest backbone point inside the gene where the midpoint is not in the cut\n at: NodeSegment\n // how much of the gene's span the cut's backbone covers, so a label can say\n // when a gene runs off the cut\n covered: number\n}\n\n// A backbone node names its sequence the way the graph does, `GRCh38#0#chr6`,\n// and a gene the way the assembly does, `chr6`; the contig is the part they\n// share.\nfunction contig(name: string) {\n return name.split('#').at(-1)!\n}\n\n// The backbone of one contig in offset order, for finding the nodes a gene\n// lies over without reading the rest. `reach` is the longest node: a node over\n// a gene's start begins no further before it than that.\ninterface ContigBackbone {\n nodes: AnchoredNode[]\n reach: number\n}\n\nexport function genePins(\n graph: Graph,\n genes: GeneModel[],\n positions: Record<string, NodeSegment[]>,\n): GenePin[] {\n // Per contig and sorted once, so a gene reads the few nodes it lies over.\n // Every gene used to read every backbone node, splitting both names each\n // time, on each frame of a node drag: 199 ms for 100 genes over 15k nodes,\n // now 12.\n const byContig = new Map<string, ContigBackbone>()\n const contigOf = new Map<string, string>()\n for (const node of graph.nodes) {\n if (isBackbone(node) && positions[node.id]?.length) {\n const { refName } = node.stable\n const name =\n contigOf.get(refName) ??\n contigOf.set(refName, contig(refName)).get(refName)!\n const entry =\n byContig.get(name) ??\n byContig.set(name, { nodes: [], reach: 0 }).get(name)!\n entry.nodes.push(node)\n entry.reach = Math.max(entry.reach, node.length)\n }\n }\n for (const { nodes } of byContig.values()) {\n nodes.sort((a, b) => a.stable.start - b.stable.start)\n }\n const pins: GenePin[] = []\n for (const gene of genes) {\n const backbone = byContig.get(contig(gene.refName))\n if (!backbone) {\n continue\n }\n const parts: string[] = []\n let at: NodeSegment | undefined\n let atDistance = Infinity\n let covered = 0\n const mid = (gene.start + gene.end) / 2\n const { nodes, reach } = backbone\n for (\n let i = firstNodeAtOrAfter(nodes, gene.start - reach);\n i < nodes.length && nodes[i]!.stable.start < gene.end;\n i++\n ) {\n const node = nodes[i]!\n const nodeStart = node.stable.start\n const nodeEnd = nodeStart + node.length\n if (nodeEnd <= gene.start) {\n continue\n }\n const line = positions[node.id]!\n covered += Math.min(nodeEnd, gene.end) - Math.max(nodeStart, gene.start)\n for (const exon of gene.exons) {\n const a = Math.max(exon.start, nodeStart)\n const b = Math.min(exon.end, nodeEnd)\n if (b <= a) {\n continue\n }\n const stretch = polylineSlice(\n line,\n (a - nodeStart) / node.length,\n (b - nodeStart) / node.length,\n )\n if (stretch.length === 1) {\n stretch.push({ ...stretch[0]! })\n }\n parts.push(svgPath(stretch))\n }\n const pinBp = Math.min(Math.max(mid, nodeStart), nodeEnd)\n const distance = Math.abs(pinBp - mid)\n if (distance < atDistance) {\n atDistance = distance\n const [p] = polylineSlice(\n line,\n (pinBp - nodeStart) / node.length,\n (pinBp - nodeStart) / node.length,\n )\n at = p\n }\n }\n if (at) {\n pins.push({\n gene,\n exons: parts.join(''),\n at,\n covered: covered / (gene.end - gene.start),\n })\n }\n }\n return pins\n}\n", "// Minimal GFA (Graphical Fragment Assembly) text parser shared by the graph\n// pangenome plugins (GraphGenomeView, TubeMapView). Handles GFA1/GFA2 segment,\n// link, edge, path and walk records plus header tags.\n\n// Only the first two colons delimit a tag; everything after them is the value.\n// A Z value may contain colons of its own, and rGFA stable names routinely do\n// (`SN:Z:chr1:1-1000` from a sliced reference), where splitting on every colon\n// silently truncates the name to `chr1` and mis-anchors the whole layout.\nfunction parseTag(tag: string, tags: Record<string, string | number>) {\n const nameEnd = tag.indexOf(':')\n const typeEnd = tag.indexOf(':', nameEnd + 1)\n if (nameEnd !== -1 && typeEnd !== -1) {\n const name = tag.slice(0, nameEnd)\n const type = tag.slice(nameEnd + 1, typeEnd)\n const val = tag.slice(typeEnd + 1)\n if (type === 'i' || type === 'f') {\n tags[name] = +val\n } else if (type === 'Z') {\n tags[name] = val\n }\n }\n}\n\nfunction parseTags(fields: string[]) {\n const tags: Record<string, string | number> = {}\n for (const field of fields) {\n parseTag(field, tags)\n }\n return tags\n}\n\nexport interface GFANode {\n id: string\n length: number\n sequence: string\n tags: Record<string, string | number>\n}\n\n// rGFA (minigraph) anchors every segment to a reference coordinate: SN is the\n// stable sequence name, SO the offset on it, and SR the stable rank, where 0 is\n// the reference backbone and higher ranks are the sequence that diverges from\n// it. Plain GFA (pggb, odgi, Minigraph-Cactus) carries none of these, which is\n// why coordinates there exist only inside the P/W lines.\nexport interface StableCoordinate {\n refName: string\n start: number\n rank: number\n // How the anchoring path reads this segment. Only path-derived coordinates\n // set it: an rGFA segment is by construction forward on the sequence SN\n // names, so there is nothing there for it to say.\n strand?: '+' | '-'\n}\n\nexport function stableCoordinate(node: GFANode): StableCoordinate | undefined {\n const { SN, SO, SR } = node.tags\n return typeof SN === 'string' &&\n typeof SO === 'number' &&\n typeof SR === 'number'\n ? { refName: SN, start: SO, rank: SR }\n : undefined\n}\n\nexport interface GFALink {\n source: string\n target: string\n strand1?: string\n strand2?: string\n cigar: string\n tags: Record<string, string | number>\n}\n\nexport interface GFAPath {\n name: string\n path: string\n rest: string[]\n}\n\nexport interface GFAWalkSegment {\n id: string\n strand: string\n}\n\nexport interface GFAWalk {\n sample: string\n haplotype: number\n contig: string\n start: number\n end: number\n segments: GFAWalkSegment[]\n tags: Record<string, string | number>\n}\n\nconst GT = 62 // '>'\nconst LT = 60 // '<'\n\n// A W record's body is `>s1<s2>s3\u2026`, and on a Minigraph-Cactus graph one record\n// is the whole walk of one haplotype across a chromosome \u2014 hundreds of\n// thousands of steps in a single field.\n//\n// So the ids are cut out by index rather than accumulated a character at a\n// time. `current += ch` builds and discards one string per character of every\n// id, and it dominated the parse: a file whose single W record walks 200k steps\n// went from ~55 ms to ~24 ms by slicing instead. `pnpm bench` keeps the number.\nfunction parseWalkBody(body: string) {\n const segments: GFAWalkSegment[] = []\n let strand = '+'\n let start = 0\n // An empty run \u2014 a leading delimiter, or two in a row \u2014 names no segment.\n function push(end: number) {\n if (end > start) {\n segments.push({ id: body.slice(start, end), strand })\n }\n }\n for (let i = 0; i < body.length; i++) {\n const ch = body.charCodeAt(i)\n if (ch === GT || ch === LT) {\n push(i)\n strand = ch === GT ? '+' : '-'\n start = i + 1\n }\n }\n push(body.length)\n return segments\n}\n\nexport interface GFAGraph {\n nodes: GFANode[]\n links: GFALink[]\n paths: GFAPath[]\n walks: GFAWalk[]\n header: Record<string, string | number>[]\n id: string\n}\n\nexport function parseGFA(file: string) {\n const graph: GFAGraph = {\n nodes: [],\n links: [],\n paths: [],\n walks: [],\n header: [],\n id: '',\n }\n\n // \\r is stripped rather than split on, so a CRLF file does not leave it on\n // whichever field happens to be last: a trailing Z tag would carry it into a\n // stable name and a trailing cigar into the CIGAR string.\n //\n // Stripped per line inside the loop rather than with a .map() over the split,\n // which would hold a second copy of every line in the file at once.\n for (const rawLine of file.split('\\n')) {\n const line = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine\n if (line.startsWith('H')) {\n const [, ...rest] = line.split('\\t')\n graph.header.push(parseTags(rest))\n } else if (line.startsWith('S')) {\n const [, name, ...rest] = line.split('\\t')\n let len: number\n let seq: string\n let tagfields: string[]\n let gfa1 = false\n // A truncated record has no field to read; `*` is what the spec writes for\n // a segment whose sequence is absent, so treating a missing one the same\n // way yields a 0 bp segment instead of throwing partway through the file.\n const first = rest[0] ?? '*'\n // GFA2 puts an integer length in this field, GFA1 the sequence. Testing\n // the digits rather than the coerced number keeps a GFA2 `S sid 0 *` from\n // being read as a GFA1 sequence of \"0\".\n if (/^\\d+$/.test(first)) {\n len = +first\n seq = rest[1] ?? '*'\n tagfields = rest.slice(2)\n } else {\n gfa1 = true\n seq = first\n // `*` is GFA1's \"no sequence here\"; its length is whatever LN says\n // below, and 0 if the file states none. Counting the placeholder's own\n // character instead reports every such segment as 1 bp.\n len = seq === '*' ? 0 : seq.length\n tagfields = rest.slice(1)\n }\n const tags = parseTags(tagfields)\n if (gfa1 && 'LN' in tags) {\n len = +tags.LN\n }\n graph.nodes.push({ id: name!, length: len, sequence: seq, tags })\n } else if (line.startsWith('E')) {\n // From here down, a record cut off before the fields that make it a\n // link, walk or path states nothing drawable and is dropped; the file\n // around it still reads. A link missing only its overlap is kept.\n const [, , source, target, , , , , cigar, ...rest] = line.split('\\t')\n if (source && target) {\n graph.links.push({\n source: source.slice(0, -1),\n target: target.slice(0, -1),\n strand1: source.at(-1),\n strand2: target.at(-1),\n cigar: cigar ?? '*',\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('L')) {\n const [, source, strand1, target, strand2, cigar, ...rest] =\n line.split('\\t')\n if (source && target && strand2) {\n graph.links.push({\n source,\n target,\n strand1,\n strand2,\n cigar: cigar ?? '*',\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('W')) {\n const [, sample, hap, contig, start, end, body, ...rest] =\n line.split('\\t')\n if (sample && hap && contig && start && end && body) {\n graph.walks.push({\n sample,\n haplotype: +hap,\n contig,\n start: start === '*' ? -1 : +start,\n end: end === '*' ? -1 : +end,\n segments: parseWalkBody(body),\n tags: parseTags(rest),\n })\n }\n } else if (line.startsWith('P')) {\n const [, name, path, ...rest] = line.split('\\t')\n if (name && path) {\n graph.paths.push({ name, path, rest })\n }\n }\n }\n return graph\n}\n", "import { stableCoordinate } from '../../gfa-core/index'\nimport { pathOrigin, surveyPaths } from '../pathAnchoring'\n\nimport type { GFAGraph, GFANode } from '../../gfa-core/index'\nimport type { PathSteps } from '../pathAnchoring'\nimport type { Graph, GraphEdge, GraphNode, GraphPath } from '../types'\n\n// Single pass over every reference a GFA makes to a segment, collecting the two\n// things the drawn graph needs from them.\n//\n// `canonical` is the one orientation each segment is drawn in, which is what\n// Bandage calls single-node mode. A segment is the same sequence read either\n// way (`L 1 + 2 +` is the same edge as `L 2 - 1 -`), so `1+` and `1-` are one\n// node, not two: drawing both doubles every segment, and since the L lines only\n// wire one of the pair, the other copy has no edges at all and scatters loose\n// across the layout. Links carry the topology, so their orientation wins; a\n// segment only paths mention takes the orientation they walk it in.\n//\n// `traversals` is how many paths cross each segment. Assembly GFAs carry read\n// depth in a dp/RC/FC/KC tag, but a pangenome GFA (odgi, vg) carries none \u2014\n// there the meaningful depth is how many samples go through the segment, i.e.\n// core versus accessory sequence.\nfunction surveySegments(gfaGraph: GFAGraph) {\n const canonical = new Map<string, '+' | '-'>()\n const traversals = new Map<string, number>()\n function claim(id: string, strand: '+' | '-') {\n if (!canonical.has(id)) {\n canonical.set(id, strand)\n }\n }\n function traverse(id: string, strand: '+' | '-') {\n claim(id, strand)\n traversals.set(id, (traversals.get(id) ?? 0) + 1)\n }\n for (const link of gfaGraph.links) {\n claim(link.source, link.strand1 === '-' ? '-' : '+')\n claim(link.target, link.strand2 === '-' ? '-' : '+')\n }\n for (const gfaPath of gfaGraph.paths) {\n for (const segment of gfaPath.path.split(',')) {\n traverse(segment.slice(0, -1), segment.endsWith('-') ? '-' : '+')\n }\n }\n for (const walk of gfaGraph.walks) {\n for (const seg of walk.segments) {\n traverse(seg.id, seg.strand === '-' ? '-' : '+')\n }\n }\n return { canonical, traversals }\n}\n\n// P and W state the same thing in different shapes: a P body is a comma-joined\n// `<id><strand>` list whose start offset lives in the record's *name*, a W\n// record arrives already split into steps and states its start as a field.\n// Normalized here so the coordinate walk sees one kind of path.\nfunction anchorablePaths(gfaGraph: GFAGraph): PathSteps[] {\n return [\n ...gfaGraph.paths.map(p => ({\n ...pathOrigin(p.name),\n steps: p.path.split(',').map(segment => ({\n id: segment.slice(0, -1),\n strand: segment.endsWith('-') ? ('-' as const) : ('+' as const),\n })),\n })),\n ...gfaGraph.walks.map(w => ({\n name: `${w.sample}#${w.haplotype}#${w.contig}`,\n // `*` parses to -1, meaning the record declines to say; 0 is then the\n // only offset that can be assumed, and it makes the walk self-relative\n // rather than wrong\n start: w.start === -1 ? 0 : w.start,\n steps: w.segments.map(s => ({\n id: s.id,\n strand: s.strand === '-' ? ('-' as const) : ('+' as const),\n })),\n })),\n ]\n}\n\n// Carriage stated as a tag rather than derived from a walk. `RgfaTabixAdapter`\n// synthesizes its S-lines from a segs.bed whose sixth column is a list of GFA\n// tags, and `scripts/pggb_gfa_to_bed.py` in jbrowse-components puts every\n// haplotype that visits a segment there as `SM:Z:K12.1,Sakai.1`. That is the\n// only way carriage reaches an INDEXED graph: the cut has no P or W lines to\n// walk, so `pathAnchoring` has no visits to derive it from.\n//\n// It does not compete with that walk. `pathAnchoring.anchorNode` rebuilds\n// `samples` from the visits whenever there are any, so a graph loaded as a file\n// keeps the authoritative path-derived set and this is what a tabix cut falls\n// back to.\nfunction carriedSamples(gfaNode: GFANode) {\n const sm = gfaNode.tags.SM\n if (typeof sm !== 'string') {\n return undefined\n }\n const samples = sm\n .split(',')\n .map(s => s.trim())\n .filter(Boolean)\n return samples.length > 0 ? samples : undefined\n}\n\nconst positive = (v: unknown) =>\n typeof v === 'number' && v > 0 ? v : undefined\n\n// The depth a segment states, or undefined. DP is a depth in either case it is\n// written. RC, FC and KC are read, fragment and k-mer counts, so depth is the\n// count over the length: taken raw, a 100 kb node at 5x outweighed a 1 kb node\n// at 50x tenfold in every width drawn from it.\nfunction statedDepth({ tags, length }: GFANode) {\n const count = positive(tags.RC) ?? positive(tags.FC) ?? positive(tags.KC)\n return (\n positive(tags.dp) ??\n positive(tags.DP) ??\n (count !== undefined && length > 0 ? count / length : undefined)\n )\n}\n\nfunction makeNode(\n gfaNode: GFANode,\n strand: '+' | '-',\n depth: number,\n): GraphNode {\n return {\n id: `${gfaNode.id}${strand}`,\n name: gfaNode.id,\n length: gfaNode.length,\n depth,\n stable: stableCoordinate(gfaNode),\n samples: carriedSamples(gfaNode),\n }\n}\n\nexport function convertGFAToGraph(gfaGraph: GFAGraph, name = 'Imported GFA') {\n const nodes: GraphNode[] = []\n const edges: GraphEdge[] = []\n\n const { canonical, traversals } = surveySegments(gfaGraph)\n // segments no link, path, or walk mentions are drawn forward-strand\n const nodeId = (segmentId: string) =>\n `${segmentId}${canonical.get(segmentId) ?? '+'}`\n\n for (const gfaNode of gfaGraph.nodes) {\n const depth =\n statedDepth(gfaNode) ?? Math.max(traversals.get(gfaNode.id) ?? 1, 1)\n nodes.push(makeNode(gfaNode, canonical.get(gfaNode.id) ?? '+', depth))\n }\n\n for (const link of gfaGraph.links) {\n edges.push({\n from: nodeId(link.source),\n to: nodeId(link.target),\n fromStrand: link.strand1 === '-' ? '-' : '+',\n toStrand: link.strand2 === '-' ? '-' : '+',\n })\n }\n\n const paths: GraphPath[] = []\n const edgeToPathsMap = new Map<string, Set<string>>()\n\n // A path that walks the graph on the reverse strand visits an edge's nodes in\n // the opposite order to the L line that declared it, so the key is unordered.\n const edgeKey = (a: string, b: string) => [a, b].sort().join('--')\n\n function recordPathEdges(nodeIds: string[], name: string) {\n for (let i = 0; i < nodeIds.length - 1; i++) {\n const key = edgeKey(nodeIds[i]!, nodeIds[i + 1]!)\n if (!edgeToPathsMap.has(key)) {\n edgeToPathsMap.set(key, new Set())\n }\n edgeToPathsMap.get(key)!.add(name)\n }\n }\n\n for (const gfaPath of gfaGraph.paths) {\n // path segments arrive as `<id><strand>`; the node they land on is the\n // segment's canonical orientation, whichever way this path reads it\n const nodeIds = gfaPath.path\n .split(',')\n .map(segment => nodeId(segment.slice(0, -1)))\n paths.push({ name: gfaPath.name, nodeIds } satisfies GraphPath)\n recordPathEdges(nodeIds, gfaPath.name)\n }\n\n for (const walk of gfaGraph.walks) {\n const nodeIds = walk.segments.map(seg => nodeId(seg.id))\n // The full PanSN name, contig included, which is also what `anchorablePaths`\n // above calls the same record. Dropping the contig made a path's name an\n // ASSEMBLY's name, and one assembly walks one W record per contig: two walks\n // of `HG00438#1` then shared an identity, so `edgeToPathsMap` merged them,\n // `pathColors` (keyed by name) kept only the last colour written while the\n // node stripes kept the first, and the key drew one haplotype twice in two\n // colours. That is the ordinary Minigraph-Cactus shape, not a corner case.\n //\n // The legend narrows it back: `labelTiers` tries sample, then sample and\n // haplotype, then the whole name, so a graph with one contig per haplotype\n // is labelled exactly as before and only a colliding one widens.\n const name = `${walk.sample}#${walk.haplotype}#${walk.contig}`\n paths.push({\n name,\n nodeIds,\n sample: walk.sample,\n haplotype: walk.haplotype,\n contig: walk.contig,\n } satisfies GraphPath)\n recordPathEdges(nodeIds, name)\n }\n\n for (const edge of edges) {\n const pathIds = edgeToPathsMap.get(edgeKey(edge.from, edge.to))\n if (pathIds && pathIds.size > 0) {\n edge.pathIds = [...pathIds]\n }\n }\n\n // Only walked when the file has paths to walk, so an rGFA \u2014 which has none at\n // all \u2014 pays nothing for it. `lengthOf` reads the segment table rather than\n // the nodes just built, because a path may name a segment no S line declared.\n const lengths = new Map(gfaGraph.nodes.map(n => [n.id, n.length]))\n const anchoring =\n gfaGraph.paths.length > 0 || gfaGraph.walks.length > 0\n ? surveyPaths(anchorablePaths(gfaGraph), id => lengths.get(id) ?? 0)\n : undefined\n\n return {\n name,\n nodes,\n edges,\n paths: paths.length > 0 ? paths : undefined,\n anchorPaths: anchoring?.anchorPaths,\n pathVisits: anchoring?.pathVisits,\n // rGFA's own tags win wherever they are present; pathAnchoring only ever\n // fills in for a graph that carries none.\n anchoredBy: nodes.some(n => n.stable) ? 'tags' : undefined,\n } satisfies Graph\n}\n", "import type { SubgraphRegion } from '../launchSubgraph/launchSubgraphView'\n\n// The members of a LinearGenomeView a hosted pane reads and drives.\n// Structural, because this plugin takes no runtime dependency on the LGV\n// plugin (see hoverSync/index.tsx).\nexport interface HostBlock {\n refName: string\n assemblyName: string\n start: number\n end: number\n offsetPx: number\n reversed?: boolean\n displayedRegionIndex?: number\n}\n\nexport interface LinearHost {\n id: string\n initialized: boolean\n bpPerPx: number\n offsetPx: number\n width: number\n displayedRegions: readonly { start: number; end: number }[]\n dynamicBlocks: { contentBlocks: readonly HostBlock[] }\n coarseDynamicBlocks: readonly HostBlock[]\n horizontalScroll: (distance: number) => number\n zoomTo: (bpPerPx: number, offset?: number) => number\n}\n\nexport function isLinearHost(view: unknown): view is LinearHost {\n if (typeof view !== 'object' || view === null) {\n return false\n }\n const v = view as Record<string, unknown>\n return (\n typeof v.horizontalScroll === 'function' &&\n typeof v.zoomTo === 'function' &&\n 'coarseDynamicBlocks' in v &&\n 'dynamicBlocks' in v\n )\n}\n\n// launchSubgraphView's widestBlock, restated rather than imported: that module\n// imports the model, which imports this.\nfunction widest(blocks: readonly HostBlock[]) {\n return blocks.reduce<HostBlock | undefined>(\n (best, b) => (best && best.end - best.start >= b.end - b.start ? best : b),\n undefined,\n )\n}\n\nexport interface HostWindow {\n refName: string\n assemblyName: string\n start: number\n end: number\n bpPerPx: number\n // the view's width in bp, which the margin is sized by: a block at a contig\n // end is clipped, the zoom is not\n span: number\n regionStart: number\n regionEnd: number\n}\n\n// x is reference bp on an anchored layout, so the graph shows what the linear\n// view shows when one bp is as many px in both and the cut's refName sits at\n// the same screen x. Undefined when the linear view shows no block of that\n// refName, which is a re-cut's job rather than the frame's.\nexport function hostFrame(view: LinearHost, region: SubgraphRegion) {\n const block = widest(\n view.dynamicBlocks.contentBlocks.filter(\n b =>\n b.refName === region.refName && b.assemblyName === region.assemblyName,\n ),\n )\n const { bpPerPx } = view\n return block && bpPerPx > 0\n ? {\n scale: 1 / bpPerPx,\n translateX: block.offsetPx - view.offsetPx - block.start / bpPerPx,\n }\n : undefined\n}\n\n// Read off the LIVE blocks even when the coarse ones woke the caller: those\n// name where the window was half a second ago (SyntenyFollow/CLAUDE.md).\nexport function hostWindow(view: LinearHost): HostWindow | undefined {\n const block = widest(view.dynamicBlocks.contentBlocks)\n const displayed =\n block?.displayedRegionIndex === undefined\n ? undefined\n : view.displayedRegions[block.displayedRegionIndex]\n return block && view.bpPerPx > 0\n ? {\n refName: block.refName,\n assemblyName: block.assemblyName,\n start: block.start,\n end: block.end,\n bpPerPx: view.bpPerPx,\n span: view.bpPerPx * view.width,\n regionStart: displayed?.start ?? block.start,\n regionEnd: displayed?.end ?? block.end,\n }\n : undefined\n}\n\n// The window plus one window-width each side, clamped to the displayed\n// region, with the margins narrowed until the whole cut fits under `capBp`.\nexport function hostCut(window: HostWindow, capBp: number): SubgraphRegion {\n const visible = window.end - window.start\n const margin = Math.max(0, Math.min(window.span, (capBp - visible) / 2))\n return {\n refName: window.refName,\n assemblyName: window.assemblyName,\n start: Math.max(window.regionStart, Math.floor(window.start - margin)),\n end: Math.min(window.regionEnd, Math.ceil(window.end + margin)),\n }\n}\n\nexport function cutHolds(cut: SubgraphRegion | undefined, window: HostWindow) {\n return (\n cut?.refName === window.refName &&\n cut.assemblyName === window.assemblyName &&\n cut.start <= window.start &&\n window.end <= cut.end\n )\n}\n", "import { setConf } from '@jbrowse/core/configuration'\nimport { getSnapshot } from '@jbrowse/mobx-state-tree'\n\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\n// The graph's own off-ramp charcoal, for a segment with no reference\n// coordinate (GeometryBuilder's REFERENCE_RAMP_ALT_COLOR).\nexport const OFF_REFERENCE_COLOR = 'rgb(60,65,72)'\n\n// The reference-position ramp as a `color` expression a linear track can run:\n// hue 0 to 300 over the domain by the feature's reference midpoint, the same\n// function of the same two numbers the graph paints its nodes with. `rank` is\n// what RgfaTabixAdapter puts on a feature; a track carrying none reads\n// `undefined > 0` as false and stays on the ramp.\nexport function referencePositionColor({\n start,\n end,\n}: {\n start: number\n end: number\n}) {\n const mid = \"(get(feature,'start')+get(feature,'end'))/2\"\n const hue = `min(300, max(0, (${mid} - ${start}) / ${end - start} * 300))`\n return `jexl:get(feature,'rank')>0 ? '${OFF_REFERENCE_COLOR}' : 'hsl(' + ${hue} + ',70%,50%)'`\n}\n\nexport interface LaneDisplay {\n type: string\n configuration: AnyConfigurationModel\n}\n\ninterface LaneTrack {\n configuration: { trackId: string }\n displays: LaneDisplay[]\n}\n\ninterface LaneView {\n id: string\n tracks?: LaneTrack[]\n}\n\nfunction isLaneView(view: unknown): view is LaneView {\n return (\n typeof view === 'object' &&\n view !== null &&\n 'id' in view &&\n Array.isArray((view as { tracks?: unknown }).tracks)\n )\n}\n\n// The display a graph was cut from: the track by id in the linear view it is\n// paired with, or in any linear view holding it when the graph was never\n// paired (a hand-written session), the same rule the hover sync uses.\nexport function sourceLaneDisplay(\n views: unknown[],\n connectedViewId: string | undefined,\n trackId: string,\n) {\n for (const view of views) {\n if (\n isLaneView(view) &&\n (connectedViewId === undefined || view.id === connectedViewId)\n ) {\n const track = view.tracks?.find(t => t.configuration.trackId === trackId)\n const display = track?.displays[0]\n if (display?.type === 'LinearBasicDisplay') {\n return display\n }\n }\n }\n return undefined\n}\n\n// Idempotent, since the autorun that calls this re-fires on every domain read.\n// A host at 5.0.0-beta.9 or later keeps `color` as a channel object whose\n// `value` holds the expression; earlier hosts keep the bare string.\nexport function paintSourceLane(display: LaneDisplay, color: string) {\n const { color: current } = getSnapshot(display.configuration) as {\n color?: unknown\n }\n const painted =\n typeof current === 'object' && current !== null && 'value' in current\n ? current.value\n : current\n if (painted !== color) {\n setConf(display, 'color', color)\n }\n}\n", "import { isBackbone } from '../anchoredNodes'\n\nimport type { Graph, NodeSegment } from '../types'\n\ntype Positions = Record<string, NodeSegment[]>\n\n// Turn a finished force layout so the reference runs left to right: the\n// rotation that best maps backbone midpoints onto their reference coordinate\n// (a one-dimensional orthogonal Procrustes fit, weighted by segment length),\n// then a reflection so the off-reference mass sits below the axis. A rigid\n// transform, so the drawing FMMM made is unchanged; only its frame moves.\n//\n// About the length-weighted centroid, and translated back, so a drawing that is\n// already oriented comes back where it was rather than recentred on the origin.\nexport function orientToReference(graph: Graph, positions: Positions) {\n const backbone = graph.nodes.filter(\n node => isBackbone(node) && (positions[node.id]?.length ?? 0) > 0,\n )\n if (backbone.length < 2) {\n return positions\n }\n const points = backbone.map(node => {\n const segments = positions[node.id]!\n const mid = segments[Math.floor(segments.length / 2)]!\n return {\n x: mid.x,\n y: mid.y,\n r: node.stable!.start + node.length / 2,\n w: node.length,\n }\n })\n const weight = points.reduce((sum, p) => sum + p.w, 0)\n const cx = points.reduce((sum, p) => sum + p.x * p.w, 0) / weight\n const cy = points.reduce((sum, p) => sum + p.y * p.w, 0) / weight\n const cr = points.reduce((sum, p) => sum + p.r * p.w, 0) / weight\n // cross-covariance of (x, y) against (r, 0); only the first row is non-zero\n let sxr = 0\n let syr = 0\n for (const p of points) {\n sxr += p.w * (p.x - cx) * (p.r - cr)\n syr += p.w * (p.y - cy) * (p.r - cr)\n }\n // the rotation taking the direction (sxr, syr) onto +x\n const angle = -Math.atan2(syr, sxr)\n const c = Math.cos(angle)\n const s = Math.sin(angle)\n\n const out: Positions = {}\n for (const [id, segments] of Object.entries(positions)) {\n out[id] = segments.map(p => {\n const x = p.x - cx\n const y = p.y - cy\n return { x: cx + c * x - s * y, y: cy + s * x + c * y }\n })\n }\n\n let below = 0\n let above = 0\n for (const node of graph.nodes) {\n if (isBackbone(node)) {\n continue\n }\n for (const p of out[node.id] ?? []) {\n if (p.y > cy) {\n below++\n } else {\n above++\n }\n }\n }\n if (above > below) {\n for (const segments of Object.values(out)) {\n for (const p of segments) {\n p.y = 2 * cy - p.y\n }\n }\n }\n return out\n}\n", "import { isBackbone } from '../anchoredNodes'\nimport { buildNeighbors } from '../referenceSpan'\nimport { drawnNodeLength } from './drawnScale'\n\nimport type { LayoutScaling } from './drawnScale'\nimport type { Graph, GraphNode } from '../types'\n\n// A node as the engine takes it: an optional `x`/`y` is where FMMM starts the\n// node's chain (`addToOgdfGraph`, graphlayout.cpp).\nexport type LayoutNode = GraphNode & { x?: number; y?: number }\n\nexport interface Seed {\n x: number\n y: number\n}\n\n// y per BFS step off the backbone. FMMM keeps only the coarse shape of these\n// (OGDF applies the initial placement at its coarsest multilevel graph), so\n// the gap needs to say \"below the line\", not to place a lane.\nconst LANE_GAP = 30\n\n// Initial positions for a reference-anchored graph: the backbone end to end\n// along x at drawn length, each off-reference node at the midpoint of the node\n// a BFS from the backbone reached it from, a lane lower per step. Two alleles\n// off one anchor share a seed; FMMM separates them. Nodes no backbone reaches\n// spread along x below everything rather than piling on one point.\n//\n// Lengths come from `scaling.nodes`, not the graph's bp: under a compressing\n// spread a node's `length` crosses the RPC as drawn units times 1000 with a\n// matching `nodeLengthPerMegabase`, and a seed from bp would misplace every\n// chain against the one the engine builds.\nexport function referenceSeeds(\n graph: Graph,\n scaling: LayoutScaling,\n): Map<string, Seed> {\n const { opts } = scaling\n const drawn = new Map(\n scaling.nodes.map(node => [node.id, drawnNodeLength(opts, node.length)]),\n )\n const drawnOf = (id: string) => drawn.get(id) ?? opts.minimumNodeLength\n const backbone = graph.nodes\n .filter(isBackbone)\n .sort((a, b) => a.stable.start - b.stable.start)\n\n const seeds = new Map<string, Seed>()\n let x = 0\n for (const node of backbone) {\n seeds.set(node.id, { x, y: 0 })\n x += drawnOf(node.id) + opts.edgeLength\n }\n\n const adjacent = buildNeighbors(graph)\n\n const depth = new Map(backbone.map(node => [node.id, 0]))\n // for-of over an array that grows as it goes: the iterator reads the live\n // length, so nodes pushed here are visited\n const queue = backbone.map(node => node.id)\n for (const id of queue) {\n const at = seeds.get(id)!\n const next = depth.get(id)! + 1\n for (const neighbour of adjacent.get(id) ?? []) {\n if (depth.has(neighbour)) {\n continue\n }\n depth.set(neighbour, next)\n seeds.set(neighbour, { x: at.x + drawnOf(id) / 2, y: LANE_GAP * next })\n queue.push(neighbour)\n }\n }\n\n let unreached = 0\n for (const node of graph.nodes) {\n if (!seeds.has(node.id)) {\n seeds.set(node.id, {\n x: (unreached++ * x) / graph.nodes.length,\n y: LANE_GAP * 3,\n })\n }\n }\n return seeds\n}\n\nexport function seededNodes(\n graph: Graph,\n scaling: LayoutScaling,\n): LayoutNode[] {\n const seeds = referenceSeeds(graph, scaling)\n return scaling.nodes.map(node => ({ ...node, ...seeds.get(node.id) }))\n}\n", "import type { Feature } from '@jbrowse/core/util'\n\nexport interface RepeatCall {\n bp: number\n // TRGT's `SD`. Zero means the allele is a copy of the one the reads did\n // support, so its length states nothing about a second haplotype; absent\n // where the genotyper writes no such field.\n spanningReads?: number\n}\n\n// A tandem repeat array as the walk rows need it: its span on the reference,\n// which is what the bars measure between, and its unit length, which is what\n// they tile by. Read off whatever repeat annotation the session has, since the\n// tools all state the same two facts under different names: VCF 4.5's\n// `<CNV:TR>` allele gives `RUL` and `RUS` (one entry per repeat sequence,\n// grouped by `RN`), UCSC simpleRepeat and TRF give `period` and the consensus\n// `sequence`, TRGT gives `MOTIFS` and `TRID`, ExpansionHunter `RU` and `REPID`,\n// HipSTR and GangSTR `PERIOD`, vamos its `motifs`. A VCF record's INFO is\n// searched as well as its top level, and a list's first entry is the unit the\n// bars tile by: for a compound array that is the first repeat sequence, which\n// is the expanding one at every such locus in the common catalogues.\nexport interface RepeatArray {\n key: string\n name: string\n refName: string\n start: number\n end: number\n unit: number\n motif?: string\n // Each sample's genotyped alleles, from a genotyper's per-sample `AL`\n // (TRGT); absent from a catalogue.\n calls?: Record<string, RepeatCall[]>\n}\n\nexport const REPEAT_ADAPTER_TYPES = new Set([\n 'BedAdapter',\n 'BedTabixAdapter',\n 'BigBedAdapter',\n 'VcfAdapter',\n 'VcfTabixAdapter',\n])\n\nconst REPEAT_TRACK_HINT =\n /repeat|tandem|\\bstr\\b|vntr|trf|trgt|vamos|expansion|microsat/i\n\nexport function pickRepeatTrack<\n T extends { trackId: string; name?: string; adapterType: string },\n>(tracks: T[], named: string) {\n if (named) {\n return tracks.find(t => t.trackId === named)\n }\n const candidates = tracks.filter(t => REPEAT_ADAPTER_TYPES.has(t.adapterType))\n return candidates.find(t =>\n REPEAT_TRACK_HINT.test(`${t.trackId} ${t.name ?? ''}`),\n )\n}\n\ntype FeatureLike = Feature | Record<string, unknown>\n\nfunction own(f: FeatureLike, name: string): unknown {\n return typeof (f as Feature).get === 'function'\n ? (f as Feature).get(name)\n : (f as Record<string, unknown>)[name]\n}\n\n// A TRGT repeat catalogue is a BED whose name column packs the record:\n// `ID=HTT;MOTIFS=CAG,CCG;STRUC=(CAG)nCAACAG(CCG)n`. Read as fields when it\n// has that shape.\nconst PACKED_NAME = /^\\w+=[^;]*(;\\w+=[^;]*)*$/\n\nfunction packedName(f: FeatureLike) {\n const name = own(f, 'name')\n if (typeof name !== 'string' || !PACKED_NAME.test(name)) {\n return undefined\n }\n return Object.fromEntries(\n name.split(';').map(pair => {\n const i = pair.indexOf('=')\n return [pair.slice(0, i), pair.slice(i + 1)]\n }),\n )\n}\n\n// A field at the feature's top level, in a VCF record's INFO, or in a packed\n// name column, in that order.\nfunction field(f: FeatureLike, name: string): unknown {\n const top = own(f, name)\n if (top !== undefined) {\n return top\n }\n const info = own(f, 'INFO') as Record<string, unknown> | undefined\n return info?.[name] ?? packedName(f)?.[name]\n}\n\nconst MOTIF_FIELDS = [\n 'RUS',\n 'MOTIFS',\n 'RU',\n 'motifs',\n 'motif',\n 'sequence',\n 'consensus',\n]\nconst PERIOD_FIELDS = [\n 'RUL',\n 'period',\n 'PERIOD',\n 'consensusSize',\n 'unit',\n 'unitLength',\n]\nconst NAME_FIELDS = ['TRID', 'REPID', 'VARID', 'ID', 'name', 'id']\n\n// The first entry of a list-valued field, whether it arrived parsed or as\n// the comma-joined text a VCF INFO or a BED column holds; VCF's missing \".\"\n// counts as absent.\nfunction first(f: FeatureLike, names: string[]) {\n for (const name of names) {\n const value = field(f, name)\n const one = String((Array.isArray(value) ? value[0] : value) ?? '')\n .split(',')[0]!\n .trim()\n if (one !== '' && one !== '.') {\n return one\n }\n }\n return undefined\n}\n\nconst IUPAC = /^[ACGTURYSWKMBDHVN]+$/i\n\n// The unit in bp: a stated period, else the length of the first motif.\nexport function repeatUnitOf(f: FeatureLike) {\n const period = Number(first(f, PERIOD_FIELDS))\n if (Number.isFinite(period) && period > 0) {\n return Math.round(period)\n }\n const motif = first(f, MOTIF_FIELDS)\n return motif && IUPAC.test(motif) ? motif.length : undefined\n}\n\n// A per-sample list field, parsed or as the comma-joined text a VCF column\n// holds. A missing entry stays missing: read as 0, an absent `SD` would make\n// every allele of every genotyper that writes no such field unspanned.\nfunction numbers(value: unknown) {\n const raw: unknown[] = Array.isArray(value)\n ? value\n : String(value ?? '').split(',')\n return raw.map(one => {\n const n =\n one === '' || one === null || one === undefined ? NaN : Number(one)\n return Number.isFinite(n) ? n : undefined\n })\n}\n\nfunction callsOf(f: FeatureLike) {\n const samples = own(f, 'samples') as\n Record<string, Record<string, unknown>> | undefined\n const called: Record<string, RepeatCall[]> = {}\n for (const [sample, fields] of Object.entries(samples ?? {})) {\n const reads = numbers(fields.SD)\n const alleles = numbers(fields.AL).flatMap((bp, i) =>\n bp !== undefined && bp > 0\n ? [reads[i] === undefined ? { bp } : { bp, spanningReads: reads[i] }]\n : [],\n )\n if (alleles.length > 0) {\n called[sample] = alleles\n }\n }\n return Object.keys(called).length > 0 ? called : undefined\n}\n\nexport function repeatArraysFrom(features: FeatureLike[]): RepeatArray[] {\n const arrays: RepeatArray[] = []\n for (const f of features) {\n const start = field(f, 'start') as number\n const end = field(f, 'end') as number\n const unit = repeatUnitOf(f)\n if (!(end > start) || unit === undefined) {\n continue\n }\n const refName = field(f, 'refName') as string\n const motif = first(f, MOTIF_FIELDS)\n const stated = first(f, NAME_FIELDS)\n arrays.push({\n key: `${refName}:${start}-${end}`,\n name:\n (stated && !PACKED_NAME.test(stated) ? stated : undefined) ??\n (motif && motif.length <= 12\n ? `(${motif})n`\n : `${refName}:${(start + 1).toLocaleString()}-${end.toLocaleString()}`),\n refName,\n start,\n end,\n unit,\n motif,\n calls: callsOf(f),\n })\n }\n return arrays.sort((a, b) => a.start - b.start)\n}\n", "import { pathOrigin } from './pathAnchoring'\n\nimport type { Graph } from './types'\n\n// One walk lifted out of the drawing: the nodes it visits and the links it\n// takes, so the geometry can fade everything else, plus what it carries\n// through the window against the reference walk.\nexport interface WalkHighlight {\n name: string\n nodeIds: Set<string>\n edgeIndexes: Set<number>\n steps: number\n bp: number\n referenceBp?: number\n}\n\nexport function walkHighlight(\n graph: Graph,\n name: string,\n): WalkHighlight | undefined {\n const path = graph.paths?.find(p => p.name === name)\n if (!path) {\n return undefined\n }\n const byId = new Map(graph.nodes.map(n => [n.id, n]))\n const edgeAt = new Map<string, number>()\n graph.edges.forEach((e, i) => {\n edgeAt.set(`${e.from}>${e.to}`, i)\n })\n const nodeIds = new Set(path.nodeIds)\n const edgeIndexes = new Set<number>()\n for (let i = 1; i < path.nodeIds.length; i++) {\n const a = path.nodeIds[i - 1]!\n const b = path.nodeIds[i]!\n const index = edgeAt.get(`${a}>${b}`) ?? edgeAt.get(`${b}>${a}`)\n if (index !== undefined) {\n edgeIndexes.add(index)\n }\n }\n const bpOf = (ids: string[]) =>\n ids.reduce((sum, id) => sum + (byId.get(id)?.length ?? 0), 0)\n // `referencePath` is the anchor name, which pathOrigin has stripped of the\n // range suffix odgi leaves on a P record's name\n const reference = graph.referencePath\n ? graph.paths!.find(p => pathOrigin(p.name).name === graph.referencePath)\n : undefined\n return {\n name,\n nodeIds,\n edgeIndexes,\n steps: path.nodeIds.length,\n bp: bpOf(path.nodeIds),\n referenceBp:\n reference && reference !== path ? bpOf(reference.nodeIds) : undefined,\n }\n}\n", "import { isBackbone } from '../GraphGenomeView/anchoredNodes'\n\nimport type { GraphNode } from '../GraphGenomeView/types'\n\n// What a LinearGenomeView writes to `session.hovered` on every mousemove (see\n// LinearGenomeViewContainer): the bp under the cursor, plus the feature under it\n// if the topmost track had one. Nothing names the source VIEW, so a graph view\n// filters by whether the position falls in the region it was cut from \u2014 but the\n// position does name the source ASSEMBLY, and that turns out to be the half\n// that matters.\nexport interface LgvHover {\n refName: string\n // The 0-based base under the pointer, the convention `stable.start` and the\n // cut region are in. Not the LGV's own `coord`, which is 1-based for display.\n coord: number\n // The assembly the hovered view is showing. `hoverPosition` is a\n // `PxToBpResult`, i.e. the displayed region the pointer landed in spread flat,\n // so this is that region's own `assemblyName`.\n //\n // Read because refName alone does not identify a locus in a session holding\n // several assemblies, which is every session these graphs are launched in. A\n // PanSN name reduces to a bare contig \u2014 the E. coli pangenome loads five\n // strains each with one refName `chr`, and HPRC's hg38 and a GenArk haplotype\n // both call it `chr6` \u2014 so a synteny stack's rows all answer to the same\n // name at unrelated coordinates. Without this the hover on ANY row lit up a\n // node in the graph, and the node then published its own reference interval\n // back into every linear view.\n //\n // Optional because the channel is typed `unknown` and read structurally: a\n // hover that states no assembly is taken at its word rather than dropped.\n assemblyName?: string\n featureName?: string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\ninterface FeatureLike {\n get: (key: string) => unknown\n}\n\nfunction isFeatureLike(value: unknown): value is FeatureLike {\n return isRecord(value) && typeof value.get === 'function'\n}\n\n// A display hovers either a Feature, read with `get`, or a plain hit item with\n// a `name` of its own, which is what the canvas feature display hands over.\n// Reading only the first left the exact segment match dead on that display.\nfunction hoveredName(feature: unknown) {\n const name = isFeatureLike(feature)\n ? feature.get('name')\n : isRecord(feature)\n ? feature.name\n : undefined\n return typeof name === 'string' ? name : undefined\n}\n\n// `hovered` is typed `unknown` on the session by design \u2014 it is a shared channel\n// every view writes its own shape to \u2014 so this reads the LGV's shape out of it\n// structurally rather than asserting a type onto it.\nexport function readLgvHover(hovered: unknown): LgvHover | undefined {\n let result: LgvHover | undefined\n if (isRecord(hovered) && isRecord(hovered.hoverPosition)) {\n const { refName, coord, coord0, assemblyName } = hovered.hoverPosition\n if (typeof refName === 'string' && typeof coord === 'number') {\n result = {\n refName,\n // `coord` is 1-based; a host that states the 0-based `coord0` is\n // taken at its word. Read as 0-based, the last base of every node lit\n // the next one, and a 1 bp node could not be reached at all.\n coord: typeof coord0 === 'number' ? coord0 : coord - 1,\n assemblyName:\n typeof assemblyName === 'string' ? assemblyName : undefined,\n featureName: hoveredName(hovered.hoverFeature),\n }\n }\n }\n return result\n}\n\n// Whether a hover is one this graph can answer: the same assembly, the same\n// stable sequence, and inside the window the cut was made for.\n//\n// The assembly is checked FIRST and is the whole reason this is not just a\n// coordinate test. See LgvHover.assemblyName: a graph cut from K12 and a\n// synteny row on Sakai both call their sequence `chr`, so on refName and\n// coordinate alone every row of the stack matched and the graph highlighted a\n// K12 node at Sakai's offset. A hover that names no assembly still passes,\n// which is what a hand-written channel writer and the older LGVs get.\nexport function hoverInRegion(\n hover: LgvHover,\n region: { refName: string; assemblyName: string; start: number; end: number },\n) {\n return (\n (hover.assemblyName === undefined ||\n hover.assemblyName === region.assemblyName) &&\n hover.refName === region.refName &&\n hover.coord >= region.start &&\n hover.coord < region.end\n )\n}\n\n// The graph node a linear-view hover points at.\n//\n// The feature under the cursor is the exact answer when the graph's own segments\n// track is what's hovered: an RgfaTabixAdapter feature's name is the segment id,\n// which is `GraphNode.name` (`GraphNode.id` carries a strand suffix, so the\n// match has to be on name). Any other track \u2014 genes, bubbles \u2014 supplies only a\n// coordinate, which still identifies the backbone segment covering it: the\n// backbone tiles the reference exactly once, whether rGFA's rank 0 declared it\n// or a reference path's own steps did.\nexport function nodeForLgvHover({\n hover,\n nodes,\n}: {\n hover: LgvHover\n nodes: GraphNode[]\n}) {\n let byName: string | undefined\n let byCoord: string | undefined\n for (const node of nodes) {\n if (node.name === hover.featureName) {\n byName = node.id\n } else if (\n byCoord === undefined &&\n isBackbone(node) &&\n hover.coord >= node.stable.start &&\n hover.coord < node.stable.start + node.length\n ) {\n byCoord = node.id\n }\n }\n // An exact segment match wins over the coordinate it happens to sit at.\n return byName ?? byCoord ?? null\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\n\nimport { locLabel, locString } from './contributors'\nimport { linearViewTarget } from './linearViewTarget'\n\nimport type { Contributor, GraphLocation } from './contributors'\nimport type { TrackScanSession } from '../launchSubgraph/subgraphTracks'\n\nexport interface GraphLaunchSession extends TrackScanSession {\n addView: (type: string, snapshot?: Record<string, unknown>) => { id: string }\n // The session's own loader for a view whose state model is registered\n // lazily (JBrowse 4's LinearSyntenyView); absent on an older core, where\n // addView is enough\n launchView?: (\n type: string,\n snapshot?: Record<string, unknown>,\n ) => Promise<{ id: string }>\n assemblyNames: string[]\n views: unknown[]\n // optional, as a hand-built session in a test has none\n notifyError?: (message: string, error?: unknown) => void\n}\n\n// A launch is a promise nothing waits on. Let go, a refusal moved nothing and\n// said nothing; this is where it is told to the user instead.\nfunction reportFailure(\n session: GraphLaunchSession,\n what: string,\n launch: Promise<unknown>,\n) {\n launch.catch((error: unknown) => {\n console.error(`[GraphGenomeView] ${what}`, error)\n session.notifyError?.(`${what}: ${error}`, error)\n })\n}\n\n// A node is often shorter than a line of text \u2014 a base-level allele can be a\n// handful of bp \u2014 and a linear view opened on exactly that span shows the span\n// and no context at all. Padding to a minimum window puts the node in the middle\n// of something readable.\nconst MIN_LAUNCH_SPAN_BP = 1000\n\nexport function paddedLocation(\n loc: GraphLocation,\n minSpan = MIN_LAUNCH_SPAN_BP,\n): GraphLocation {\n const pad = Math.max(0, Math.ceil((minSpan - (loc.end - loc.start)) / 2))\n return { ...loc, start: Math.max(0, loc.start - pad), end: loc.end + pad }\n}\n\n// Restate the rank-0 contributor in the terms of the region the graph was cut\n// from. Two separate corrections, both required:\n//\n// - the *assembly*. A backbone segment's SN is the graph's own spelling of the\n// reference, which need not be the name of any loaded assembly: the HPRC\n// graph says `GRCh38#0#chr6` in a session whose assembly is `hg38`. Taking\n// the PanSN sample there resolves to nothing and the graph offers no way out\n// at all. `loadedRegion` is authoritative \u2014 the adapter resolved that\n// assembly to that stable sequence to cut the subgraph in the first place.\n// The backbone's `haplotype` (`GRCh38#0`) goes with it, since\n// resolveLocationAssembly tries that before the sample.\n// - the *locus*. The cut region is what the user framed in the linear view, and\n// it is the span every other panel of a synteny launch is compared against;\n// the union of backbone segments differs from it whenever the window ends\n// inside a bubble.\n//\n// A subgraph is cut on one reference, so there is at most one rank-0\n// contributor. Must run before contributors are resolved against the session's\n// assemblies, or the reference is filtered out by the name this replaces.\nexport function withReferenceRegion(\n contributors: Contributor[],\n region:\n | { refName: string; assemblyName: string; start: number; end: number }\n | undefined,\n): Contributor[] {\n return region\n ? contributors.map(c =>\n c.rank === 0\n ? {\n ...c,\n sample: region.assemblyName,\n haplotype: undefined,\n refName: region.refName,\n start: region.start,\n end: region.end,\n }\n : c,\n )\n : contributors\n}\n\n// Show a location in a linear view: move the one already on screen if there is\n// one to move, open a pane only if there isn't.\n//\n// This is the difference between \"flip to the linear view\" and \"accumulate\n// panes\". The pangenome sessions this ships in are a linear view above a graph,\n// and there the answer to \"where is this node?\" should be that the linear view\n// scrolls to it \u2014 see linearViewTarget for which view counts as the one to move.\n//\n// `tracks` is only applied to a view being created; a view already open keeps\n// whatever the user has turned on. Returns the id of the view that ended up\n// showing the location, so the graph can pair with it for the hover sync.\nexport function showInLinearView({\n session,\n location,\n assembly,\n connectedViewId,\n tracks = [],\n}: {\n session: GraphLaunchSession\n location: GraphLocation\n assembly: string\n connectedViewId?: string\n tracks?: string[]\n}) {\n const existing = linearViewTarget({\n views: session.views,\n connectedViewId,\n assemblyName: assembly,\n })\n let viewId: string\n if (existing) {\n reportFailure(\n session,\n `Could not show ${assembly} ${locLabel(location)}`,\n Promise.resolve(existing.navToLocString(locString(location), assembly)),\n )\n viewId = existing.id\n } else {\n viewId = session.addView('LinearGenomeView', {\n displayName: `${assembly} \u2014 ${locLabel(location)}`,\n init: { assembly, loc: locString(location), tracks },\n }).id\n }\n return viewId\n}\n\n// Mark where a node sits on the reference, in the linear view already on\n// screen, instead of scrolling that view to it. Review: \"need to be able to\n// just highlight lineargenomeview coords\".\n//\n// The other half of the pair above, and the half that survives being let go of:\n// hover sync draws a band for as long as the pointer is over the node, where a\n// highlight is written into the view's own list and stays until it is removed.\n// It also keeps the reader's frame \u2014 the interesting comparison at a pangenome\n// locus is usually the whole window, not the 200 bp the node occupies.\n//\n// Nothing to mark when no linear view on that assembly is ours to move (see\n// linearViewTarget); the caller offers the item only when there is.\nexport function highlightInLinearView({\n session,\n location,\n assembly,\n connectedViewId,\n}: {\n session: GraphLaunchSession\n location: GraphLocation\n assembly: string\n connectedViewId?: string\n}) {\n const target = linearViewTarget({\n views: session.views,\n connectedViewId,\n assemblyName: assembly,\n })\n target?.addToHighlights?.({\n refName: location.refName,\n start: location.start,\n end: location.end,\n assemblyName: assembly,\n })\n return target !== undefined\n}\n\n// Which assemblies a track config says it covers, for deciding whether a panel\n// can draw it at all. A trackId naming nothing in the session reads as covering\n// nothing, so a stale id drops the lane rather than adding one the panel would\n// fail to load.\nfunction assemblyNamesOfTrack(\n session: GraphLaunchSession,\n trackId: string | undefined,\n) {\n if (trackId === undefined) {\n return []\n }\n const track = session.tracks.find(\n t => readConfObject(t, 'trackId') === trackId,\n )\n const names: unknown = track ? readConfObject(track, 'assemblyNames') : []\n return Array.isArray(names) ? names.filter(n => typeof n === 'string') : []\n}\n\n// One panel per contributing assembly, each framed on the locus that assembly\n// contributes here, with the graph's own reference panel on top.\n//\n// The panel loci come from the graph rather than from the synteny track: rGFA\n// states where each contributor's sequence sits on its own coordinates, so the\n// launch needs no mate discovery, no PAF lookup and no dialog. That is the whole\n// point of launching from the graph instead of from a linear view.\n//\n// Panels carry THE GRAPH'S OWN TRACK and nothing else, which is a narrower rule\n// than the single-view launch's (see launchTracks, which scans the session for\n// the assembly's annotation). Tried that here: a gene track per panel takes\n// about 160px of a row that is otherwise a ruler, so on five strains the\n// annotation is most of the viewport and the ribbons \u2014 the thing the launch\n// exists to draw \u2014 are squeezed into the gaps, and `collapseEmptyRows` is here\n// to prevent exactly that.\n//\n// The graph's own track does not have that shape. It goes only in the panels\n// whose assembly its config declares, which is where the graph HAS coordinates:\n// the HPRC segments track names two, so both its panels get one lane, while the\n// E. coli one names K12 alone and a five-strain launch adds a single lane rather\n// than five. And it is the lane that makes the band readable, since the ribbons\n// otherwise connect two bare rulers and the reader has nothing to match across\n// them.\nexport function launchSyntenyView({\n session,\n contributors,\n trackId,\n graphTrackId,\n}: {\n session: GraphLaunchSession\n contributors: Contributor[]\n trackId?: string\n graphTrackId?: string\n}) {\n const graphTrackAssemblies = assemblyNamesOfTrack(session, graphTrackId)\n // The panels the chosen track aligns, in the order given. A pairwise track is\n // offered beside an all-vs-all, two assemblies being a synteny view, and over\n // every contributor it sat on each level and could draw on one at most. A\n // track that states no assemblies keeps them all.\n const aligned = assemblyNamesOfTrack(session, trackId)\n const covered = contributors.filter(c => aligned.includes(c.sample))\n const panels = covered.length >= 2 ? covered : contributors\n const snapshot = {\n init: {\n views: panels.map(c => ({\n assembly: c.sample,\n loc: locString(c),\n ...(graphTrackId !== undefined &&\n graphTrackAssemblies.includes(c.sample)\n ? { tracks: [graphTrackId] }\n : {}),\n })),\n // One entry per LEVEL, not one for the track. `init.tracks` is 2D \u2014 the\n // gap between views[i] and views[i+1] \u2014 and a flat `[trackId]` is read as\n // the level-0 shorthand, so on a five-strain launch only the top band got\n // the alignment and the four below it opened as bare rulers. The same\n // all-vs-all track fills every level: it carries all the pairs.\n tracks: trackId ? panels.slice(1).map(() => [trackId]) : [],\n collapseEmptyRows: true,\n },\n }\n // launchView where the session has it: a synteny view's state model is\n // loaded on demand and addView throws until it is\n if (session.launchView) {\n reportFailure(\n session,\n 'Could not open the synteny view',\n session.launchView('LinearSyntenyView', snapshot),\n )\n } else {\n session.addView('LinearSyntenyView', snapshot)\n }\n}\n", "export interface NavigableLinearView {\n id: string\n assemblyNames: string[]\n navToLocString: (locString: string, assemblyName?: string) => unknown\n // The other way of answering \"where is this node\": mark it rather than scroll\n // to it. Optional, and not part of isLinearView's test \u2014 navigating is what\n // makes a view a target here, and a view that cannot be marked can still be\n // moved.\n addToHighlights?: (highlight: {\n refName: string\n start: number\n end: number\n assemblyName: string\n }) => unknown\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n// Reads `session.views` structurally, the same way graphViewHighlights does: the\n// members needed here are declared by LinearGenomeView, not by the\n// AbstractViewModel the session array is typed as.\nfunction isLinearView(view: unknown): view is NavigableLinearView {\n return (\n isRecord(view) &&\n view.type === 'LinearGenomeView' &&\n typeof view.id === 'string' &&\n typeof view.navToLocString === 'function' &&\n Array.isArray(view.assemblyNames)\n )\n}\n\n// The view a graph was launched from is not always in `session.views`: a row of\n// a LinearSyntenyView, LinearComparativeView or BreakpointSplitView sits in\n// that view's own `views[]`, so the candidates are the session's views plus one\n// level of rows.\nexport function withRows(views: unknown[]) {\n return views.flatMap(view =>\n isRecord(view) && Array.isArray(view.views)\n ? [view, ...view.views]\n : [view],\n )\n}\n\n// The linear view a \"show me this\" from the graph should move, rather than\n// opening a pane beside it. Three cases, in order:\n//\n// - the view this graph is paired with (`connectedViewId`), which is either\n// the view the graph was launched from or the one it later opened itself.\n// Explicit, so it wins.\n// - failing that, the only linear view in the session showing this assembly.\n// The pangenome sessions this view ships in are a linear view above a graph\n// with no pairing recorded, and there \"the linear view\" is unambiguous \u2014 the\n// same reasoning hoverSync's isConnected already applies to highlights.\n// - two or more candidates and no pairing: nothing. Guessing which of the\n// user's views to scroll is worse than opening one that is ours to move.\n//\n// Assembly-gated throughout: a linear view on a different assembly cannot show\n// this location at all.\nexport function linearViewTarget({\n views,\n connectedViewId,\n assemblyName,\n}: {\n views: unknown[]\n connectedViewId: string | undefined\n assemblyName: string\n}) {\n const candidates = withRows(views)\n .filter(view => isLinearView(view))\n .filter(view => view.assemblyNames.includes(assemblyName))\n const connected = candidates.find(view => view.id === connectedViewId)\n return connected ?? (candidates.length === 1 ? candidates[0] : undefined)\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\n\nimport type { TrackScanSession } from '../launchSubgraph/subgraphTracks'\n\nconst ANNOTATION_TRACK_TYPE = 'FeatureTrack'\n\n// What to turn on in a linear view the graph opens on `assemblyName`.\n//\n// A launch used to carry the graph's own track and nothing else, and that track\n// is configured for the reference alone \u2014 so `Launch view` \u2192 `CFT073 chr:\u2026`\n// landed on `No tracks active`, which answers \"where is this strain's sequence?\"\n// with an empty pane. What the session does hold for a contributing assembly is\n// that assembly's own annotation, and on a pangenome config that is one track\n// per strain (`CFT073_genes`), which is exactly what makes the landing readable.\n//\n// Annotation tracks only, not everything configured for the assembly. A launch\n// is a jump to a locus, and an alignments or coverage track turned on behind the\n// user's back fetches data nobody asked for; a FeatureTrack at a 50 kb window is\n// a tabix range query. The graph's own segments track is a FeatureTrack too, so\n// the reference case still gets it from the same scan.\n//\n// `first` is the graph's own track when the launch is on the reference, so the\n// segments the graph drew sit at the top of the view rather than wherever config\n// order puts them.\nexport function launchTracks({\n session,\n assemblyName,\n first,\n}: {\n session: TrackScanSession\n assemblyName: string\n first?: string\n}) {\n const found: string[] = []\n for (const track of session.tracks) {\n const type: unknown = readConfObject(track, 'type')\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n type === ANNOTATION_TRACK_TYPE &&\n Array.isArray(assemblyNames) &&\n assemblyNames.includes(assemblyName) &&\n typeof trackId === 'string' &&\n trackId !== first\n ) {\n found.push(trackId)\n }\n }\n return first ? [first, ...found] : found\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { getTrackName } from '@jbrowse/core/util/tracks'\n\nimport type { TrackScanSession } from '../launchSubgraph/subgraphTracks'\n\nexport interface LaunchableSyntenyTrack {\n trackId: string\n name: string\n // how many of the requested assemblies this track covers, so a whole-set\n // all-vs-all sorts above a pairwise alignment covering two of them\n coverage: number\n}\n\nconst SYNTENY_TRACK_TYPE = 'SyntenyTrack'\n\n// Synteny datasets in the session that align at least two of `assemblyNames`.\n//\n// Track type rather than adapter name: SyntenyTrack is the core track type every\n// synteny adapter is configured under, so this does not go stale when an adapter\n// is added or renamed the way naming PAF adapters would.\n//\n// Two is the floor because that is the floor for a synteny view; more is better,\n// and an all-vs-all covering every contributing assembly is what makes the\n// multi-panel launch worth offering at all.\nexport function launchableSyntenyTracks(\n session: TrackScanSession,\n assemblyNames: string[],\n) {\n const wanted = new Set(assemblyNames)\n const found: LaunchableSyntenyTrack[] = []\n for (const track of session.tracks) {\n const type: unknown = readConfObject(track, 'type')\n const trackAssemblies: unknown = readConfObject(track, 'assemblyNames')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n type === SYNTENY_TRACK_TYPE &&\n typeof trackId === 'string' &&\n Array.isArray(trackAssemblies)\n ) {\n const coverage = trackAssemblies.filter(name => wanted.has(name)).length\n if (coverage >= 2) {\n found.push({ trackId, name: getTrackName(track, session), coverage })\n }\n }\n }\n return found.sort((a, b) => b.coverage - a.coverage)\n}\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { getTrackName } from '@jbrowse/core/util/tracks'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nexport interface SubgraphTrack {\n trackId: string\n name: string\n // The assembly a cut has to be on; see graphReferenceAssembly.\n referenceAssembly: string | undefined\n // The lanes the cut is for; undefined is every haplotype.\n haplotypes: string[] | undefined\n}\n\nexport interface TrackScanSession {\n tracks: AnyConfigurationModel[]\n assemblies: AnyConfigurationModel[]\n}\n\n// The linear view a launch is offered from, as far as the lanes go.\nexport interface LaunchingView {\n tracks: readonly {\n configuration: AnyConfigurationModel\n displays: readonly unknown[]\n }[]\n}\n\ninterface LaneSelectingDisplay {\n laneSelection: readonly string[] | undefined\n hiddenLanes: readonly string[]\n}\n\n// Whether an adapter declares it can cut a local subgraph. Discovery is by\n// declared capability, not adapter name: the old launcher hardcoded\n// `GfaTabixAdapter`/`GfaServerAdapter`, which is exactly what left it dead when\n// those were removed.\n//\n// `getAdapterType` throws for a type that was never registered, and a session\n// can hold tracks whose plugin isn't loaded, so registration is checked first \u2014\n// a session-wide scan reaches tracks a single display's menu never would.\nexport function adapterCanCutSubgraph(\n pluginManager: PluginManager,\n adapterType: unknown,\n) {\n return (\n typeof adapterType === 'string' &&\n pluginManager.adapterTypes.has(adapterType) &&\n pluginManager\n .getAdapterType(adapterType)\n .adapterCapabilities.includes('getSubgraph')\n )\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(item => typeof item === 'string')\n}\n\nfunction isLaneSelecting(display: unknown): display is LaneSelectingDisplay {\n return (\n typeof display === 'object' &&\n display !== null &&\n 'laneSelection' in display &&\n 'hiddenLanes' in display\n )\n}\n\n// The assemblies the track names after its reference, which is what core's\n// MultiWaySyntenyDisplay opens a lane-declaring source on.\nexport function trackLanes(track: AnyConfigurationModel) {\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n const lanes = isStringArray(assemblyNames) ? assemblyNames.slice(1) : []\n return lanes.length > 0 ? lanes : undefined\n}\n\n// A graph is cut on the first assembly its track names, the rule\n// GbzBaseSyntenyAdapter states for its anchor. Every other assembly the track\n// names holds its sequence as rank>0 alleles off that reference's backbone, so\n// a window on one has no reference coordinates to frame the cut by.\nexport function graphReferenceAssembly(track: AnyConfigurationModel) {\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n return isStringArray(assemblyNames) ? assemblyNames[0] : undefined\n}\n\nexport function offReferenceProblem(\n referenceAssembly: string | undefined,\n assemblyName: string,\n) {\n return referenceAssembly === undefined || referenceAssembly === assemblyName\n ? undefined\n : `The graph is cut on its reference, ${referenceAssembly}: open it from a view of ${referenceAssembly}`\n}\n\n// The lanes the track draws in `view`: the ones in force there less any the\n// reader hid. A track not on screen there has its own assemblies to go by.\nfunction lanesToCut(\n track: AnyConfigurationModel,\n trackId: string,\n view: LaunchingView | undefined,\n) {\n const shown = view?.tracks\n .find(t => readConfObject(t.configuration, 'trackId') === trackId)\n ?.displays.find(isLaneSelecting)\n return shown\n ? shown.laneSelection?.filter(name => !shown.hiddenLanes.includes(name))\n : trackLanes(track)\n}\n\nexport function subgraphTrack(\n track: AnyConfigurationModel,\n trackId: string,\n session: TrackScanSession,\n view?: LaunchingView,\n): SubgraphTrack {\n return {\n trackId,\n name: getTrackName(track, session),\n referenceAssembly: graphReferenceAssembly(track),\n haplotypes: lanesToCut(track, trackId, view),\n }\n}\n\n// Tracks anywhere in the session that can cut a subgraph on `assemblyName`, so\n// the launch can be offered from a linear view that doesn't have the graph track\n// in it. The entry point no longer has to be the graph track's own menu \u2014 which\n// was the whole problem: a user browsing genes had no way in.\nexport function subgraphTracks(\n pluginManager: PluginManager,\n session: TrackScanSession,\n assemblyName: string,\n view?: LaunchingView,\n) {\n const found: SubgraphTrack[] = []\n for (const track of session.tracks) {\n const assemblyNames: unknown = readConfObject(track, 'assemblyNames')\n const adapter: unknown = readConfObject(track, 'adapter')\n const trackId: unknown = readConfObject(track, 'trackId')\n if (\n Array.isArray(assemblyNames) &&\n assemblyNames.includes(assemblyName) &&\n typeof trackId === 'string' &&\n typeof adapter === 'object' &&\n adapter !== null &&\n 'type' in adapter &&\n adapterCanCutSubgraph(pluginManager, adapter.type)\n ) {\n found.push(subgraphTrack(track, trackId, session, view))\n }\n }\n return found\n}\n", "import type modelFactory from './GraphGenomeView/model'\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\nimport type { SnapshotIn } from '@jbrowse/mobx-state-tree'\n\n// GraphGenomeView has no assembly/track resolution \u2014 every field is a plain\n// persisted view prop, so the launch spec forwards straight onto the view\n// snapshot. `afterAttach` loads `gfaLocation`, or refetches the\n// `loadedTrackId`/`loadedRegion` pair, once the view is created.\n//\n// **Taken off the model rather than listed by hand**, which is the whole point.\n// This named four props (`gfaLocation`, `colorScheme`, `linearLayout`,\n// `drawPaths`) while the extension point spreads whatever it is handed \u2014 so the\n// runtime accepted every prop and the DECLARED contract accepted a third of\n// them. The docs' own figure specs launch with thirteen: `loadedTrackId`,\n// `loadedRegion`, `layoutMode`, `colorDomain`, `paneHeight`, `referencePath`,\n// `layoutQuality`, `maxRegionBp` and `bubbleSpread` among them, none of which\n// this said were allowed. It carries a `#extensionPoint` tag, so the published\n// API docs said so too.\n//\n// Derived, so adding a view prop widens the launch with it. `type` is the\n// dispatch key rather than a setting \u2014 loadSessionSpec strips it before calling\n// this \u2014 and is the one field a caller may not supply.\nexport type LaunchGraphGenomeViewArgs = Partial<\n Omit<SnapshotIn<ReturnType<typeof modelFactory>>, 'type'>\n> & {\n session: AbstractSessionModel\n}\n\ndeclare module '@jbrowse/core/PluginManager' {\n interface ExtensionPointRegistry {\n 'LaunchView-GraphGenomeView': {\n args: LaunchGraphGenomeViewArgs\n result: LaunchGraphGenomeViewArgs\n }\n }\n}\n\nexport default function LaunchGraphGenomeViewF(pluginManager: PluginManager) {\n /** #extensionPoint LaunchView-GraphGenomeView | async | Programmatically launch a graph genome view */\n pluginManager.addToExtensionPoint('LaunchView-GraphGenomeView', args => {\n const { session, ...spec } = args\n session.addView('GraphGenomeView', spec)\n return args\n })\n}\n", "import { lazy } from 'react'\n\nimport DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'\n\nimport { configSchemaFactory } from './configSchema'\nimport { stateModelFactory } from './model'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function LinearGraphDisplayF(pluginManager: PluginManager) {\n pluginManager.addDisplayType(() => {\n const configSchema = configSchemaFactory()\n return new DisplayType({\n name: 'LinearGraphDisplay',\n displayName: 'Graph',\n configSchema,\n stateModel: stateModelFactory(configSchema),\n trackType: ['FeatureTrack', 'SyntenyTrack'],\n viewType: 'LinearGenomeView',\n ReactComponent: lazy(() => import('./components/LinearGraphDisplay')),\n })\n })\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { trackHeightConfigSchemaFields } from '@jbrowse/display-kit/trackHeightConfigSchemaFields'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport { COLOR_SCHEME_VALUES } from '../GraphGenomeView/colorSchemes'\nimport { LAYOUT_MODE_VALUES } from '../GraphGenomeView/layoutModes'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config LinearGraphDisplay\n * The graph drawn as a track of the linear genome view. A layout whose x is\n * reference bp draws under the view's coordinates and re-cuts as the view\n * moves; force, ordered and walk rows draw in their own coordinates, fitted to\n * the track.\n */\nexport function configSchemaFactory() {\n return ConfigurationSchema(\n 'LinearGraphDisplay',\n {\n /**\n * #slot\n * the layout the track opens in\n */\n layoutMode: {\n type: 'stringEnum',\n model: types.enumeration('LayoutMode', LAYOUT_MODE_VALUES),\n defaultValue: 'auto',\n },\n /**\n * #slot\n */\n colorScheme: {\n type: 'stringEnum',\n model: types.enumeration('ColorScheme', COLOR_SCHEME_VALUES),\n defaultValue: 'auto',\n },\n ...trackHeightConfigSchemaFields({\n defaultHeight: 300,\n height:\n 'the most the track takes; a layout shorter than it takes only what it needs',\n }),\n },\n { explicitlyTyped: true, explicitIdentifier: 'displayId' },\n )\n}\n\nexport type LinearGraphDisplayConfigModel = ReturnType<\n typeof configSchemaFactory\n>\nexport type LinearGraphDisplayConfig = Instance<LinearGraphDisplayConfigModel>\n", "import { lazy } from 'react'\n\nimport { ConfigurationReference, getConf } from '@jbrowse/core/configuration'\nimport { BaseDisplay } from '@jbrowse/core/pluggableElementTypes'\nimport { getContainingView, getSession } from '@jbrowse/core/util'\nimport TrackHeightMixin from '@jbrowse/display-kit/TrackHeightMixin'\nimport { addDisposer, types } from '@jbrowse/mobx-state-tree'\nimport SettingsIcon from '@mui/icons-material/Settings'\nimport { autorun } from 'mobx'\n\nimport { COLOR_SCHEMES } from '../GraphGenomeView/colorSchemes'\nimport { isLinearHost } from '../GraphGenomeView/host'\nimport { LAYOUT_MODES } from '../GraphGenomeView/layoutModes'\nimport paneModelFactory from '../GraphGenomeView/model'\n\nimport type { LinearGraphDisplayConfigModel } from './configSchema'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\nconst GraphSettingsDialog = lazy(\n () => import('../GraphGenomeView/components/GraphSettingsDialog'),\n)\n\ntype ConfiguredSlot = 'layoutMode' | 'colorScheme' | 'height'\n\nexport function stateModelFactory(configSchema: LinearGraphDisplayConfigModel) {\n const Pane = paneModelFactory()\n return (\n types\n .compose(\n 'LinearGraphDisplay',\n BaseDisplay,\n TrackHeightMixin(),\n types.model({\n type: types.literal('LinearGraphDisplay'),\n configuration: ConfigurationReference(configSchema),\n // The graph pane, the same model the standalone view is. Here the\n // linear view above is its host: x is that view's window for a layout\n // whose x is reference bp, and the cut is re-made when the window\n // leaves it (pane.host).\n pane: types.optional(Pane, () => ({\n type: 'GraphGenomeView' as const,\n })),\n // Which of the config's slots have reached the pane. Each reaches it\n // once, so a choice made in the track menu survives a reload, and a\n // launch that states a pane prop keeps it over the config's.\n configured: types.optional(\n types.frozen<Partial<Record<ConfiguredSlot, boolean>>>(),\n {},\n ),\n }),\n )\n // A launch names the pane's props without the pane's own type, and what\n // it states is what the config does not set.\n .preProcessSnapshot(snapshot => {\n const { pane, configured } = snapshot as {\n pane?: Record<string, unknown>\n configured?: Partial<Record<ConfiguredSlot, boolean>>\n }\n return pane\n ? ({\n ...snapshot,\n configured: {\n layoutMode: pane.layoutMode !== undefined,\n colorScheme: pane.colorScheme !== undefined,\n height: pane.paneHeight !== undefined,\n ...configured,\n },\n pane:\n pane.type === undefined\n ? { type: 'GraphGenomeView', ...pane }\n : pane,\n } as typeof snapshot)\n : snapshot\n })\n .views(self => ({\n get host() {\n let view: unknown\n try {\n view = getContainingView(self)\n } catch {\n return undefined\n }\n return isLinearHost(view) ? view : undefined\n },\n get trackId(): string {\n return getConf(self.parentTrack, 'trackId')\n },\n }))\n .views(self => ({\n trackMenuItems(): MenuItem[] {\n const { pane } = self\n const { graph } = pane\n const walks = pane.walkChoices\n return [\n {\n label: 'Layout',\n subMenu: LAYOUT_MODES.map(mode => ({\n type: 'radio' as const,\n label: mode.label,\n checked: pane.layoutMode === mode.value,\n disabled: graph ? !mode.available(graph) : false,\n onClick: () => {\n void pane.switchLayout(mode.value)\n },\n })),\n },\n {\n label: 'Color',\n subMenu: COLOR_SCHEMES.map(scheme => ({\n type: 'radio' as const,\n label: scheme.label,\n checked: pane.colorScheme === scheme.value,\n onClick: () => {\n pane.setColorScheme(scheme.value)\n },\n })),\n },\n ...(walks.length > 0\n ? [\n {\n label: 'Walk',\n subMenu: [\n {\n type: 'radio' as const,\n label: 'None',\n checked: pane.highlightedPath === '',\n onClick: () => {\n pane.setHighlightedPath('')\n },\n },\n ...walks.map(walk => ({\n type: 'radio' as const,\n label: walk.label,\n checked: pane.highlightedPath === walk.name,\n onClick: () => {\n pane.setHighlightedPath(walk.name)\n },\n })),\n ],\n },\n ]\n : []),\n ...(pane.hostPlacesX\n ? []\n : [\n {\n label: 'Zoom in',\n onClick: () => {\n pane.zoom(1.5, pane.width / 2, pane.canvasHeight / 2)\n },\n },\n {\n label: 'Zoom out',\n onClick: () => {\n pane.zoom(1 / 1.5, pane.width / 2, pane.canvasHeight / 2)\n },\n },\n {\n label: 'Zoom to fit',\n onClick: () => {\n pane.zoomToFit()\n },\n },\n ]),\n {\n type: 'checkbox',\n label: 'Mark bubbles',\n checked: pane.showBubbles,\n onClick: () => {\n pane.setShowBubbles(!pane.showBubbles)\n },\n },\n {\n type: 'checkbox',\n label: 'Show deletion edges',\n checked: pane.showDeletionEdges,\n onClick: () => {\n pane.setShowDeletionEdges(!pane.showDeletionEdges)\n },\n },\n {\n type: 'checkbox',\n label: 'Genes on the backbone',\n checked: pane.showGenes,\n onClick: () => {\n pane.setShowGenes(!pane.showGenes)\n },\n },\n {\n label: 'Settings',\n icon: SettingsIcon,\n onClick: () => {\n getSession(self).queueDialog(onClose => [\n GraphSettingsDialog,\n { model: pane, open: true, onClose },\n ])\n },\n },\n ]\n },\n }))\n .actions(self => ({\n // The config's slots the pane has not yet taken, then all of them.\n applyConfig() {\n const { pane, configured } = self\n if (!configured.layoutMode) {\n pane.setLayoutMode(getConf(self, 'layoutMode'))\n }\n if (!configured.colorScheme) {\n pane.setColorScheme(getConf(self, 'colorScheme'))\n }\n if (!configured.height) {\n pane.setPaneHeight(getConf(self, 'height'))\n }\n self.configured = {\n layoutMode: true,\n colorScheme: true,\n height: true,\n }\n },\n // The track's height is the pane's ceiling: a layout shorter than it\n // takes only what it needs, and a drag on the handle moves the ceiling.\n resizeHeight(distance: number) {\n const before = self.height\n const target = Math.max(before + distance, 40)\n self.pane.setPaneHeight(target)\n return target - before\n },\n }))\n .actions(self => ({\n afterAttach() {\n const { pane } = self\n self.applyConfig()\n pane.adoptTrack(self.trackId)\n pane.startHosting()\n addDisposer(\n self,\n autorun(\n () => {\n const { host } = self\n if (host) {\n pane.setWidth(host.width)\n pane.pairWithLinearView(host.id)\n }\n },\n { name: 'LinearGraphDisplayHost' },\n ),\n )\n addDisposer(\n self,\n autorun(\n () => {\n const height = pane.canvasHeight\n if (self.height !== height) {\n self.setHeight(height)\n }\n },\n { name: 'LinearGraphDisplayHeight' },\n ),\n )\n },\n }))\n )\n}\n\nexport type LinearGraphDisplayStateModel = ReturnType<typeof stateModelFactory>\nexport type LinearGraphDisplayModel = Instance<LinearGraphDisplayStateModel>\n", "import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'\n\nimport configSchema, { normalizeSnapshot } from './configSchema.ts'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function MinigraphBubbleAdapterF(pluginManager: PluginManager) {\n pluginManager.addAdapterType(\n () =>\n new AdapterType({\n name: 'MinigraphBubbleAdapter',\n displayName: 'Minigraph bubble BED adapter',\n normalizeSnapshot,\n configSchema,\n adapterMetadata: {\n category: 'Graph adapters',\n },\n getAdapterClass: () =>\n import('./MinigraphBubbleAdapter.ts').then(r => r.default),\n }),\n )\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config MinigraphBubbleAdapter\n * #trackType FeatureTrack\n * #fileFormat graph | Minigraph bubble BED | `gfatools bubble` output; HPRC publishes one ready-made, so no preprocessing at all\n * the tabix-indexed BED `gfatools bubble` writes for a minigraph graph: one row\n * per bubble, on the reference's own coordinates. Unlike\n * [RgfaTabixAdapter](/docs/config/rgfatabixadapter) this needs no index build of\n * your own, because HPRC publishes this file beside its minigraph graph.\n *\n * It shows *where* the graph varies and by how much, not the graph itself.\n *\n * #example\n * ```js\n * {\n * type: 'MinigraphBubbleAdapter',\n * uri: 'https://jbrowse.org/demos/hprc/hprc-v2.0-mc-grch38.bubbles.bed.gz',\n * assemblyNameToPanSN: { hg38: 'GRCh38' },\n * }\n * ```\n */\n\nexport function normalizeSnapshot(snap: Record<string, unknown>) {\n const { uri, baseUri, csi } = snap\n return typeof uri === 'string'\n ? {\n ...snap,\n bubblesLocation: { uri, baseUri },\n index: {\n indexType: csi ? 'CSI' : 'TBI',\n location: { uri: `${uri}.${csi ? 'csi' : 'tbi'}`, baseUri },\n },\n }\n : snap\n}\n\nconst MinigraphBubbleAdapter = ConfigurationSchema(\n 'MinigraphBubbleAdapter',\n {\n /**\n * #slot\n */\n bubblesLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.bb.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n index: ConfigurationSchema('MinigraphBubbleIndex', {\n /**\n * #slot index.indexType\n */\n indexType: {\n model: types.enumeration('IndexType', ['TBI', 'CSI']),\n type: 'stringEnum',\n defaultValue: 'TBI',\n },\n /**\n * #slot index.location\n */\n location: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.bb.bed.gz.tbi',\n locationType: 'UriLocation',\n },\n },\n }),\n /**\n * #slot\n * Maps a JBrowse assembly name to its PanSN sample prefix, for when they\n * differ. `gfatools bubble` names each row after the graph's stable\n * sequence, so a Minigraph-Cactus graph yields `GRCh38#0#chr6` and an\n * `hg38` assembly needs `{ hg38: 'GRCh38' }`; HPRC's published v1.0\n * minigraph bubbles are bare (`chr6`) and need nothing.\n */\n assemblyNameToPanSN: {\n type: 'frozen',\n defaultValue: {},\n },\n },\n {\n explicitlyTyped: true,\n\n /**\n * #preProcessSnapshot\n *\n * preprocessor to allow the minimal config\n * ```json\n * { \"type\": \"MinigraphBubbleAdapter\", \"uri\": \"graph.bb.bed.gz\" }\n * ```\n */\n preProcessSnapshot: normalizeSnapshot,\n },\n)\n\nexport type MinigraphBubbleAdapterConfig = Instance<\n typeof MinigraphBubbleAdapter\n>\n\nexport default MinigraphBubbleAdapter\n", "import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'\n\nimport configSchema, { normalizeSnapshot } from './configSchema.ts'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function RgfaTabixAdapterF(pluginManager: PluginManager) {\n pluginManager.addAdapterType(\n () =>\n new AdapterType({\n name: 'RgfaTabixAdapter',\n displayName: 'Indexed rGFA adapter',\n normalizeSnapshot,\n configSchema,\n // what the launch menus look for (launchSubgraph/subgraphTracks.ts)\n adapterCapabilities: ['getSubgraph'],\n adapterMetadata: {\n category: 'Graph adapters',\n },\n getAdapterClass: () =>\n import('./RgfaTabixAdapter.ts').then(r => r.default),\n }),\n )\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\n/**\n * #config RgfaTabixAdapter\n * #trackType FeatureTrack\n * #fileFormat graph | Indexed rGFA | Built by `scripts/build_rgfa_tabix.sh`; serves segments as features and extracts local subgraphs\n * two tabix-indexed BED projections of an rGFA (minigraph). rGFA tags every\n * segment with `SN`/`SO`/`SR`, so both files record coordinates the graph\n * already states rather than inventing any. Plain GFA (pggb, odgi,\n * Minigraph-Cactus) has no such tags and is not supported.\n *\n * The `uri` shorthand takes the prefix the build script was given and resolves\n * `<uri>.segs.bed.gz`, `<uri>.links.bed.gz` and their `.tbi` indexes. `coarse`\n * takes the same shorthand for a second pair at one node per bubble, built by\n * `build_bubble_tier.sh` in jbrowse-components, plus the zoom past which a\n * graph view following a linear view cuts it.\n *\n * #example\n * ```js\n * {\n * type: 'RgfaTabixAdapter',\n * uri: 'https://example.com/hprc.rgfa',\n * coarse: { uri: 'https://example.com/hprc.tier10000', aboveBpPerPx: 1000 },\n * }\n * ```\n */\n\nfunction tbi(uri: string, baseUri: unknown, csi: unknown) {\n return {\n indexType: csi ? 'CSI' : 'TBI',\n location: { uri: `${uri}.${csi ? 'csi' : 'tbi'}`, baseUri },\n }\n}\n\nfunction prefixLocations(snap: Record<string, unknown>) {\n const { uri, baseUri, csi } = snap\n return typeof uri === 'string'\n ? {\n ...snap,\n segmentsLocation: { uri: `${uri}.segs.bed.gz`, baseUri },\n segmentsIndex: tbi(`${uri}.segs.bed.gz`, baseUri, csi),\n linksLocation: { uri: `${uri}.links.bed.gz`, baseUri },\n linksIndex: tbi(`${uri}.links.bed.gz`, baseUri, csi),\n }\n : snap\n}\n\nexport function normalizeSnapshot(snap: Record<string, unknown>) {\n const { coarse, baseUri } = snap\n return prefixLocations(\n typeof coarse === 'object' && coarse !== null\n ? { ...snap, coarse: prefixLocations({ baseUri, ...coarse }) }\n : snap,\n )\n}\n\nconst indexSchema = (name: string, defaultUri: string) =>\n ConfigurationSchema(name, {\n /**\n * #slot\n */\n indexType: {\n model: types.enumeration('IndexType', ['TBI', 'CSI']),\n type: 'stringEnum',\n defaultValue: 'TBI',\n },\n /**\n * #slot\n */\n location: {\n type: 'fileLocation',\n defaultValue: { uri: defaultUri, locationType: 'UriLocation' },\n },\n })\n\nconst RgfaTabixCoarseTier = ConfigurationSchema('RgfaTabixCoarseTier', {\n /**\n * #slot\n * The linear view's bp per px past which a graph following it cuts this\n * pair. Unset means the track has no coarse tier. There is no default,\n * because the zoom a fine cut stays drawable to depends on the graph's\n * density: a minigraph graph averages ~7 kb per segment, a base-level pggb\n * graph ~17 bp.\n */\n aboveBpPerPx: { type: 'maybeNumber' },\n /**\n * #slot\n */\n segmentsLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.tier.segs.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n segmentsIndex: indexSchema(\n 'RgfaCoarseSegmentsIndex',\n '/path/to/graph.tier.segs.bed.gz.tbi',\n ),\n /**\n * #slot\n */\n linksLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.tier.links.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n linksIndex: indexSchema(\n 'RgfaCoarseLinksIndex',\n '/path/to/graph.tier.links.bed.gz.tbi',\n ),\n})\n\nconst RgfaTabixAdapter = ConfigurationSchema(\n 'RgfaTabixAdapter',\n {\n /**\n * #slot\n * one row per segment: `stableName start end segmentId rank`, as emitted by\n * `gfatools gfa2bed -m`\n */\n segmentsLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.segs.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n segmentsIndex: indexSchema(\n 'RgfaSegmentsIndex',\n '/path/to/graph.segs.bed.gz.tbi',\n ),\n /**\n * #slot\n * one row per link per endpoint, each carrying both endpoints in full so a\n * neighbour on another stable sequence needs no segment-id lookup\n */\n linksLocation: {\n type: 'fileLocation',\n defaultValue: {\n uri: '/path/to/graph.links.bed.gz',\n locationType: 'UriLocation',\n },\n },\n /**\n * #slot\n */\n linksIndex: indexSchema(\n 'RgfaLinksIndex',\n '/path/to/graph.links.bed.gz.tbi',\n ),\n /**\n * #slot\n * Maps a JBrowse assembly name to its PanSN sample prefix in the graph, for\n * when they differ. Defaults to identity: the assembly name is assumed to be\n * the PanSN sample name, and a graph whose stable names are bare (minigraph\n * writes `chr1`) needs nothing here at all.\n *\n * Minigraph-Cactus writes PanSN stable names, so HPRC's release-2 graph\n * calls the reference `GRCh38#0#chr1` and an `hg38` assembly needs\n * `{ hg38: 'GRCh38' }`. The prefix is what disambiguates: that same graph\n * also contains `CHM13#0#chr1`, so matching on the contig alone would\n * silently resolve to the wrong sample.\n */\n assemblyNameToPanSN: {\n type: 'frozen',\n defaultValue: {},\n },\n /**\n * #slot\n * The same graph at one node per bubble, which a graph view following a\n * linear view cuts once that view is zoomed out past `aboveBpPerPx`, and\n * which no bp cap applies to. `{ uri, aboveBpPerPx }`, where `uri` is a\n * prefix as the adapter's own is.\n */\n coarse: RgfaTabixCoarseTier,\n },\n {\n explicitlyTyped: true,\n\n /**\n * #preProcessSnapshot\n *\n * preprocessor to allow the minimal config\n * ```json\n * { \"type\": \"RgfaTabixAdapter\", \"uri\": \"graph.rgfa\" }\n * ```\n */\n preProcessSnapshot: normalizeSnapshot,\n },\n)\n\nexport type RgfaTabixAdapterConfig = Instance<typeof RgfaTabixAdapter>\n\nexport default RgfaTabixAdapter\n", "import { getSession } from '@jbrowse/core/util'\nimport { alpha } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport { graphViewHighlights } from './graphViewHighlights'\n\nimport type { HighlightRegion } from './graphViewHighlights'\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\nconst useStyles = makeStyles()(theme => ({\n highlight: {\n height: '100%',\n position: 'absolute',\n background: alpha(theme.palette.primary.main, 0.25),\n borderLeft: `2px solid ${theme.palette.primary.main}`,\n borderRight: `2px solid ${theme.palette.primary.main}`,\n pointerEvents: 'none',\n zIndex: 10,\n },\n}))\n\n// One band over the linear view for a hovered graph node. `getHighlightCoords`\n// is the LGV's own projection \u2014 it canonicalizes the refName and floors the\n// width to 3 px, so a pure insertion (a zero-length reference span) still shows\n// as a mark rather than vanishing.\nconst Highlight = observer(function Highlight({\n model,\n region,\n}: {\n model: LinearGenomeViewModel\n region: HighlightRegion\n}) {\n const { classes } = useStyles()\n const coords = model.getHighlightCoords(region)\n return coords ? (\n <div\n data-testid=\"graph-node-highlight\"\n className={classes.highlight}\n style={{ left: coords.left, width: coords.width }}\n />\n ) : null\n})\n\nconst GraphNodeHighlight = observer(function GraphNodeHighlight({\n model,\n}: {\n model: LinearGenomeViewModel\n}) {\n const { views } = getSession(model)\n return (\n <>\n {graphViewHighlights([...views], model.id, model.assemblyNames).map(\n ({ key, region }) => (\n <Highlight key={`graph-hover-${key}`} model={model} region={region} />\n ),\n )}\n </>\n )\n})\n\nexport default GraphNodeHighlight\n", "import { withRows } from '../launchFromGraph/linearViewTarget'\n\nexport interface HighlightRegion {\n refName: string\n start: number\n end: number\n assemblyName?: string\n}\n\nexport interface GraphViewHighlight {\n key: string\n region: HighlightRegion\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\nfunction readRegion(value: unknown): HighlightRegion | undefined {\n let region: HighlightRegion | undefined\n if (isRecord(value)) {\n const { refName, start, end, assemblyName } = value\n if (\n typeof refName === 'string' &&\n typeof start === 'number' &&\n typeof end === 'number'\n ) {\n region = {\n refName,\n start,\n end,\n assemblyName:\n typeof assemblyName === 'string' ? assemblyName : undefined,\n }\n }\n }\n return region\n}\n\n// Whether a graph view's highlights belong on a given linear view.\n// `connectedViewId` is written by the launch menu, so a launched pair is\n// explicit. A graph view without one \u2014 a hand-written session snapshot, or\n// `Add > Graph genome view` followed by a subgraph load \u2014 matches any linear\n// view, because the alternative is silently drawing nothing.\n//\n// So does one whose linear view has been closed. Held to an id nothing answers\n// to, it matched no view and its hover drew nowhere for the rest of the\n// session, the view it next opened included.\nfunction isConnected(\n view: Record<string, unknown>,\n linearViewId: string,\n liveViewIds: ReadonlySet<unknown>,\n) {\n const connectedViewId = view.connectedViewId\n return (\n connectedViewId === undefined ||\n connectedViewId === linearViewId ||\n !liveViewIds.has(connectedViewId)\n )\n}\n\n// ...and whether the view can mean anything by it. `getHighlightCoords`\n// canonicalizes a refName against the region's OWN assembly and then lays it\n// out against this view's displayed regions, without ever asking whether the\n// two are the same assembly \u2014 so a band handed to the wrong view is drawn, at\n// coordinates that belong to somebody else's genome.\n//\n// That is not hypothetical for an unpaired graph, which by the rule above\n// matches every linear view in the session. A synteny stack's rows are LGVs on\n// different assemblies, and a PanSN name reduces to a bare contig: the five\n// E. coli strains each have one refName `chr`, so a graph cut from K12 painted\n// its K12 interval across Sakai's row and CFT073's at their own offsets. With\n// the assembly checked the reference row keeps the band and the rest correctly\n// draw nothing.\n//\n// A region stating no assembly still passes \u2014 `readRegion` leaves it optional,\n// and the fallback is the behaviour every existing snapshot has.\nfunction isOnAssembly(\n region: HighlightRegion,\n assemblyNames: string[] | undefined,\n) {\n return (\n region.assemblyName === undefined ||\n assemblyNames === undefined ||\n assemblyNames.includes(region.assemblyName)\n )\n}\n\n// The highlights a linear view should draw for the graph views connected to it.\n// Reads `session.views` structurally: the members it needs are declared by\n// GraphGenomeView, not by the AbstractViewModel the session array is typed as.\nexport function graphViewHighlights(\n views: unknown[],\n linearViewId: string,\n // the drawing view's own assemblies, so a band is only drawn where its\n // coordinates mean something \u2014 see isOnAssembly\n linearAssemblyNames?: string[],\n): GraphViewHighlight[] {\n const highlights: GraphViewHighlight[] = []\n const liveViewIds = new Set(\n withRows(views).map(view => (isRecord(view) ? view.id : undefined)),\n )\n for (const view of views) {\n if (\n isRecord(view) &&\n view.type === 'GraphGenomeView' &&\n isConnected(view, linearViewId, liveViewIds)\n ) {\n const region = readRegion(view.hoverHighlight)\n if (region && isOnAssembly(region, linearAssemblyNames)) {\n highlights.push({\n key: typeof view.id === 'string' ? view.id : 'graph',\n region,\n })\n }\n }\n }\n return highlights\n}\n", "import GraphNodeHighlight from './GraphNodeHighlight'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\n// The graph-to-linear half of the hover sync. The reverse half needs no mount\n// point: an LGV already publishes its hover to `session.hovered`, and the graph\n// view's own autorun reads it (see GraphGenomeView/model.ts).\n//\n// Mounted through the extension point rather than imported by the LGV, so this\n// plugin never imports @jbrowse/plugin-linear-genome-view at runtime: it is not\n// one of JBrowse's shared globals, so a value import would bundle a second copy\n// of that whole plugin.\nconst POINT = 'LinearGenomeView-TracksContainerComponent'\n\nexport default function GraphHoverSyncF(pluginManager: PluginManager) {\n pluginManager.contributeToExtensionPoint(POINT, ({ model }) => (\n <GraphNodeHighlight key=\"graphgenomeview-hover-highlight\" model={model} />\n ))\n}\n", "// The name JBrowse knows this plugin by, which a config's `plugins` entry\n// states beside its `esmUrl`.\nexport const PLUGIN_NAME = 'GraphGenomeView'\n", "export const version = '4.0.2'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["require_Plugin", "__commonJSMin", "exports", "module", "require_AdapterType", "__commonJSMin", "exports", "module", "require_configuration", "__commonJSMin", "exports", "module", "require_dataAdapterCache", "__commonJSMin", "exports", "module", "require_pluggableElementTypes", "__commonJSMin", "exports", "module", "require_ViewType", "__commonJSMin", "exports", "module", "require_withLaunchInput", "__commonJSMin", "exports", "module", "require_models", "__commonJSMin", "exports", "module", "require_RenderLifecycleMixin", "__commonJSMin", "exports", "module", "require_mobx", "__commonJSMin", "exports", "module", "require_DisplayType", "__commonJSMin", "exports", "module", "require_trackHeightConfigSchemaFields", "__commonJSMin", "exports", "module", "require_TrackHeightMixin", "__commonJSMin", "exports", "module", "import_Plugin", "import_util", "import_jsx_runtime", "BubbleChart_default", "_jsx", "import_AdapterType", "import_configuration", "import_mobx_state_tree", "GbzBaseSyntenyAdapter", "snap", "configSchema_default", "GbzBaseSyntenyAdapterF", "pluginManager", "AdapterType", "configSchema_default", "r", "import_dataAdapterCache", "import_pluggableElementTypes", "isSubgraphAdapter", "adapter", "GetSubgraph", "args", "adapterConfig", "region", "sessionId", "opts", "signal", "dataAdapter", "import_react", "import_pluggableElementTypes", "import_mobx_state_tree", "GraphAddTrackWidget", "GraphAddTrackWorkflowF", "pluginManager", "import_pluggableElementTypes", "cached", "loadBandage", "mod", "GraphComputeLayout", "args", "graph", "options", "statusCallback", "module", "loadBandage", "startTime", "result", "duration", "import_react", "import_ViewType", "import_withLaunchInput", "import_configuration", "import_models", "import_ui", "import_util", "import_io", "import_mobx_state_tree", "import_RenderLifecycleMixin", "import_canvas2dUtils", "import_mobx", "WHOLE_FRACTION", "NAMED_WALKS", "MIN_CHIPPED_BP", "bubbleHalos", "graph", "bubbles", "positions", "walkLabel", "name", "byName", "n", "halos", "bubble", "parts", "top", "nodeIds", "ends", "bubbleSegmentIds", "node", "line", "isBackbone", "svgPath", "p", "anchor", "s", "sharing", "route", "id", "routes", "rarest", "at", "far", "d", "routeText", "classifyBubble", "names", "shown", "more", "formatBp", "bubblesFromGraph", "graph", "isBackbone", "byId", "n", "order", "referenceOrder", "indexOf", "id", "i", "reach", "succ", "linked", "e", "a", "lo", "hi", "from", "to", "backbone", "b", "ends", "boundaries", "node", "ia", "ib", "walkIndex", "at", "bubbles", "start", "i0", "end", "i1", "interior", "best", "cur", "t", "add", "prev", "SATURATED_PATH_COUNT", "walked", "walkRoutes", "crossed", "routes", "walksLeave", "refStart", "refEnd", "chain", "chainBroken", "k", "partial", "fallback", "startId", "endId", "seen", "min", "max", "left", "p", "steps", "bp", "key", "route", "bubbleSubgraph", "graph", "segmentIds", "keep", "nodes", "n", "ids", "paths", "p", "id", "e", "GENE_ADAPTER_TYPES", "GENE_TRACK_HINT", "pickGeneTrack", "tracks", "named", "t", "candidates", "field", "f", "name", "exonsOf", "into", "children", "child", "merged", "intervals", "sorted", "a", "b", "out", "iv", "last", "geneModelsFrom", "features", "genes", "start", "end", "exons", "contig", "name", "genePins", "graph", "genes", "positions", "byContig", "contigOf", "node", "isBackbone", "refName", "entry", "nodes", "a", "b", "pins", "gene", "backbone", "parts", "at", "atDistance", "covered", "mid", "reach", "i", "firstNodeAtOrAfter", "nodeStart", "nodeEnd", "line", "exon", "stretch", "polylineSlice", "svgPath", "pinBp", "distance", "p", "parseTag", "tag", "tags", "nameEnd", "typeEnd", "name", "type", "val", "parseTags", "fields", "field", "stableCoordinate", "node", "SN", "SO", "SR", "GT", "LT", "parseWalkBody", "body", "segments", "strand", "start", "push", "end", "i", "ch", "parseGFA", "file", "graph", "rawLine", "line", "rest", "len", "seq", "tagfields", "gfa1", "first", "source", "target", "cigar", "strand1", "strand2", "sample", "hap", "contig", "path", "surveySegments", "gfaGraph", "canonical", "traversals", "claim", "id", "strand", "traverse", "link", "gfaPath", "segment", "walk", "seg", "anchorablePaths", "p", "pathOrigin", "w", "s", "carriedSamples", "gfaNode", "sm", "samples", "positive", "v", "statedDepth", "tags", "length", "count", "makeNode", "depth", "stableCoordinate", "convertGFAToGraph", "name", "nodes", "edges", "nodeId", "segmentId", "paths", "edgeToPathsMap", "edgeKey", "a", "recordPathEdges", "nodeIds", "i", "key", "edge", "pathIds", "lengths", "n", "anchoring", "surveyPaths", "isLinearHost", "view", "v", "widest", "blocks", "best", "b", "hostFrame", "region", "block", "bpPerPx", "hostWindow", "displayed", "hostCut", "window", "capBp", "visible", "margin", "cutHolds", "cut", "import_configuration", "import_mobx_state_tree", "OFF_REFERENCE_COLOR", "referencePositionColor", "start", "end", "hue", "isLaneView", "view", "sourceLaneDisplay", "views", "connectedViewId", "trackId", "display", "t", "paintSourceLane", "color", "current", "orientToReference", "graph", "positions", "backbone", "node", "isBackbone", "points", "segments", "mid", "weight", "sum", "p", "cx", "cy", "cr", "sxr", "syr", "angle", "c", "s", "out", "id", "x", "y", "below", "above", "LANE_GAP", "referenceSeeds", "graph", "scaling", "opts", "drawn", "node", "drawnNodeLength", "drawnOf", "id", "backbone", "isBackbone", "a", "b", "seeds", "x", "adjacent", "buildNeighbors", "depth", "queue", "at", "next", "neighbour", "unreached", "seededNodes", "REPEAT_ADAPTER_TYPES", "REPEAT_TRACK_HINT", "pickRepeatTrack", "tracks", "named", "t", "own", "f", "name", "PACKED_NAME", "packedName", "pair", "i", "field", "top", "MOTIF_FIELDS", "PERIOD_FIELDS", "NAME_FIELDS", "first", "names", "value", "one", "IUPAC", "repeatUnitOf", "period", "motif", "numbers", "n", "callsOf", "samples", "called", "sample", "fields", "reads", "alleles", "bp", "repeatArraysFrom", "features", "arrays", "start", "end", "unit", "refName", "stated", "a", "b", "walkHighlight", "graph", "name", "path", "p", "byId", "n", "edgeAt", "e", "i", "nodeIds", "edgeIndexes", "a", "b", "index", "bpOf", "ids", "sum", "id", "reference", "pathOrigin", "isRecord", "value", "isFeatureLike", "hoveredName", "feature", "name", "readLgvHover", "hovered", "result", "refName", "coord", "coord0", "assemblyName", "hoverInRegion", "hover", "region", "nodeForLgvHover", "nodes", "byName", "byCoord", "node", "isBackbone", "import_configuration", "isRecord", "value", "isLinearView", "view", "withRows", "views", "linearViewTarget", "connectedViewId", "assemblyName", "candidates", "reportFailure", "session", "what", "launch", "error", "MIN_LAUNCH_SPAN_BP", "paddedLocation", "loc", "minSpan", "pad", "withReferenceRegion", "contributors", "region", "c", "showInLinearView", "location", "assembly", "connectedViewId", "tracks", "existing", "linearViewTarget", "viewId", "locLabel", "locString", "highlightInLinearView", "target", "assemblyNamesOfTrack", "trackId", "track", "t", "names", "n", "launchSyntenyView", "graphTrackId", "graphTrackAssemblies", "aligned", "covered", "panels", "snapshot", "import_configuration", "ANNOTATION_TRACK_TYPE", "launchTracks", "session", "assemblyName", "first", "found", "track", "type", "assemblyNames", "trackId", "import_configuration", "import_tracks", "SYNTENY_TRACK_TYPE", "launchableSyntenyTracks", "session", "assemblyNames", "wanted", "found", "track", "type", "trackAssemblies", "trackId", "coverage", "name", "a", "b", "import_configuration", "import_tracks", "isStringArray", "value", "item", "graphReferenceAssembly", "track", "assemblyNames", "isStringArray", "offReferenceProblem", "referenceAssembly", "assemblyName", "MAX_CANVAS_HEIGHT", "MIN_CANVAS_HEIGHT", "VARIANT_MAP_HEIGHT", "SEGMENTS_SUFFIX", "bubblePrefix", "adapterConfig", "uri", "FIT_PADDING", "HOVER_BRIGHTEN", "SELECT_BRIGHTEN", "VIEWPORT_DEBOUNCE_MS", "VIEWPORT_PANES_BUILT", "dependOn", "_values", "MAX_GRAPH_REGION_BP", "formatSpanBp", "bp", "DEFAULT_MAX_GRAPH_NODES", "MIN_ZOOM", "MAX_ZOOM", "clampZoom", "zoom", "axisScaleOf", "scale", "pixelRows", "viewportOf", "model", "padded", "v", "panes", "w", "h", "fittedTranslateY", "bounds", "usableHeight", "yScale", "leftoverY", "contains", "outer", "inner", "forceLayouts", "FORCE_LAYOUT_CACHE_SIZE", "forceLayoutsOf", "graph", "cache", "inheritForceLayouts", "from", "to", "remember", "key", "result", "stateModelFactory", "LAYOUT_MODE_VALUES", "COLOR_SCHEME_VALUES", "BUBBLE_SPREAD_VALUES", "NODE_WIDTH_VALUES", "self", "m", "modeUsesLayoutEngine", "n", "paths", "pathColorsLegible", "pathLegend", "walkHighlight", "bubblesFromGraph", "track", "t", "region", "GENE_ADAPTER_TYPES", "REPEAT_ADAPTER_TYPES", "pickRepeatTrack", "r", "buildNeighbors", "contigThickness", "nodeWidth", "nodeById", "mean", "meanDepth", "maxNodeWidthPx", "id", "node", "nodeWidthPx", "deletionEdges", "repeat", "bars", "walkRows", "samples", "rows", "b", "withCalls", "row", "i", "bubble", "classifyBubble", "positions", "genePins", "labels", "c", "bubbleHalos", "name", "computeReferenceRamp", "minX", "minY", "maxX", "maxY", "segments", "seg", "extent", "walkRowsExtent", "usableWidth", "ceiling", "floor", "pickGeneTrack", "d", "ramp", "nodeId", "neighbors", "span", "nodeReferenceSpan", "backbone", "backboneNodes", "contributingAssemblies", "backboneSpan", "assemblyManager", "panSN", "byPrefix", "asm", "prefix", "loaded", "sample", "resolveContributors", "withReferenceRegion", "linearViewTarget", "own", "nodeOwnLocation", "ownAssembly", "resolveLocationAssembly", "onReference", "paddedLocation", "above", "view", "isLinearHost", "host", "layout", "loadedRegion", "hostFrame", "error", "message", "ms", "strokeCount", "built", "quality", "linear", "mode", "reanchored", "anchorFromPaths", "draw", "show", "scheme", "spread", "width", "trackId", "px", "hops", "haplotypes", "viewId", "paired", "withRows", "edgeIdx", "panning", "session", "s", "tx", "ty", "factor", "centerX", "centerY", "newScale", "ratio", "fitX", "fitY", "translateX", "engaging", "scheduleViewportDirty", "requestPositionsDirtyFrame", "dx", "dy", "loadController", "callLayout", "scaling", "rpcManager", "sessionId", "anchored", "isBackbone", "seededNodes", "status", "forceLayoutKey", "computeLayout", "start", "local", "layoutModeByValue", "hit", "spreadFor", "merged", "mergeRuns", "duration", "layoutScaling", "drawn", "drawnNodeLength", "splitRuns", "oriented", "orientToReference", "liveRequest", "layoutInto", "request", "isLive", "signal", "computed", "e", "live", "parseAndLayout", "text", "keepSelection", "gfaGraph", "parseGFA", "anchorGraph", "convertGFAToGraph", "selected", "liveLoad", "beginLoad", "load", "loadedTrack", "loadBubbles", "features", "f", "trackFeatures", "what", "config", "loadGenes", "geneModelsFrom", "loadRepeats", "repeatArraysFrom", "doSubgraphLoad", "opts", "regionSize", "refusal", "offReferenceProblem", "graphReferenceAssembly", "fetchStart", "gfaText", "label", "locLabel", "cutFromLoadedTrack", "coarse", "loadWholeGFA", "source", "location", "stated", "sub", "bubbleSubgraph", "bubbleSegmentIds", "BUBBLE_KIND_NAMES", "seen", "tier", "cap", "visible", "cutHolds", "hostCut", "places", "frame", "a", "blocks", "hostWindow", "backend", "domain", "display", "sourceLaneDisplay", "paintSourceLane", "referencePositionColor", "hover", "readLgvHover", "hoverInRegion", "nodeForLgvHover", "firstViewport", "viewport", "hoveredNode", "hoveredEdge", "selectedNode", "nodes", "geometryStart", "viewportBounds", "batch", "buildGeometry", "dpr", "target", "showInLinearView", "launchTracks", "highlightInLinearView", "launchSyntenyView", "launchableSyntenyTracks", "items", "item", "graphLaunchMenuItems", "graphLaunchKeys", "GraphGenomeViewF", "pluginManager", "ViewType", "stateModelFactory", "LaunchGraphGenomeViewF", "pluginManager", "args", "session", "spec", "import_react", "import_DisplayType", "import_configuration", "import_trackHeightConfigSchemaFields", "import_mobx_state_tree", "configSchemaFactory", "LAYOUT_MODE_VALUES", "COLOR_SCHEME_VALUES", "import_react", "import_configuration", "import_pluggableElementTypes", "import_util", "import_TrackHeightMixin", "import_mobx_state_tree", "import_mobx", "GraphSettingsDialog", "stateModelFactory", "configSchema", "Pane", "TrackHeightMixin", "snapshot", "pane", "configured", "self", "view", "isLinearHost", "graph", "walks", "LAYOUT_MODES", "mode", "COLOR_SCHEMES", "scheme", "walk", "Settings_default", "onClose", "distance", "before", "target", "host", "height", "LinearGraphDisplayF", "pluginManager", "configSchema", "configSchemaFactory", "DisplayType", "stateModelFactory", "import_AdapterType", "import_configuration", "import_mobx_state_tree", "normalizeSnapshot", "snap", "uri", "baseUri", "csi", "MinigraphBubbleAdapter", "configSchema_default", "MinigraphBubbleAdapterF", "pluginManager", "AdapterType", "normalizeSnapshot", "configSchema_default", "r", "import_AdapterType", "import_configuration", "import_mobx_state_tree", "tbi", "uri", "baseUri", "csi", "prefixLocations", "snap", "normalizeSnapshot", "coarse", "indexSchema", "name", "defaultUri", "RgfaTabixCoarseTier", "RgfaTabixAdapter", "configSchema_default", "RgfaTabixAdapterF", "pluginManager", "AdapterType", "normalizeSnapshot", "configSchema_default", "r", "import_util", "import_material", "import_mobx_react", "import_mui", "isRecord", "value", "readRegion", "region", "refName", "start", "end", "assemblyName", "isConnected", "view", "linearViewId", "liveViewIds", "connectedViewId", "isOnAssembly", "assemblyNames", "graphViewHighlights", "views", "linearAssemblyNames", "highlights", "withRows", "import_jsx_runtime", "useStyles", "theme", "Highlight", "model", "region", "classes", "coords", "GraphNodeHighlight", "views", "graphViewHighlights", "key", "GraphNodeHighlight_default", "import_jsx_runtime", "POINT", "GraphHoverSyncF", "pluginManager", "model", "GraphNodeHighlight_default", "PLUGIN_NAME", "version", "GraphGenomeViewPlugin", "Plugin", "PLUGIN_NAME", "version", "pluginManager", "GraphGenomeViewF", "LinearGraphDisplayF", "LaunchGraphGenomeViewF", "GraphHoverSyncF", "RgfaTabixAdapterF", "MinigraphBubbleAdapterF", "GbzBaseSyntenyAdapterF", "GraphAddTrackWorkflowF", "GraphComputeLayout", "GetSubgraph", "BubbleChart_default", "session", "e"]
|
|
7
7
|
}
|