jbrowse-plugin-graphgenomeviewer 4.0.8 → 4.0.9
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/jbrowse-plugin-graphgenomeviewer.esm.js +1 -1
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +2 -2
- package/package.json +1 -1
- package/src/GraphGenomeView/tubeMap/mismatches.test.ts +146 -0
- package/src/GraphGenomeView/tubeMap/mismatches.ts +84 -0
- package/src/LinearGraphDisplay/LinearGraphDisplay.test.ts +63 -2
- package/src/LinearGraphDisplay/index.ts +3 -1
- package/src/version.ts +1 -1
|
@@ -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:@jbrowse/core/pluggableElementTypes/DisplayType", "global-externals:@jbrowse/display-kit/trackHeightConfigSchemaFields", "global-externals:@jbrowse/display-kit/TrackHeightMixin", "global-externals:@jbrowse/render-core/displayPhase", "../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/viewModel.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/GraphGenomeView/host.ts", "../src/gfa-core/gfaParser.ts", "../src/GraphGenomeView/gfa/gfaConverter.ts", "../src/GraphGenomeView/layout/orientToReference.ts", "../src/GraphGenomeView/layout/referenceSeeds.ts", "../src/GraphGenomeView/pipeline.ts", "../src/GraphGenomeView/repeats/repeatFeatures.ts", "../src/GraphGenomeView/tubeMap/warp.ts", "../src/GraphGenomeView/tubeMap/frame.ts", "../src/GraphGenomeView/viewport.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/graphTrackConfig.ts", "../src/GraphTrack/index.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/graphTrackDefaults/index.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[\"@jbrowse/core/pluggableElementTypes/DisplayType\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/trackHeightConfigSchemaFields\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/TrackHeightMixin\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/displayPhase\"];", "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 GraphTrackF from './GraphTrack/index'\nimport LaunchGraphGenomeViewF from './LaunchGraphGenomeView'\nimport LinearGraphDisplayF from './LinearGraphDisplay/index'\nimport MinigraphBubbleAdapterF from './MinigraphBubbleAdapter/index'\nimport RgfaTabixAdapterF from './RgfaTabixAdapter/index'\nimport GraphTrackDefaultsF from './graphTrackDefaults/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 GraphTrackF(pluginManager)\n LaunchGraphGenomeViewF(pluginManager)\n GraphHoverSyncF(pluginManager)\n RgfaTabixAdapterF(pluginManager)\n MinigraphBubbleAdapterF(pluginManager)\n GbzBaseSyntenyAdapterF(pluginManager)\n GraphAddTrackWorkflowF(pluginManager)\n GraphTrackDefaultsF(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 display's subgraph.\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 GraphTrack\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 SubgraphRegion {\n refName: string\n assemblyName: string\n start: number\n end: number\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 the pane's cutSubgraph.\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 { EngineRequest } from './GraphGenomeView/pipeline'\nimport type { LayoutResult } from './GraphGenomeView/types'\nimport type { RpcExecuteArgs } from '@jbrowse/core/rpc/RpcRegistry'\n\n// The payload is the pipeline's EngineRequest, the same request a standalone\n// page hands the engine. `sessionId` and `statusCallback` are the CALL's, not\n// the payload's: core's `EntriesDeclaringCallLevelFields` fails the build for a\n// registry entry that declares either. `execute` still receives both, through\n// `RpcExecuteArgs`'s intersection with `RpcCallContext`.\n\ndeclare module '@jbrowse/core/rpc/RpcRegistry' {\n interface RpcRegistry {\n GraphComputeLayout: {\n args: EngineRequest\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 './viewModel'\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 { getSession } from '@jbrowse/core/util'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport { GraphPaneMixin, MAX_GRAPH_REGION_BP, formatSpanBp } from './model'\nimport {\n graphReferenceAssembly,\n offReferenceProblem,\n} from '../graphTrackConfig'\n\nimport type { SubgraphRegion } from '../GetSubgraph'\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\nimport type { FileLocation } from '@jbrowse/core/util/types'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\nexport default function stateModelFactory() {\n return types\n .compose(\n 'GraphGenomeView',\n BaseViewModel,\n GraphPaneMixin(),\n types.model({\n type: types.literal('GraphGenomeView'),\n // a whole GFA the view loads on attach, kept so the source round-trips\n // through a session snapshot\n gfaLocation: types.maybe(types.frozen<FileLocation>()),\n // the reference window the file or the cut was stated beside\n loadedRegion: types.maybe(types.frozen<SubgraphRegion>()),\n // A session track the view cuts `loadedRegion` from once, as 4.0\n // sessions and the docs' figure specs state it. Following the linear\n // view is the track's job (LinearGraphDisplay).\n loadedTrackId: types.optional(types.string, ''),\n subgraphContext: types.optional(types.number, 1),\n subgraphHaplotypes: types.maybe(types.frozen<string[]>()),\n maxRegionBp: types.optional(types.number, MAX_GRAPH_REGION_BP),\n }),\n )\n .views(self => ({\n get rpcSessionId() {\n return self.id\n },\n // a view is not its own containing view\n get paneWidth() {\n return self.width\n },\n get sourceTrack(): AnyConfigurationModel | undefined {\n return self.loadedTrackId\n ? getSession(self).tracks.find(t => t.trackId === self.loadedTrackId)\n : undefined\n },\n get canRetryLoad() {\n return (\n self.gfaLocation !== undefined ||\n (self.loadedTrackId !== '' && self.loadedRegion !== undefined)\n )\n },\n }))\n .views(self => ({\n get hasPendingSource() {\n return self.canRetryLoad && !self.graph && !self.loadCanceled\n },\n menuItems() {\n return self.launchMenuItems()\n },\n }))\n .actions(self => {\n const clearPane = self.clearGraph\n return {\n // the stated source goes too, or a reloaded session would load the\n // dismissed graph again\n clearGraph() {\n clearPane()\n self.gfaLocation = undefined\n self.loadedRegion = undefined\n self.loadedTrackId = ''\n },\n load() {\n const region = self.loadedRegion\n if (self.gfaLocation) {\n return self.loadGFAFromLocation(self.gfaLocation, region)\n }\n if (!self.loadedTrackId || !region) {\n return\n }\n const track = self.sourceTrack\n const span = region.end - region.start\n const problem = track\n ? offReferenceProblem(\n graphReferenceAssembly(track),\n region.assemblyName,\n )\n : undefined\n if (!track) {\n self.setError(\n new Error(\n `The track this graph was cut from, \"${self.loadedTrackId}\", is not in this session`,\n ),\n )\n } else if (problem) {\n self.setError(new Error(problem))\n } else if (span > self.maxRegionBp) {\n self.setError(\n new Error(\n `Region too large (${formatSpanBp(span)}) \u2014 zoom in to view graph (max ${formatSpanBp(self.maxRegionBp)})`,\n ),\n )\n } else {\n return self.cutSubgraph(readConfObject(track, 'adapter'), region, {\n hops: self.subgraphContext,\n haplotypes: self.subgraphHaplotypes,\n })\n }\n return undefined\n },\n }\n })\n .actions(self => ({\n retryLoad() {\n void self.load()\n },\n // Here rather than when the rendering backend starts: the canvas only\n // mounts once there is a graph, so a view whose graph must be fetched\n // would never fetch it.\n afterAttach() {\n if (!self.graph) {\n void self.load()\n }\n },\n }))\n}\n\nexport type GraphGenomeViewModel = Instance<\n ReturnType<typeof stateModelFactory>\n>\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { pushLaunchViewMenuItem } from '@jbrowse/core/ui'\nimport {\n getContainingTrack,\n getContainingView,\n getRpcSessionId,\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 } from './anchoredNodes'\nimport { BUBBLE_SPREAD_VALUES } 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 {\n COLOR_SCHEMES,\n COLOR_SCHEME_VALUES,\n resolveColorScheme,\n} 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 { hostFrame, isLinearHost } from './host'\nimport { layoutLabels } from './labelLayout'\nimport { ROW_HEIGHT_PX } from './layout/rowSpacing'\nimport { walkRowsExtent } from './layout/walkRowLayout'\nimport { walkRows } from './layout/walkRows'\nimport {\n LAYOUT_MODES,\n LAYOUT_MODE_VALUES,\n layoutModeByValue,\n modeUsesLayoutEngine,\n} from './layoutModes'\nimport { NODE_WIDTH_VALUES, nodeInk } from './nodeWidths'\nimport { anchorFromPaths } from './pathAnchoring'\nimport { pathColorsLegible, pathLegend } from './pathColors'\nimport {\n FIT_PADDING,\n clampZoom,\n drawingBounds,\n engineKey,\n fitTransform,\n fittedTranslateY,\n forceLayout,\n loadGraph,\n} from './pipeline'\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 { tubeMapPicture } from './tubeMap/draw'\nimport { tubeMapFrame, tubeMapNodeAt } from './tubeMap/frame'\nimport {\n axisScaleOf,\n contains,\n padded,\n viewportOf,\n zoomAbout,\n} from './viewport'\nimport { walkHighlight } from './walkHighlight'\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'\n\nimport type { BubbleSpread } from './bubbleSpreads'\nimport type { ColorScheme, ResolvedColorScheme } from './colorSchemes'\nimport type { GeneModel } from './genes/geneFeatures'\nimport type { LinearHost } from './host'\nimport type { LayoutModeValue } from './layoutModes'\nimport type { NodeWidth } from './nodeWidths'\nimport type { Bounds, EngineRequest } from './pipeline'\nimport type { RenderBatch, Renderer } from './renderer/types'\nimport type { RepeatArray } from './repeats/repeatFeatures'\nimport type { Graph, GraphNode, LayoutResult } from './types'\nimport type { SubgraphCutOptions, SubgraphRegion } 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 { AnyConfigurationModel } from '@jbrowse/core/configuration'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Feature } from '@jbrowse/core/util'\nimport type { FileLocation } from '@jbrowse/core/util/types'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\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}\nconst HOVER_BRIGHTEN = 1.4\nconst SELECT_BRIGHTEN = 1.6\nconst VIEWPORT_DEBOUNCE_MS = 150\nconst VIEWPORT_PANES_BUILT = 1\n\n// What the canvas draws under the tube map, whose ink is all TubeMapOverlay's\nconst EMPTY_BATCH: RenderBatch = {\n nodeStrokes: [],\n nodeStrokeRuns: new Map(),\n arrows: [],\n arrowRuns: new Map(),\n edgeCurves: [],\n edgeCurveRuns: new Map(),\n}\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\ntype ViewportOwner = 'fit' | 'user' | 'host'\n\nfunction geometryPainted(model: {\n lastGeometryStrokeCount?: number\n viewportRebuildPending: boolean\n geometryViewportDirty: number\n viewportDirty: number\n paintedGeometryVersion: number\n geometryVersion: number\n}) {\n return (\n model.lastGeometryStrokeCount !== undefined &&\n !model.viewportRebuildPending &&\n model.geometryViewportDirty === model.viewportDirty &&\n model.paintedGeometryVersion === model.geometryVersion\n )\n}\n\n// What the pane shows, in layout units.\nfunction paneViewportOf(model: {\n translateX: number\n translateY: number\n paneWidth: number\n axisScale: AxisScale\n canvasHeight: number\n}): Bounds {\n return viewportOf(model, model.axisScale, model.paneWidth, model.canvasHeight)\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 function GraphPaneMixin() {\n return types\n .compose(\n 'GraphPane',\n RenderLifecycleMixin(),\n types.model({\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 // unset takes the host's default: force in a view of its own, the\n // display config's in a track\n layoutMode: types.maybe(types.enumeration(LAYOUT_MODE_VALUES)),\n colorScheme: types.maybe(types.enumeration(COLOR_SCHEME_VALUES)),\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 reference span the reference-position ramp runs over, for a graph\n // with no region of its own to span it\n colorDomain: types.maybe(\n types.frozen<{ start: number; end: number }>(),\n ),\n // the linear view a graph of its own is paired with for the hover sync\n connectedViewId: types.maybe(types.string),\n }),\n )\n .volatile(() => ({\n graph: undefined as Graph | undefined,\n // the reference window the graph on screen was cut for, set with it\n graphRegion: undefined as SubgraphRegion | undefined,\n layoutResult: undefined as LayoutResult | undefined,\n // what the legends in the pane's top-right corner measure, so no label\n // is placed under them\n legendSize: { width: 0, height: 0 },\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 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 paintedGeometryVersion: -1,\n viewportRebuildPending: false,\n geometryViewportDirty: -1,\n }))\n .views(self => ({\n get defaultLayoutMode(): LayoutModeValue {\n return 'force'\n },\n get defaultColorScheme(): ColorScheme {\n return 'auto'\n },\n // a source declared but not yet loaded\n get hasPendingSource() {\n return false\n },\n get canRetryLoad() {\n return false\n },\n // the track the graph is cut from: the one the pane is a display of\n get sourceTrack(): AnyConfigurationModel | undefined {\n try {\n return getContainingTrack(self).configuration\n } catch {\n return undefined\n }\n },\n }))\n .views(self => ({\n get chosenLayoutMode() {\n return self.layoutMode ?? self.defaultLayoutMode\n },\n get chosenColorScheme() {\n return self.colorScheme ?? self.defaultColorScheme\n },\n }))\n .views(self => ({\n get paneWidth() {\n return getContainingView(self).width\n },\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 get geometryPainted() {\n return geometryPainted(self)\n },\n // The app-wide readiness contract (AppReadyMarker, @jbrowse/capture)\n // reads this: a declared source still fetching, or a graph whose\n // settled geometry is not on the canvas yet.\n get showLoading() {\n return (\n self.error === undefined &&\n (self.isLoading ||\n self.hasPendingSource ||\n (self.graph !== undefined && !geometryPainted(self)))\n )\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.chosenLayoutMode, 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 // The tube map colours its tubes whether or not paths are drawn on the\n // nodes, so under it the key follows the tubes.\n get pathLegend() {\n const paths = self.graph?.paths\n const colouring = self.drawPaths || self.layoutResult?.tubeMap\n return colouring && 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 // Undefined for a graph with neither, where the ramp spans the drawn\n // extent (computeReferenceRamp)\n get rampDomain() {\n return self.colorDomain ?? self.graphRegion\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 resolveColorScheme(self.chosenColorScheme, self.graph)\n },\n get nodePositions() {\n return self.layoutResult?.nodePositions\n },\n get labelsNodeSizes() {\n return !self.layoutResult?.tubeMap\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 get sourceAdapter() {\n const track = self.sourceTrack\n return track\n ? (readConfObject(track, 'adapter') as Record<string, unknown>)\n : undefined\n },\n get sourceTrackId() {\n const track = self.sourceTrack\n return track ? (readConfObject(track, 'trackId') as string) : undefined\n },\n get assemblyTrackChoices() {\n const region = self.graphRegion\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 get nodeInk(): NodeInk {\n return nodeInk(\n self.graph,\n self.nodeById,\n self.contigThickness,\n self.nodeWidth,\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 &&\n self.chosenLayoutMode !== 'walkrows' &&\n !self.layoutResult?.tubeMap\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.chosenLayoutMode !== '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.graphRegion)\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.chosenLayoutMode === 'variants' ? self.bubbles : []\n return bubbles.map(bubble => ({\n bubble,\n ...classifyBubble(bubble, self.repeatArrays),\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.chosenLayoutMode !== 'variants' &&\n self.chosenLayoutMode !== 'walkrows' &&\n !self.layoutResult?.tubeMap &&\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.chosenLayoutMode === 'variants' ||\n self.chosenLayoutMode === 'walkrows' ||\n self.layoutResult?.tubeMap ||\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 self.repeatArrays,\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' &&\n self.graph &&\n !self.layoutResult?.tubeMap\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.graphRegion\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 const layout = self.layoutResult\n const bars = this.walkRowBars\n return layout\n ? drawingBounds(layout, {\n region: self.popStack.length === 0 ? self.graphRegion : undefined,\n extent: bars && layout.extent ? walkRowsExtent(bars) : undefined,\n })\n : undefined\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 const bounds = this.layoutBounds\n const usableWidth = self.paneWidth - 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.chosenLayoutMode === '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.graphRegion\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.graphRegion),\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.graphRegion\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.graphRegion\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 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 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, graphRegion: 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 overlayLabels() {\n return layoutLabels(self)\n },\n get hostFrame() {\n const { host, graphRegion } = self\n return self.hostPlacesX && host && graphRegion\n ? hostFrame(host, graphRegion)\n : undefined\n },\n get tubeMapPicture() {\n const drawing = self.layoutResult?.tubeMap\n return drawing ? tubeMapPicture(drawing.layout) : undefined\n },\n get tubeMapFrame() {\n const drawing = self.layoutResult?.tubeMap\n return drawing\n ? tubeMapFrame(drawing, {\n scaleX: self.scaleX,\n translateX: self.translateX,\n scaleY: self.scaleY,\n translateY: self.translateY,\n usableHeight: self.canvasHeight - FIT_PADDING * 2,\n })\n : undefined\n },\n }))\n .views(self => ({\n tubeMapNodeAt(sx: number, sy: number) {\n const drawing = self.layoutResult?.tubeMap\n const frame = self.tubeMapFrame\n return drawing && frame ? tubeMapNodeAt(drawing, frame, sx, sy) : null\n },\n }))\n .actions(self => ({\n setError(error: unknown) {\n self.error = error\n self.isLoading = false\n self.statusMessage = ''\n },\n finishLoading() {\n self.isLoading = false\n self.statusMessage = ''\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.geometryViewportDirty = self.viewportDirty\n self.builtViewport = built\n self.geometryVersion++\n },\n markPainted(version: number) {\n self.paintedGeometryVersion = version\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 },\n setWalkRowSamples(samples: string[] | undefined) {\n self.walkRowSamples = samples\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 setLegendSize(size: { width: number; height: number }) {\n self.legendSize = size\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.graphRegion\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 t = zoomAbout(self, factor, centerX, centerY, self.pixelRows)\n self.scale = t.scale\n self.translateX = t.translateX\n self.translateY = t.translateY\n },\n setViewportDirty() {\n self.viewportRebuildPending = false\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 const bounds = self.layoutBounds\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 beats persisting a negative scale into\n // the session snapshot.\n const fit = bounds\n ? fitTransform(\n bounds,\n self.paneWidth,\n self.canvasHeight,\n self.pixelRows,\n )\n : undefined\n if (fit) {\n self.scale = fit.scale\n self.translateX = fit.translateX\n self.translateY = fit.translateY\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.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 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.viewportRebuildPending = true\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 function callEngine(request: EngineRequest) {\n const { rpcManager } = getSession(self)\n return rpcManager.call(getRpcSessionId(self), 'GraphComputeLayout', {\n ...request,\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 return engineKey(graph, {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n bubbleSpread: self.bubbleSpread,\n })\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.chosenLayoutMode).run(\n graph,\n self.graphRegion,\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 const oriented = (yield forceLayout(\n graph,\n {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n bubbleSpread: self.bubbleSpread,\n },\n callEngine,\n )) as { result: LayoutResult; duration: number }\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.result)\n return oriented\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 region: SubgraphRegion | undefined,\n keepSelection = false,\n ) {\n self.setStatusMessage('Parsing GFA')\n const graph = loadGraph(text, name, {\n referencePath: self.referencePath || region?.assemblyName,\n maxNodes: self.maxGraphNodes,\n })\n const selected = keepSelection ? self.selectedNode : null\n self.graph = graph\n self.graphRegion = region\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 // 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 (adapterConfig.type !== 'RgfaTabixAdapter' || prefix === undefined) {\n return\n }\n try {\n const features = (yield getSession(self).rpcManager.call(\n getRpcSessionId(self),\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(\n getRpcSessionId(self),\n 'CoreGetFeatures',\n {\n adapterConfig: readConfObject(config, 'adapter'),\n regions: [region],\n },\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 // 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. `region` is the window a\n // declared file was stated beside, which the parse anchors on.\n function* loadWholeGFA(\n name: string,\n source: string | ((signal: AbortSignal) => Promise<string>),\n region?: SubgraphRegion,\n ) {\n const { isLive, signal } = beginLoad()\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, region)\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.finishLoading()\n }\n }\n return isLive()\n }\n\n function abortLoad() {\n loadController?.abort()\n loadController = undefined\n liveLoad++\n liveRequest++\n }\n\n return {\n // Back to the import form: drop the graph and everything derived from\n // it. Any load in flight ends here too, or it would land its graph\n // afterwards.\n clearGraph() {\n abortLoad()\n self.graph = undefined\n self.graphRegion = undefined\n self.layoutResult = 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 // The user's stop, leaving whatever is drawn under it\n stopLoad() {\n abortLoad()\n self.isLoading = false\n self.statusMessage = ''\n self.loadCanceled = true\n },\n beforeDestroy() {\n abortLoad()\n },\n loadGFA: flow(function* (text: string, name = 'Imported GFA') {\n yield* loadWholeGFA(name, text)\n }),\n loadGFAFromLocation: flow(function* (\n location: FileLocation,\n region?: SubgraphRegion,\n ) {\n self.setStatusMessage('Fetching GFA')\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 region,\n )\n if (region && live && self.graph) {\n yield* loadRepeats(region, () => self.graphRegion === region)\n }\n }),\n // One cut of a region, laid out with the annotations over it. Whether\n // the region may be cut is the caller's call; overlapping cuts are\n // ordered by liveLoad, so the latest one lands.\n cutSubgraph: flow(function* (\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n opts: SubgraphCutOptions = {},\n ) {\n const { isLive, signal } = beginLoad()\n self.isLoading = true\n self.error = undefined\n self.setStatusMessage('Fetching subgraph')\n try {\n const fetchStart = performance.now()\n const gfaText = (yield getSession(self).rpcManager.call(\n getRpcSessionId(self),\n 'GetSubgraph',\n { adapterConfig, region, opts, 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 yield* parseAndLayout(gfaText, locLabel(region), region, true)\n if (!isLive()) {\n return\n }\n // Independent remote reads, each landing as it arrives. A coarse\n // cut reads no bubble index: its nodes are the bubbles, and the\n // index over its window can run to a chromosome's rows.\n self.setStatusMessage('Reading annotations')\n yield Promise.all([\n opts.tier === 'coarse'\n ? undefined\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.cutSubgraph]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.finishLoading()\n }\n }\n }),\n // Re-read one annotation track alone, for a track change. The graph,\n // its 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.graphRegion\n const trackId = self.repeatTrack?.trackId\n if (region) {\n yield* loadRepeats(\n region,\n () =>\n self.graphRegion === region &&\n self.repeatTrack?.trackId === trackId,\n )\n }\n }),\n reloadGenes: flow(function* () {\n const region = self.graphRegion\n const trackId = self.geneTrack?.trackId\n if (region) {\n yield* loadGenes(\n region,\n () =>\n self.graphRegion === 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.chosenLayoutMode,\n label: graph.name,\n indexBubbles: self.indexBubbles,\n },\n ]\n self.indexBubbles = undefined\n const label = `${BUBBLE_KIND_NAMES[classifyBubble(bubble, self.repeatArrays).kind]} at ${bubble.refName}:${bubble.start.toLocaleString()}`\n self.graph = { ...sub, name: label }\n if (self.chosenLayoutMode === '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.finishLoading()\n }\n } catch (e) {\n if (isLive()) {\n self.error = e\n self.finishLoading()\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.finishLoading()\n }\n } catch (e) {\n self.error = e\n self.finishLoading()\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.finishLoading()\n }\n } catch (e) {\n console.error('[GraphGenomeView.recomputeLayout]', e)\n self.error = e\n self.finishLoading()\n }\n }),\n }\n })\n .actions(self => ({\n startRenderingBackend(backend: Renderer) {\n if (!self.autorunsInstalled) {\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.graphRegion\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 = paneViewportOf(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.paneWidth, self.canvasHeight)\n const nodeById = self.nodeById\n if (self.layoutResult?.tubeMap) {\n dependOn(self.viewportDirty)\n b.uploadGeometry(EMPTY_BATCH)\n self.setGeometryMetrics(0, 0, {\n scale: untracked(() => self.scale),\n bounds: untracked(() => paneViewportOf(self)),\n })\n return true\n }\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(paneViewportOf(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 self.markPainted(untracked(() => self.geometryVersion))\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 cancelLoad() {\n if (self.canCancelLoad) {\n self.stopLoad()\n self.graph = undefined\n }\n },\n // loads the source again, for a host that has one\n retryLoad() {},\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 },\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.graphRegion?.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: onReference ? self.sourceTrackId : 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.sourceTrackId,\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 graphMenuItems(): MenuItem[] {\n const walks = self.walkChoices\n return [\n {\n label: 'Layout',\n subMenu: LAYOUT_MODES.map(mode => ({\n type: 'radio' as const,\n label: mode.label,\n checked: self.chosenLayoutMode === mode.value,\n disabled: self.graph ? !mode.available(self.graph) : false,\n onClick: () => {\n void self.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: self.chosenColorScheme === scheme.value,\n onClick: () => {\n self.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: self.highlightedPath === '',\n onClick: () => {\n self.setHighlightedPath('')\n },\n },\n ...walks.map(walk => ({\n type: 'radio' as const,\n label: walk.label,\n checked: self.highlightedPath === walk.name,\n onClick: () => {\n self.setHighlightedPath(walk.name)\n },\n })),\n ],\n },\n ]\n : []),\n ...(self.chosenLayoutMode === 'walkrows' &&\n self.repeatChoices.length > 0\n ? [\n {\n label: 'Repeat',\n subMenu: [\n {\n type: 'radio' as const,\n label: 'Whole window',\n checked: self.repeatKey === '',\n onClick: () => {\n self.setRepeatKey('')\n },\n },\n ...self.repeatChoices.map(({ key, name, unit }) => ({\n type: 'radio' as const,\n label: `${name} \u00B7 ${unit.toLocaleString()} bp unit`,\n checked: self.repeatKey === key,\n onClick: () => {\n self.setRepeatKey(key)\n },\n })),\n ],\n },\n ]\n : []),\n ...(self.hostPlacesX\n ? []\n : [\n {\n label: 'Zoom in',\n onClick: () => {\n self.zoom(1.5, self.paneWidth / 2, self.canvasHeight / 2)\n },\n },\n {\n label: 'Zoom out',\n onClick: () => {\n self.zoom(\n 1 / 1.5,\n self.paneWidth / 2,\n self.canvasHeight / 2,\n )\n },\n },\n {\n label: 'Zoom to fit',\n onClick: () => {\n self.zoomToFit()\n },\n },\n ]),\n {\n type: 'checkbox',\n label: 'Mark bubbles',\n checked: self.showBubbles,\n onClick: () => {\n self.setShowBubbles(!self.showBubbles)\n },\n },\n {\n type: 'checkbox',\n label: 'Show deletion edges',\n checked: self.showDeletionEdges,\n onClick: () => {\n self.setShowDeletionEdges(!self.showDeletionEdges)\n },\n },\n {\n type: 'checkbox',\n label: 'Genes on the backbone',\n checked: self.showGenes,\n onClick: () => {\n self.setShowGenes(!self.showGenes)\n },\n },\n ]\n },\n launchMenuItems(): 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 GraphPaneModel = Instance<ReturnType<typeof GraphPaneMixin>>\n", "import { bubbleSegmentIds, classifyBubble, formatBp } from './classifyBubble'\nimport { isBackbone } from '../anchoredNodes'\nimport { svgPath } from '../util/geometry'\n\nimport type { BubbleKind, RepeatSpan } 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 name goes, in layout units: over the middle of its nodes, at the\n // height of the highest\n labelAt: 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 repeats: readonly RepeatSpan[] = [],\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 minX = Infinity\n let maxX = -Infinity\n let minY = Infinity\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 minX = Math.min(minX, p.x)\n maxX = Math.max(maxX, p.x)\n minY = Math.min(minY, p.y)\n }\n }\n if (nodeIds.length === 0) {\n continue\n }\n const labelAt = { x: (minX + maxX) / 2, y: minY }\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 : labelAt\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, repeats),\n path: parts.join(''),\n labelAt,\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", "import type { SubgraphRegion } from '../GetSubgraph'\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 // whether the view's body is mounted, which an off-screen view's is not\n effectiveBodyMounted?: boolean\n hasVisibleContent?: boolean\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\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(\n window: HostWindow,\n capBp: number,\n margins = true,\n): SubgraphRegion {\n const visible = window.end - window.start\n const margin = margins\n ? Math.max(0, Math.min(window.span, (capBp - visible) / 2))\n : 0\n let start = Math.max(window.regionStart, Math.floor(window.start - margin))\n let end = Math.min(window.regionEnd, Math.ceil(window.end + margin))\n // Rounding outward can put a cut a base or two past the cap, which the load\n // refuses whole; trim it back rather than lose the cut.\n const over = end - start - capBp\n if (over > 0) {\n start += Math.floor(over / 2)\n end -= Math.ceil(over / 2)\n }\n return {\n refName: window.refName,\n assemblyName: window.assemblyName,\n start,\n end,\n }\n}\n\n// With margins, a cut holds while the window is inside it; without, only the\n// window itself does, since the drawing is the window.\nexport function cutHolds(\n cut: SubgraphRegion | undefined,\n window: HostWindow,\n margins = true,\n) {\n if (\n cut?.refName !== window.refName ||\n cut.assemblyName !== window.assemblyName\n ) {\n return false\n }\n // to the base, since a cut trimmed to its cap can end a base inside the window\n if (margins) {\n return cut.start <= window.start + 1 && window.end <= cut.end + 1\n }\n const exact = hostCut(window, Infinity, false)\n return (\n Math.abs(cut.start - exact.start) < 1 && Math.abs(cut.end - exact.end) < 1\n )\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 { 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 { isBackbone } from './anchoredNodes'\nimport { spreadFor } from './bubbleSpreads'\nimport { convertGFAToGraph } from './gfa/gfaConverter'\nimport { drawnNodeLength, layoutScaling } from './layout/drawnScale'\nimport { mergeRuns, splitRuns } from './layout/mergeRuns'\nimport { orientToReference } from './layout/orientToReference'\nimport { seededNodes } from './layout/referenceSeeds'\nimport { anchorGraph } from './pathAnchoring'\nimport { parseGFA } from '../gfa-core/index'\n\nimport type { BubbleSpread } from './bubbleSpreads'\nimport type { LayoutScaling } from './layout/drawnScale'\nimport type { LayoutNode } from './layout/referenceSeeds'\nimport type { Graph, LayoutResult, NodeSegment } from './types'\n\n// The graph a view draws, from GFA text to layouts, with no host in it: the\n// GraphGenomeView model and a standalone page both drive this.\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\n// A general GFA states its coordinates only in its P/W lines, so the walk that\n// recovers them happens before anything reads `stable`; otherwise the anchored\n// layouts see an unanchored graph and hand off to force. `maxNodes` is checked\n// here because it is upstream of everything that scales with the node count.\nexport function loadGraph(\n text: string,\n name: string,\n opts: { referencePath?: string; maxNodes?: number } = {},\n) {\n const graph = anchorGraph(\n convertGFAToGraph(parseGFA(text), name),\n opts.referencePath,\n )\n if (graph.nodes.length === 0) {\n throw new Error(`No graph segments in ${name}`)\n }\n if (opts.maxNodes !== undefined && graph.nodes.length > opts.maxNodes) {\n throw new Error(\n `Graph too large to draw: ${graph.nodes.length.toLocaleString()} nodes (limit ${opts.maxNodes.toLocaleString()}). Zoom in to a smaller region, or raise maxGraphNodes on this view.`,\n )\n }\n return graph\n}\n\n// What the Bandage engine is handed. A node may carry an `x`/`y` seed, which\n// the engine reads as where its chain starts (referenceSeeds.ts).\nexport interface EngineRequest {\n graph: { nodes: LayoutNode[]; edges: Graph['edges'] }\n options: Record<string, unknown>\n}\n\nexport type LayoutEngine = (\n request: EngineRequest,\n) => Promise<{ result: LayoutResult; duration: number }>\n\nexport interface EngineSettings {\n quality: number\n linearLayout: boolean\n bubbleSpread: BubbleSpread\n}\n\n// `scaling.nodes` rather than the graph's own: under a compressing drawn-length\n// law a node's `length` crosses this boundary as a drawn length, which is all\n// the engine ever reads it as. An anchored graph also carries a seed per node,\n// the backbone along x, and asks the engine not to rotate components: FMMM\n// then keeps the reference's coarse shape instead of curling it into a C\n// (docs/layout-experiments.md, experiment 2).\nexport function engineRequest(\n graph: Graph,\n scaling: LayoutScaling,\n settings: EngineSettings,\n): EngineRequest {\n const anchored = graph.nodes.some(isBackbone)\n return {\n graph: {\n nodes: anchored ? seededNodes(graph, scaling) : scaling.nodes,\n edges: graph.edges,\n },\n options: {\n quality: settings.quality,\n linearLayout: settings.linearLayout,\n ...scaling.opts,\n ...(anchored ? { rotateComponents: false } : {}),\n },\n }\n}\n\n// What a force layout of this graph is a function of, as a cache key: the\n// settings `engineRequest` reads, whether it seeds, and the reference path the\n// seeds follow.\nexport function engineKey(graph: Graph, settings: EngineSettings) {\n const anchored = graph.nodes.some(isBackbone)\n return `${settings.quality}|${settings.linearLayout}|${settings.bubbleSpread}|${anchored}|${graph.referencePath ?? ''}`\n}\n\n// The engine lays out the runs, not the nodes: a base-level cut is thousands\n// of nodes in unbranching chains, and one chain per run is the same drawing at\n// a third of the time. Members take their share of the run's polyline by drawn\n// length, so the picture is per node again before anything else sees it, and\n// it is turned so the reference reads left to right.\nexport async function forceLayout(\n graph: Graph,\n settings: EngineSettings,\n engine: LayoutEngine,\n) {\n const spread = spreadFor(settings.bubbleSpread)\n const merged = mergeRuns(graph)\n const { result, duration } = await engine(\n engineRequest(merged.graph, layoutScaling(merged.graph, spread), settings),\n )\n const scaling = layoutScaling(graph, spread)\n const drawn = new Map(\n scaling.nodes.map(n => [n.id, drawnNodeLength(scaling.opts, n.length)]),\n )\n const positions = splitRuns(\n result.nodePositions,\n merged.runs,\n id => drawn.get(id) ?? 0,\n )\n return {\n result: {\n ...result,\n nodePositions: graph.nodes.some(isBackbone)\n ? orientToReference(graph, positions)\n : positions,\n },\n duration,\n }\n}\n\nexport function layoutExtent(\n nodePositions: Record<string, NodeSegment[]>,\n): Bounds {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const segments of Object.values(nodePositions)) {\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 return { minX, minY, maxX, maxY }\n}\n\n// Gap between the drawing and the edge of the pane, on all four sides.\nexport const FIT_PADDING = 40\n\n// The floor keeps a scale positive and finite, and has to clear the smallest\n// scale a real layout asks for: fitting a chromosome-scale rGFA (250 Mbp) into\n// ~720 px needs ~3e-6.\nconst MIN_ZOOM = 1e-6\nconst MAX_ZOOM = 100\n\nexport function clampZoom(zoom: number) {\n return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom))\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, and centring an\n// overflow would push the top row, the reference backbone, off the top.\nexport function 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\n// The transform that fits `bounds` in the pane, or undefined when there is\n// nothing to fit or nothing to fit into. Each axis constrains the scale only\n// when it has extent, since an anchored window holding only backbone puts every\n// node on row 0. A row layout (`pixelRows`) fits on x alone and keeps y in\n// screen px; an isotropic one fits on whichever axis binds.\nexport function fitTransform(\n bounds: { minX: number; minY: number; w: number; h: number },\n paneWidth: number,\n paneHeight: number,\n pixelRows: boolean,\n) {\n const usableWidth = paneWidth - FIT_PADDING * 2\n const usableHeight = paneHeight - FIT_PADDING * 2\n if (\n usableWidth <= 0 ||\n usableHeight <= 0 ||\n !(bounds.w > 0 || (!pixelRows && bounds.h > 0))\n ) {\n return undefined\n }\n const fitX = bounds.w > 0 ? usableWidth / bounds.w : Infinity\n const fitY = bounds.h > 0 ? usableHeight / bounds.h : Infinity\n const scale = clampZoom(pixelRows ? fitX : Math.min(fitX, fitY))\n return {\n scale,\n translateX:\n FIT_PADDING - bounds.minX * scale + (usableWidth - bounds.w * scale) / 2,\n translateY: fittedTranslateY(bounds, usableHeight, pixelRows ? 1 : scale),\n }\n}\n\n// Extent of the drawing in layout units, which the fit and the pane height\n// read. On a reference-bp layout x is the cut window rather than how far the\n// drawing reaches: an allele anchored far outside it is a fact about the\n// graph, not a reason to draw the window at 6% of the frame. `extent` stands\n// in for the layout's own when an overlay's rows reach further (walk rows).\nexport function drawingBounds(\n layout: LayoutResult,\n opts: {\n region?: { start: number; end: number }\n extent?: LayoutResult['extent']\n } = {},\n) {\n let { minX, minY, maxX, maxY } = layoutExtent(layout.nodePositions)\n const { region } = opts\n if (layout.referenceAxis && region && region.end > region.start) {\n minX = region.start\n maxX = region.end\n }\n const extent = opts.extent ?? layout.extent\n if (extent) {\n minX = Math.min(minX, extent.minX ?? minX)\n minY = Math.min(minY, extent.minY ?? minY)\n maxX = Math.max(maxX, extent.maxX ?? maxX)\n maxY = Math.max(maxY, extent.maxY ?? maxY)\n }\n return { minX, minY, w: maxX - minX, h: maxY - minY }\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 type { TubeMapColumn } from '../layout/tubeMapLayout'\n\n// Tube x to screen x under the reference axis. A column covers the screen span\n// of the reference bp its backbone node covers, which puts the tubes under the\n// linear view's other tracks. But the tube map changes lanes BETWEEN columns,\n// and adjacent reference nodes abut in bp, so the curves would have no width\n// at all. At each boundary the gap the tube map drew (its curves, and any\n// columns of inserted sequence, which cover no reference) gets its tube px\n// back, up to MAX_GAP_PX, where there is room: first from reference no\n// column covers, then from up to half of each neighbouring column. Zoomed in that is a\n// few px off a wide node; zoomed out the columns are thin, the gap gets what\n// they can spare and the curves steepen.\n//\n// Piecewise linear, so a curve's bezier, which lies inside one gap, maps\n// exactly by mapping its control points.\n\nexport interface Knot {\n tx: number\n sx: number\n}\n\n// The most screen a boundary claims. The tube map spaces its columns to keep\n// the lane changes shallow, which with many haplotypes is hundreds of px a\n// gap; on the reference axis the curves steepen instead.\nconst MAX_GAP_PX = 24\n\nconst isReal = (c: TubeMapColumn) => c.bp1 > c.bp0\n\nexport function referenceKnots(\n columns: readonly TubeMapColumn[],\n bpToScreen: (bp: number) => number,\n): Knot[] {\n const real = columns.filter(isReal)\n if (real.length === 0) {\n const first = columns[0]\n return first ? [{ tx: first.x0, sx: bpToScreen(first.bp0) }] : []\n }\n const screen = real.map(c => ({\n c,\n s0: bpToScreen(c.bp0),\n s1: bpToScreen(c.bp1),\n trimLeft: 0,\n trimRight: 0,\n }))\n for (let i = 0; i + 1 < screen.length; i++) {\n const left = screen[i]!\n const right = screen[i + 1]!\n const demand = Math.min(right.c.x0 - left.c.x1, MAX_GAP_PX)\n const short = demand - (right.s0 - left.s1)\n if (short > 0) {\n const capLeft = (left.s1 - left.s0) / 2\n const capRight = (right.s1 - right.s0) / 2\n let fromLeft = Math.min(short / 2, capLeft)\n const fromRight = Math.min(short - fromLeft, capRight)\n fromLeft = Math.min(short - fromRight, capLeft)\n left.trimRight = fromLeft\n right.trimLeft = fromRight\n }\n }\n const knots: Knot[] = []\n for (const { c, s0, s1, trimLeft, trimRight } of screen) {\n knots.push(\n { tx: c.x0, sx: s0 + trimLeft },\n { tx: c.x1, sx: s1 - trimRight },\n )\n }\n return knots\n}\n\n// Beyond the first and last knot the drawing continues at one screen px per\n// tube px, which is where the track stubs past the end nodes go.\nexport function warpX(knots: readonly Knot[], tx: number) {\n const n = knots.length\n if (n === 0) {\n return tx\n }\n const first = knots[0]!\n const last = knots[n - 1]!\n if (tx <= first.tx) {\n return first.sx + (tx - first.tx)\n }\n if (tx >= last.tx) {\n return last.sx + (tx - last.tx)\n }\n let lo = 0\n let hi = n - 1\n while (hi - lo > 1) {\n const mid = (lo + hi) >> 1\n if (knots[mid]!.tx <= tx) {\n lo = mid\n } else {\n hi = mid\n }\n }\n const a = knots[lo]!\n const b = knots[hi]!\n const span = b.tx - a.tx\n return span > 0 ? a.sx + ((tx - a.tx) / span) * (b.sx - a.sx) : a.sx\n}\n", "import { referenceKnots, warpX } from './warp'\n\nimport type { TubeMapDrawing } from '../layout/tubeMapLayout'\n\nexport interface TubeMapView {\n scaleX: number\n translateX: number\n scaleY: number\n translateY: number\n // the pane's height less its fit padding\n usableHeight: number\n}\n\n// Tube coordinates to screen px\nexport interface TubeMapTransform {\n x: (tx: number) => number\n y: (ty: number) => number\n // screen px per tube px in y, which thins the node outlines of a squeezed\n // stack along with its tubes\n yScale: number\n}\n\nconst MIN_TUBE_Y_SCALE = 0.05\n\n// How far a node's box reaches past its tubes (tubemap-core's nodeOutlinePath)\nconst NODE_BOX_PAD = 9\n\n// On the reference axis y is px and a linear view has no vertical pan, so a\n// stack of tubes taller than the track squeezes to fit it; dragging the track\n// taller gives the tubes back their width.\nfunction squeeze(drawing: TubeMapDrawing, usableHeight: number) {\n const { minY, maxY } = drawing.layout.bounds\n const h = maxY - minY\n return drawing.columns && h > 0\n ? Math.max(MIN_TUBE_Y_SCALE, Math.min(1, usableHeight / h))\n : 1\n}\n\n// The tube map's own axis is the layout's x; the reference axis warps each\n// column onto the bp it covers (warp.ts).\nexport function tubeMapFrame(\n drawing: TubeMapDrawing,\n view: TubeMapView,\n): TubeMapTransform {\n const { scaleX, translateX, translateY } = view\n const yScale = view.scaleY * squeeze(drawing, view.usableHeight)\n const y = (ty: number) => (ty - drawing.yOffset) * yScale + translateY\n const { columns } = drawing\n if (columns) {\n const knots = referenceKnots(columns, bp => bp * scaleX + translateX)\n return { x: tx => warpX(knots, tx), y, yScale }\n }\n return { x: tx => tx * scaleX + translateX, y, yScale }\n}\n\n// The node whose box is under a screen point. The canvas's hit test measures\n// from a centreline and a stroke width, and a tube map box is as tall as the\n// tubes through it.\nexport function tubeMapNodeAt(\n drawing: TubeMapDrawing,\n { x, y }: TubeMapTransform,\n sx: number,\n sy: number,\n) {\n let hit: string | null = null\n drawing.layout.nodes.forEach(node => {\n if (\n node.order >= 0 &&\n x(node.x - NODE_BOX_PAD) <= sx &&\n sx <= x(node.x + node.pixelWidth + NODE_BOX_PAD) &&\n y(node.y - NODE_BOX_PAD) <= sy &&\n sy <= y(node.y + node.contentHeight + NODE_BOX_PAD)\n ) {\n hit = node.name\n }\n })\n return hit\n}\n", "import { clampZoom } from './pipeline'\n\nimport type { Bounds } from './pipeline'\nimport type { AxisScale } from './util/geometry'\n\n// The pan and zoom arithmetic a pane runs, in layout units, with no host in it.\n\nexport interface PaneTransform {\n scale: number\n translateX: number\n translateY: number\n}\n\n// The zoom as a pair of axis scales. `pixelRows` rides along rather than being\n// derived downstream: the deletion bow is capped in a row layout and not in\n// the isotropic one, and `scaleY !== scaleX` coincides at one zoom level.\nexport function axisScaleOf(scale: number, pixelRows: boolean): AxisScale {\n return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows }\n}\n\n// What a pane of `width` x `height` css px shows, in layout units.\nexport function viewportOf(\n t: { translateX: number; translateY: number },\n axis: AxisScale,\n width: number,\n height: number,\n): Bounds {\n return {\n minX: -t.translateX / axis.scaleX,\n minY: -t.translateY / axis.scaleY,\n maxX: (width - t.translateX) / axis.scaleX,\n maxY: (height - t.translateY) / axis.scaleY,\n }\n}\n\n// The window a geometry build covers: the pane plus `panes` whole panes on\n// every side, so an ordinary pan never blanks its margins for the debounce and\n// never rebuilds at all.\nexport function 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\nexport function 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// Zoom by `factor` about a screen point. y only follows when it is on the same\n// scale: a row layout's y is screen px, so moving it by the ratio would slide\n// the rows off under the cursor while their pitch stayed put.\nexport function zoomAbout(\n t: PaneTransform,\n factor: number,\n centerX: number,\n centerY: number,\n pixelRows: boolean,\n): PaneTransform {\n const scale = clampZoom(t.scale * factor)\n const ratio = scale / t.scale\n return {\n scale,\n translateX: centerX - (centerX - t.translateX) * ratio,\n translateY: pixelRows\n ? t.translateY\n : centerY - (centerY - t.translateY) * ratio,\n }\n}\n\n// Where a screen point falls in layout units.\nexport function screenToLayout(\n x: number,\n y: number,\n t: { translateX: number; translateY: number },\n axis: AxisScale,\n) {\n return {\n x: (x - t.translateX) / axis.scaleX,\n y: (y - t.translateY) / axis.scaleY,\n }\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 { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nexport interface TrackScanSession {\n tracks: AnyConfigurationModel[]\n assemblies: AnyConfigurationModel[]\n}\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. The cut region 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 './launchFromGraph'\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.\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 './launchFromGraph'\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'\n\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(item => typeof item === 'string')\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", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport {\n TrackType,\n createBaseTrackConfig,\n createBaseTrackModel,\n} from '@jbrowse/core/pluggableElementTypes'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'\n\n// a lanes copy asking for headerLanes, so only gbz-base offers lanes\nfunction displaysInOrder(pluginManager: PluginManager): DisplayType[] {\n const displays = pluginManager.getElementTypesInGroup(\n 'display',\n ) as DisplayType[]\n const named = (name: string) => displays.find(d => d.name === name)\n const lanes = named('MultiWaySyntenyDisplay')\n return [\n named('LinearGraphDisplay'),\n named('LinearBasicDisplay'),\n lanes &&\n Object.assign(Object.create(lanes) as DisplayType, {\n adapterCapabilities: ['headerLanes'],\n }),\n ].filter((d): d is DisplayType => d !== undefined)\n}\n\nexport default function GraphTrackF(pluginManager: PluginManager) {\n pluginManager.addTrackType(() => {\n const configSchema = ConfigurationSchema(\n 'GraphTrack',\n {},\n {\n baseConfiguration: createBaseTrackConfig(pluginManager),\n explicitIdentifier: 'trackId',\n },\n )\n const track = new TrackType({\n name: 'GraphTrack',\n displayName: 'Pangenome graph track',\n configSchema,\n stateModel: createBaseTrackModel(\n pluginManager,\n 'GraphTrack',\n configSchema,\n ),\n })\n for (const display of displaysInOrder(pluginManager)) {\n track.addDisplayType(display)\n }\n return track\n })\n}\n", "import type modelFactory from './GraphGenomeView/viewModel'\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 cuts the\n// `loadedTrackId`/`loadedRegion` pair once, when 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 Object.assign(\n new DisplayType({\n name: 'LinearGraphDisplay',\n displayName: 'Graph',\n configSchema,\n stateModel: stateModelFactory(configSchema),\n trackType: 'GraphTrack',\n viewType: 'LinearGenomeView',\n ReactComponent: lazy(() => import('./components/LinearGraphDisplay')),\n }),\n { adapterCapabilities: ['getSubgraph'] },\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: 'the height of the track the graph is drawn in',\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 { getSession } from '@jbrowse/core/util'\nimport TrackHeightMixin from '@jbrowse/display-kit/TrackHeightMixin'\nimport { addDisposer, types } from '@jbrowse/mobx-state-tree'\nimport {\n computeActivityPhase,\n computeDisplayStatusPhase,\n} from '@jbrowse/render-core/displayPhase'\nimport SettingsIcon from '@mui/icons-material/Settings'\nimport { reaction } from 'mobx'\n\nimport { cutHolds, hostCut, hostWindow } from '../GraphGenomeView/host'\nimport { layoutModeByValue } from '../GraphGenomeView/layoutModes'\nimport {\n GraphPaneMixin,\n MAX_GRAPH_REGION_BP,\n formatSpanBp,\n} from '../GraphGenomeView/model'\nimport {\n graphReferenceAssembly,\n offReferenceProblem,\n trackLanes,\n} from '../graphTrackConfig'\n\nimport type { LinearGraphDisplayConfigModel } from './configSchema'\nimport type { SubgraphRegion, SubgraphTier } from '../GetSubgraph'\nimport type { ColorScheme } from '../GraphGenomeView/colorSchemes'\nimport type { HostWindow } from '../GraphGenomeView/host'\nimport type { LayoutModeValue } from '../GraphGenomeView/layoutModes'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\nimport type { DisplayStatusPhase } from '@jbrowse/render-core/displayPhase'\n\nconst GraphTrackSettingsDialog = lazy(\n () => import('./components/GraphTrackSettingsDialog'),\n)\n\nexport function stateModelFactory(configSchema: LinearGraphDisplayConfigModel) {\n return (\n types\n .compose(\n 'LinearGraphDisplay',\n BaseDisplay,\n TrackHeightMixin(),\n GraphPaneMixin(),\n types.model({\n type: types.literal('LinearGraphDisplay'),\n configuration: ConfigurationReference(configSchema),\n // A 4.0 entry's nested state, which preProcessSnapshot folds flat and\n // so never holds. Declared so a session spec stating it is not\n // reported as an unknown key.\n pane: types.maybe(types.frozen()),\n // the window the graph on screen was asked for, so a restored\n // session cuts it again\n cutRegion: types.maybe(types.frozen<SubgraphRegion>()),\n // whether that cut came from the track's coarse pair\n // (RgfaTabixAdapter), on which no bp cap applies\n coarseCut: types.optional(types.boolean, false),\n // The bp ceiling on a fine cut, a proxy for node count that only\n // holds at fine granularity; `maxGraphNodes` counts what came back.\n maxRegionBp: types.optional(types.number, MAX_GRAPH_REGION_BP),\n // How far the cut follows links past the region's own segments. At\n // 0 a detour that leaves the backbone before the window and rejoins\n // after it arrives as a fragment, since its interior is indexed only\n // under its own sequence, so one bubble draws as two unrelated\n // insertions. A hop follows alleles only, so it never walks the\n // backbone out of the window.\n subgraphContext: types.optional(types.number, 1),\n // the haplotypes a GBZ cut is for; unset is the lanes the track names\n subgraphHaplotypes: types.maybe(types.frozen<string[]>()),\n }),\n )\n // a 4.0 session nests the graph's state under `pane`\n .preProcessSnapshot(snapshot => {\n const { pane, ...rest } = snapshot as { pane?: { type?: string } }\n if (!pane) {\n return snapshot\n }\n const { type: _type, ...props } = pane\n return { ...props, ...rest } as typeof snapshot\n })\n .volatile(() => ({\n recuts: 0,\n // the host window the settle clock last woke on\n settledWindow: undefined as HostWindow | undefined,\n }))\n .views(self => ({\n get defaultLayoutMode(): LayoutModeValue {\n return getConf(self, 'layoutMode')\n },\n get defaultColorScheme(): ColorScheme {\n return getConf(self, 'colorScheme')\n },\n get canvasHeight() {\n return self.height\n },\n get canRetryLoad() {\n return self.cutRegion !== undefined\n },\n get chosenHaplotypes() {\n return (\n self.subgraphHaplotypes ??\n trackLanes(self.parentTrack.configuration)\n )\n },\n // the linear view's zoom past which the track's coarse pair is cut, or\n // undefined for a track with none\n get coarseAboveBpPerPx() {\n const coarse = self.adapterConfig.coarse as\n { aboveBpPerPx?: unknown } | undefined\n const above = coarse?.aboveBpPerPx\n return typeof above === 'number' ? above : undefined\n },\n }))\n .views(self => ({\n tierAt(bpPerPx: number): SubgraphTier {\n const above = self.coarseAboveBpPerPx\n return above !== undefined && bpPerPx > above ? 'coarse' : 'fine'\n },\n capFor(tier: SubgraphTier) {\n return tier === 'coarse' ? Infinity : self.maxRegionBp\n },\n get cutTier(): SubgraphTier {\n return self.coarseCut && self.coarseAboveBpPerPx !== undefined\n ? 'coarse'\n : 'fine'\n },\n }))\n .views(self => ({\n get settledCapBp() {\n const seen = self.settledWindow\n return seen ? self.capFor(self.tierAt(seen.bpPerPx)) : Infinity\n },\n }))\n .views(self => ({\n get regionTooLarge() {\n const seen = self.settledWindow\n return seen !== undefined && seen.end - seen.start > self.settledCapBp\n },\n get regionTooLargeReason() {\n const seen = self.settledWindow\n return seen\n ? `Region too large for a graph cut (${formatSpanBp(seen.end - seen.start)}, max ${formatSpanBp(self.settledCapBp)})`\n : ''\n },\n get zoomCanReleaseGate() {\n return true\n },\n get fetchCanceled() {\n return self.loadCanceled\n },\n }))\n .views(self => ({\n get displayPhase(): DisplayStatusPhase {\n // A backend that failed is an error the canvas reports with its\n // retry; the status chrome has no renderError phase of its own.\n return computeDisplayStatusPhase(\n {\n regionTooLarge: self.regionTooLarge,\n error: self.error ?? self.renderError,\n },\n () =>\n computeActivityPhase(\n {\n isMinimized: self.isMinimized,\n fetchInert: false,\n viewportEmpty:\n self.host?.initialized === true &&\n self.host.hasVisibleContent === false,\n isLoading: self.isLoading || !self.hasGraph,\n fetchCanceled: self.loadCanceled,\n awaitingDependentData: false,\n rendersCanvas: true,\n canvasDrawn: self.painted && self.geometryPainted,\n },\n () => true,\n () => self.host?.effectiveBodyMounted ?? true,\n ),\n )\n },\n }))\n .actions(self => ({\n // The setters describe the next cut; the caller re-cuts.\n setSubgraphContext(hops: number) {\n self.subgraphContext = hops\n },\n setSubgraphHaplotypes(haplotypes: string[] | undefined) {\n self.subgraphHaplotypes = haplotypes\n },\n setMaxRegionBp(bp: number) {\n self.maxRegionBp = bp\n },\n // Cut `cutRegion` again with the current options. A hop past a coarse\n // cut reaches nothing new: every bubble node's two links are indexed\n // under the backbone either side of it.\n cut() {\n const region = self.cutRegion\n if (!region) {\n return\n }\n const problem = offReferenceProblem(\n graphReferenceAssembly(self.parentTrack.configuration),\n region.assemblyName,\n )\n if (problem) {\n self.clearGraph()\n self.setError(new Error(problem))\n return\n }\n const coarse = self.cutTier === 'coarse'\n return self.cutSubgraph(self.adapterConfig, region, {\n hops: coarse ? 0 : self.subgraphContext,\n haplotypes: self.chosenHaplotypes,\n tier: coarse ? 'coarse' : undefined,\n })\n },\n }))\n .actions(self => ({\n recutAt(seen: HostWindow) {\n const tier = self.tierAt(seen.bpPerPx)\n self.coarseCut = tier === 'coarse'\n self.cutRegion = hostCut(\n seen,\n self.capFor(tier),\n layoutModeByValue(self.chosenLayoutMode).cutMargins,\n )\n self.recuts++\n return self.cut()\n },\n }))\n .actions(self => ({\n // The settle clock. A window the cut still holds fetches nothing; one\n // past its edge re-cuts, on the tier the zoom asks for, with margins\n // when the host places x and the window alone when the layout draws\n // its own picture of it. A canceled cut is re-made by the next move.\n // Returns whether it re-cut.\n settleOn(seen: HostWindow) {\n self.settledWindow = seen\n const margins = layoutModeByValue(self.chosenLayoutMode).cutMargins\n if (\n self.regionTooLarge ||\n (!self.loadCanceled &&\n self.tierAt(seen.bpPerPx) === self.cutTier &&\n cutHolds(self.cutRegion, seen, margins))\n ) {\n return false\n }\n void self.recutAt(seen)\n return true\n },\n switchLayout(mode: LayoutModeValue) {\n const margins = layoutModeByValue(self.chosenLayoutMode).cutMargins\n self.setLayoutMode(mode)\n const seen = self.host ? hostWindow(self.host) : undefined\n if (\n seen &&\n !self.regionTooLarge &&\n layoutModeByValue(mode).cutMargins !== margins\n ) {\n return self.recutAt(seen)\n }\n return self.recomputeLayout()\n },\n retryLoad() {\n void self.cut()\n },\n reload() {\n void self.cut()\n },\n cancelFetchByUser() {\n self.stopLoad()\n },\n }))\n .actions(self => ({\n forceLoad() {\n const seen = self.settledWindow\n if (seen) {\n self.setMaxRegionBp(Math.ceil(seen.end - seen.start))\n self.settleOn(seen)\n }\n },\n // The host's two clocks. The frame clock moves x with every frame of\n // the linear view and fetches nothing; the settle clock wakes on its\n // debounced blocks.\n afterAttach() {\n if (self.cutRegion && !self.graph) {\n void self.cut()\n }\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 .views(self => ({\n trackMenuItems(): MenuItem[] {\n return [\n ...self.graphMenuItems(),\n {\n label: 'Settings',\n icon: SettingsIcon,\n onClick: () => {\n getSession(self).queueDialog(onClose => [\n GraphTrackSettingsDialog,\n { model: self, open: true, onClose },\n ])\n },\n },\n ...self.launchMenuItems(),\n ]\n },\n }))\n )\n}\n\nexport type LinearGraphDisplayStateModel = ReturnType<typeof stateModelFactory>\nexport type LinearGraphDisplayModel = Instance<LinearGraphDisplayStateModel>\n// what the track's own settings read, which the track menu that opens them\n// cannot name without naming itself\nexport type LinearGraphCutModel = Omit<\n LinearGraphDisplayModel,\n 'trackMenuItems'\n>\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 a graph display needs of its track's adapter\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 GraphTrack\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 {\n buildAdapterConfig,\n isSegmentsLocation,\n} from '../GraphAddTrackWorkflow/buildTrackConfig'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { FileLocation } from '@jbrowse/core/util'\nimport type {\n AdapterGuesser,\n TrackTypeGuesser,\n} from '@jbrowse/core/util/tracks'\n\ninterface TrackConfigSnapshot {\n displays?: { type?: string; [key: string]: unknown }[]\n [key: string]: unknown\n}\n\nconst GRAPH_ADAPTERS = new Set(['RgfaTabixAdapter', 'GbzBaseSyntenyAdapter'])\n\nfunction locationName(loc: FileLocation) {\n return 'uri' in loc ? loc.uri : 'localPath' in loc ? loc.localPath : loc.name\n}\n\nexport function isGbzLocation(loc: FileLocation) {\n return locationName(loc).toLowerCase().endsWith('.gbz.db')\n}\n\n// A gbz-base adapter cuts on the first assembly it names. A track added\n// through Add track names that assembly on the track alone.\nexport function withGbzAnchor(snap: TrackConfigSnapshot) {\n const adapter = snap.adapter as\n { type?: string; assemblyNames?: unknown[] } | undefined\n return adapter?.type === 'GbzBaseSyntenyAdapter' &&\n !adapter.assemblyNames?.length &&\n Array.isArray(snap.assemblyNames) &&\n snap.assemblyNames.length > 0\n ? { ...snap, adapter: { ...adapter, assemblyNames: snap.assemblyNames } }\n : snap\n}\n\nexport function guessGraphAdapter(\n file: FileLocation,\n index?: FileLocation,\n adapterHint?: string,\n) {\n if (\n (!adapterHint || adapterHint === 'GbzBaseSyntenyAdapter') &&\n isGbzLocation(file)\n ) {\n return {\n type: 'GbzBaseSyntenyAdapter',\n gbzDbLocation: file,\n ...(index ? { haplotypeIndexLocation: index } : {}),\n }\n }\n if (\n (!adapterHint || adapterHint === 'RgfaTabixAdapter') &&\n isSegmentsLocation(file)\n ) {\n return buildAdapterConfig({\n choice: 'RgfaTabixAdapter',\n loc: file,\n indexLoc: index,\n assembly: '',\n sample: '',\n })\n }\n return undefined\n}\n\nexport default function GraphTrackDefaultsF(pluginManager: PluginManager) {\n pluginManager.addToExtensionPoint('Core-preProcessTrackConfig', withGbzAnchor)\n pluginManager.addToExtensionPoint(\n 'Core-guessAdapterForLocation',\n (next: AdapterGuesser): AdapterGuesser =>\n (file, index, adapterHint) =>\n guessGraphAdapter(file, index, adapterHint) ??\n next(file, index, adapterHint),\n )\n pluginManager.addToExtensionPoint(\n 'Core-guessTrackTypeForLocation',\n (next: TrackTypeGuesser): TrackTypeGuesser =>\n (adapterName, file) =>\n GRAPH_ADAPTERS.has(adapterName)\n ? 'GraphTrack'\n : next(adapterName, file),\n )\n}\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 graph displays in a linear view's own tracks, connected to it by where\n// they sit.\nfunction trackGraphs(view: Record<string, unknown>) {\n const tracks = Array.isArray(view.tracks) ? view.tracks : []\n return tracks.flatMap((track: unknown) =>\n isRecord(track) && Array.isArray(track.displays)\n ? track.displays.filter(\n (display: unknown) =>\n isRecord(display) && display.type === 'LinearGraphDisplay',\n )\n : [],\n ) as Record<string, unknown>[]\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 (isRecord(view) && view.id === linearViewId) {\n for (const display of trackGraphs(view)) {\n const region = readRegion(display.hoverHighlight)\n if (region && isOnAssembly(region, linearAssemblyNames)) {\n highlights.push({\n key: typeof display.id === 'string' ? display.id : 'graph-track',\n region,\n })\n }\n }\n }\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.8'\n"],
|
|
5
|
-
"mappings": "q8BAAA,IAAAA,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,sBAAsB,ICAtD,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,iDAAiD,ICAjF,IAAAC,EAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,6BAA6B,ICA7D,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,8CAA8C,ICA9E,IAAAC,EAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,qCAAqC,ICArE,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,8CAA8C,ICA9E,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,oCAAoC,ICApE,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,4CAA4C,ICA5E,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,2CAA2C,ICA3E,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,iDAAiD,ICAjF,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,oDAAoD,ICApF,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,uCAAuC,ICAvE,IAAAC,GAAAC,EAAA,CAAAC,GAAAC,KAAA,CAAAA,GAAO,QAAU,eAAe,mCAAmC,ICAnE,IAAAC,GAAmB,UACnBC,GAAsC,SCEtC,IAAAC,GAA4B,SACrBC,MAAQ,kBAAc,IAAc,GAAAC,KAAK,SAAU,CACxD,GAAI,MACJ,GAAI,OACJ,EAAG,KACL,EAAG,GAAG,KAAgB,GAAAA,KAAK,SAAU,CACnC,GAAI,OACJ,GAAI,KACJ,EAAG,GACL,EAAG,GAAG,KAAgB,GAAAA,KAAK,SAAU,CACnC,GAAI,OACJ,GAAI,MACJ,EAAG,KACL,EAAG,GAAG,CAAC,EAAG,aAAa,EChBvB,IAAAC,GAAwB,UCAxB,IAAAC,GAAoC,SACpCC,GAAsB,SAiChBC,MAAwB,wBAC5B,wBACA,CASE,cAAe,CACb,KAAM,cACN,aAAc,CAAC,CACjB,EAMA,cAAe,CACb,KAAM,eACN,aAAc,CACZ,IAAK,wBACL,aAAc,aAChB,CACF,EAUA,uBAAwB,CACtB,KAAM,eACN,aAAc,CACZ,IAAK,GACL,aAAc,aAChB,CACF,EAaA,eAAgB,CACd,KAAM,aACN,MAAO,SAAM,YAAY,cAAe,CACtC,OACA,YACA,aACF,CAAC,EACD,aAAc,WAChB,EAQA,gBAAiB,CACf,KAAM,SACN,aAAc,EAChB,EAOA,oBAAqB,CACnB,KAAM,SACN,aAAc,CAAC,CACjB,EAcA,QAAS,CACP,KAAM,SACN,aAAc,IACd,SAAU,EACZ,EAMA,UAAW,CACT,KAAM,SACN,aAAc,IACd,SAAU,EACZ,CACF,EACA,CACE,gBAAiB,GAejB,mBAAoBC,GACXA,EAAK,IACR,CACE,GAAGA,EACH,cAAe,CACb,IAAKA,EAAK,IACV,QAASA,EAAK,OAChB,CACF,EACAA,CAER,CACF,EAIOC,GAAQF,GD9KA,SAARG,GAAwCC,EAA8B,CAC3EA,EAAc,eACZ,IACE,IAAI,GAAAC,QAAY,CACd,KAAM,wBACN,YAAa,6BACb,aAAAC,GAQA,oBAAqB,CACnB,cACA,cACA,mBACF,EACA,gBAAiB,CACf,SAAU,kBACZ,EACA,gBAAiB,IACf,OAAO,4CAA4B,EAAE,KAAKC,GAAKA,EAAE,OAAO,CAC5D,CAAC,CACL,CACF,CEhCA,IAAAC,GAA2B,UAC3BC,GAA8C,SAyD9C,SAASC,GAAkBC,EAA6C,CACtE,MACE,gBAAiBA,GACjB,OAAQA,EAA4B,aAAgB,UAExD,CAYA,IAAqBC,EAArB,cAAyC,gCAA6C,CAAtF,kCACE,UAAO,cAMP,MAAM,QAAQC,EAAqC,CAIjD,GAAM,CAAE,cAAAC,EAAe,OAAAC,EAAQ,UAAAC,EAAW,KAAAC,EAAM,OAAAC,CAAO,EAAIL,EAErD,CAAE,YAAAM,CAAY,EAAI,QAAM,eAC5B,KAAK,cACLH,EACAF,CACF,EACA,OAAIJ,GAAkBS,CAAW,EACxBA,EAAY,YAAYJ,EAAQ,CAAE,GAAGE,EAAM,OAAAC,CAAO,CAAC,GAI5D,QAAQ,KACN,iBAAiBC,EAAY,YAAY,IAAI,iCAC/C,EACO,GACT,CACF,ECvGA,IAAAC,GAAqB,UAErBC,GAAqC,SACrCC,GAAsB,SAIhBC,MAAsB,SAAK,IAAM,OAAO,uCAAoB,CAAC,EAEpD,SAARC,GAAwCC,EAA8B,CAC3EA,EAAc,wBACZ,IACE,IAAI,wBAAqB,CACvB,KAAM,wBACN,YAAa,4BACb,eAAgBF,GAChB,WAAY,SAAM,MAAM,CAAC,CAAC,CAC5B,CAAC,CACL,CACF,CCnBA,IAAAG,GAA8B,SCU9B,IAAIC,GAEW,SAARC,IAA+B,CACpC,OAAAD,KAAW,OAAO,qCAA0B,EACzC,KAAKE,GAAOA,EAAI,QAAQ,CAAC,EACzB,MAAO,GAAe,CAErB,MAAAF,GAAS,OACH,CACR,CAAC,EACIA,EACT,CDEA,IAAqBG,EAArB,cAAgD,gBAAoC,CAApF,kCACE,UAAO,qBAEP,MAAM,QAAQC,EAA4C,CACxD,GAAM,CAAE,MAAAC,EAAO,QAAAC,EAAS,eAAAC,CAAe,EAAIH,EAE3CG,IAAiB,uBAAuB,EACxC,IAAMC,EAAS,MAAMC,GAAY,EAEjCF,IAAiB,kBAAkB,EACnC,IAAMG,EAAY,YAAY,IAAI,EAC5BC,EAASH,EAAO,cAAcH,EAAOC,CAAO,EAC5CM,EAAW,YAAY,IAAI,EAAIF,EAErC,MAAO,CAAE,OAAAC,EAAQ,SAAAC,CAAS,CAC5B,CACF,EEvCA,IAAAC,GAAqB,UAErBC,GAAqB,UACrBC,GAAiC,UCHjC,IAAAC,GAA+B,SAC/BC,GAA8B,UAC9BC,GAA2B,SAC3BC,EAAsB,SCHtB,IAAAC,EAA+B,SAC/BC,GAAuC,UACvCC,EAOO,SACPC,GAA6B,UAC7BC,EAAkD,SAClDC,GAAqC,UACrCC,GAAuB,UACvBC,EAA6C,UC6B7C,IAAMC,GAAiB,GACjBC,GAAc,EAEdC,GAAiB,IAEhB,SAASC,GACdC,EACAC,EACAC,EACAC,EAAsCC,GAAQA,EAC9CC,EAAiC,CAAC,EACpB,CACd,IAAMC,EAAS,IAAI,IAAIN,EAAM,MAAM,IAAIO,GAAK,CAACA,EAAE,KAAMA,CAAC,CAAC,CAAC,EAClDC,EAAsB,CAAC,EAC7B,QAAWC,KAAUR,EAAS,CAC5B,IAAMS,EAAkB,CAAC,EACrBC,EAAO,IACPC,EAAO,KACPC,EAAO,IACLC,EAAoB,CAAC,EACrBC,EAAsB,CAAC,EAC7B,QAAWX,KAAQY,GAAiBP,CAAM,EAAG,CAC3C,IAAMQ,EAAOX,EAAO,IAAIF,CAAI,EACtBc,EAAOD,GAAQf,EAAUe,EAAK,EAAE,EACtC,GAAI,GAACA,GAAQ,CAACC,GAAM,QAGpB,IACEC,EAAWF,CAAI,IACdA,EAAK,OAAO,MAAQR,EAAO,OAASQ,EAAK,OAAO,OAASR,EAAO,KACjE,CACAM,EAAK,KAAKG,EAAK,KAAK,MAAMA,EAAK,OAAS,CAAC,CAAC,CAAE,EAC5C,QACF,CACAJ,EAAQ,KAAKG,EAAK,EAAE,EAEpBP,EAAM,KAAKU,GAAQF,EAAK,SAAW,EAAI,CAACA,EAAK,CAAC,EAAIA,EAAK,CAAC,CAAE,EAAIA,CAAI,CAAC,EACnE,QAAWG,KAAKH,EACdP,EAAO,KAAK,IAAIA,EAAMU,EAAE,CAAC,EACzBT,EAAO,KAAK,IAAIA,EAAMS,EAAE,CAAC,EACzBR,EAAO,KAAK,IAAIA,EAAMQ,EAAE,CAAC,EAE7B,CACA,GAAIP,EAAQ,SAAW,EACrB,SAEF,IAAMQ,EAAU,CAAE,GAAIX,EAAOC,GAAQ,EAAG,EAAGC,CAAK,EAC1CU,EAASR,EAAK,OAChB,CACE,EAAGA,EAAK,OAAO,CAACS,EAAGH,IAAMG,EAAIH,EAAE,EAAG,CAAC,EAAIN,EAAK,OAC5C,EAAGA,EAAK,OAAO,CAACS,EAAGH,IAAMG,EAAIH,EAAE,EAAG,CAAC,EAAIN,EAAK,MAC9C,EACAO,EAKEG,EAAU,IAAI,IACpB,QAAWC,KAASjB,EAAO,QAAU,CAAC,EACpC,QAAWkB,KAAM,IAAI,IAAID,EAAM,KAAK,EAClCD,EAAQ,IAAIE,GAAKF,EAAQ,IAAIE,CAAE,GAAK,GAAK,CAAC,EAG9C,IAAMC,EAAuB,CAAC,EAC9B,QAAWF,KAASjB,EAAO,QAAU,CAAC,EAAG,CACvC,GAAIA,EAAO,oBAAsBX,GAC/B,MAEF,GAAI4B,EAAM,MAAM,SAAW,EACzB,SAEF,IAAMG,EAAS,KAAK,IAAI,GAAGH,EAAM,MAAM,IAAIC,GAAMF,EAAQ,IAAIE,CAAE,CAAE,CAAC,EAC9DG,EACAC,EAAM,GACV,QAAWJ,KAAMD,EAAM,MACrB,GAAID,EAAQ,IAAIE,CAAE,IAAME,EAGxB,QAAWR,KAAKnB,EAAUyB,CAAE,GAAK,CAAC,EAAG,CACnC,IAAMK,EAAI,KAAK,MAAMX,EAAE,EAAIE,EAAO,EAAGF,EAAE,EAAIE,EAAO,CAAC,EAC/CS,EAAID,IACNA,EAAMC,EACNF,EAAKT,EAET,CAEES,GACFF,EAAO,KAAK,CAAE,MAAAF,EAAO,GAAAI,EAAI,KAAMG,GAAUP,EAAOvB,CAAS,CAAE,CAAC,CAEhE,CACAK,EAAM,KAAK,CACT,OAAAC,EACA,GAAGyB,EAAezB,EAAQJ,CAAO,EACjC,KAAMK,EAAM,KAAK,EAAE,EACnB,QAAAY,EACA,QAASR,EAAQ,OACjB,QAAAA,EACA,MAAOA,EAAQ,QAAUlB,GAAiBI,EAAM,MAAM,OACtD,OAAA4B,CACF,CAAC,CACH,CACA,OAAOpB,CACT,CAEA,SAASyB,GAAUP,EAAoBvB,EAAqC,CAC1E,IAAMgC,EAAQ,CAAC,GAAG,IAAI,IAAIT,EAAM,MAAM,IAAIvB,CAAS,CAAC,CAAC,EAAE,KAAK,EACtDiC,EAAQD,EAAM,MAAM,EAAGtC,EAAW,EAAE,KAAK,IAAI,EAC7CwC,EACJF,EAAM,OAAStC,GAAc,KAAKsC,EAAM,OAAStC,EAAW,GAAK,GACnE,MAAO,GAAGuC,CAAK,GAAGC,CAAI,SAAMC,GAASZ,EAAM,EAAE,CAAC,EAChD,CCvHO,SAASa,GAAiBC,EAAiC,CAChE,GAAI,CAACA,EAAM,MAAM,KAAKC,CAAU,EAC9B,MAAO,CAAC,EAEV,IAAMC,EAAO,IAAI,IAAIF,EAAM,MAAM,IAAIG,GAAK,CAACA,EAAE,GAAIA,CAAC,CAAC,CAAC,EAC9CC,EAAQC,GAAeL,CAAK,EAC5BM,EAAU,IAAI,IAAIF,EAAM,IAAI,CAACG,EAAIC,IAAM,CAACD,EAAIC,CAAC,CAAC,CAAC,EAG/CC,EAAQ,IAAI,WAAWL,EAAM,OAAS,CAAC,EAGvCM,EAAO,IAAI,IACXC,EAAS,IAAI,IACnB,QAAWC,KAAKZ,EAAM,MAAO,CAC3B,IAAMa,EAAIP,EAAQ,IAAIM,EAAE,IAAI,EACtBE,EAAIR,EAAQ,IAAIM,EAAE,EAAE,EAC1B,GAAIC,IAAM,QAAaC,IAAM,QAAaD,IAAMC,EAC9C,SAEFH,EAAO,IAAI,GAAGC,EAAE,IAAI,IAAIA,EAAE,EAAE,EAAE,EAAE,IAAI,GAAGA,EAAE,EAAE,IAAIA,EAAE,IAAI,EAAE,EACvD,GAAM,CAACG,EAAIC,CAAE,EAAIH,EAAIC,EAAI,CAACD,EAAGC,CAAC,EAAI,CAACA,EAAGD,CAAC,EACvCJ,EAAMM,EAAK,CAAC,IACZN,EAAMO,CAAE,IACR,GAAM,CAACC,EAAMC,CAAE,EAAIL,EAAIC,EAAI,CAACF,EAAE,KAAMA,EAAE,EAAE,EAAI,CAACA,EAAE,GAAIA,EAAE,IAAI,GACvDF,EAAK,IAAIO,CAAI,GAAKP,EAAK,IAAIO,EAAM,IAAI,GAAK,EAAE,IAAIA,CAAI,GAAI,IAAIC,CAAE,CAClE,CACA,QAASV,EAAI,EAAGA,EAAIC,EAAM,OAAQD,IAChCC,EAAMD,CAAC,GAAMC,EAAMD,EAAI,CAAC,EAM1B,IAAMW,EAAWnB,EAAM,MACpB,OAAOC,CAAU,EACjB,KAAK,CAACY,EAAGC,IAAMD,EAAE,OAAO,MAAQC,EAAE,OAAO,KAAK,EAC3CM,EAAO,IAAI,IAAI,CAACD,EAAS,CAAC,EAAG,GAAIA,EAAS,GAAG,EAAE,EAAG,EAAE,CAAC,EACrDE,EAAaF,EAChB,IAAIG,GAAQ,CAACA,EAAMhB,EAAQ,IAAIgB,EAAK,EAAE,CAAE,CAAU,EAClD,OAAO,CAAC,CAACA,EAAMd,CAAC,IAAMY,EAAK,IAAIE,EAAK,EAAE,GAAKb,EAAMD,CAAC,IAAM,CAAC,EACzD,KAAK,CAAC,CAACK,EAAGU,CAAE,EAAG,CAACT,EAAGU,CAAE,IAAMD,EAAKC,GAAMX,EAAE,OAAO,MAAQC,EAAE,OAAO,KAAK,EAClEW,EAAYzB,EAAM,OAAO,IAAI0B,GAAK,CACtC,IAAMC,EAAK,IAAI,IACf,OAAAD,EAAE,QAAQ,QAAQ,CAACnB,EAAIC,IAAM,CACtBmB,EAAG,IAAIpB,CAAE,GACZoB,EAAG,IAAIpB,EAAIC,CAAC,CAEhB,CAAC,EACMmB,CACT,CAAC,EAEKC,EAA6B,CAAC,EACpC,QAASpB,EAAI,EAAGA,EAAI,EAAIa,EAAW,OAAQb,IAAK,CAC9C,GAAM,CAACqB,EAAOC,CAAE,EAAIT,EAAWb,CAAC,EAC1B,CAACuB,EAAKC,CAAE,EAAIX,EAAWb,EAAI,CAAC,EAC5ByB,EAAW7B,EAAM,MAAM0B,EAAK,EAAGE,CAAE,EACvC,GAAIC,EAAS,SAAW,EACtB,SAGF,IAAMC,EAAO,IAAI,IAAoB,CAAC,CAACL,EAAM,GAAI,CAAE,IAAK,EAAG,IAAK,EAAG,EAAG,CAAE,CAAC,CAAC,CAAC,EAC3E,QAAWtB,IAAM,CAACsB,EAAM,GAAI,GAAGI,CAAQ,EAAG,CACxC,IAAME,EAAMD,EAAK,IAAI3B,CAAE,EACvB,GAAK4B,EAGL,QAAWC,KAAK1B,EAAK,IAAIH,CAAE,GAAK,CAAC,EAAG,CAElC,GADWD,EAAQ,IAAI8B,CAAC,EACfJ,EACP,SAEF,IAAMK,GAAMD,IAAML,EAAI,GAAK,EAAI7B,EAAK,IAAIkC,CAAC,EAAG,OACtCE,GAAOJ,EAAK,IAAIE,CAAC,EACvBF,EAAK,IAAIE,EAAG,CACV,IAAK,KAAK,IAAIE,IAAM,KAAO,IAAUH,EAAI,IAAME,EAAG,EAClD,IAAK,KAAK,IAAIC,IAAM,KAAO,KAAWH,EAAI,IAAME,EAAG,EACnD,EAAG,KAAK,IAAIE,GAAsBJ,EAAI,GAAKG,IAAM,GAAK,EAAE,CAC1D,CAAC,CACH,CACF,CACA,IAAME,EACJf,GAAagB,GAAWzC,EAAOE,EAAMuB,EAAWI,EAAM,GAAIE,EAAI,EAAE,EAC5DW,EAAUF,GAAQ,EAAIA,EAAS,OAC/BG,EAASD,GAAWR,EAAK,IAAIH,EAAI,EAAE,GAAK,CAAE,IAAK,EAAG,IAAK,EAAG,EAAG,CAAE,EAI/Da,EAAaJ,IAAW,QAAaA,EAAO,KAAO,EAEnDK,EAAWhB,EAAM,OAAO,MAAQA,EAAM,OACtCiB,EAASf,EAAI,OAAO,MAGpBgB,EAAQ,CACZlB,EAAM,GACN,GAAGI,EAAS,OAAO1B,GAAMN,EAAWC,EAAK,IAAIK,CAAE,CAAE,CAAC,EAClDwB,EAAI,EACN,EACMiB,GAAcD,EAAM,KACxB,CAACxC,EAAI0C,IAAMA,EAAI,GAAK,CAACtC,EAAO,IAAI,GAAGoC,EAAME,EAAI,CAAC,CAAC,IAAI1C,CAAE,EAAE,CACzD,EACM2C,GAAUF,IAAeJ,EACzBO,GAAWH,IAAe,CAACN,EACjCd,EAAQ,KAAK,CACX,QAASC,EAAM,OAAO,QACtB,MAAOgB,EACP,IAAKC,EACL,aAAcb,EAAS,OAAS,EAChC,UAAWU,EAAO,EAIlB,UAAW,GACX,qBAAsBQ,GAAWL,EAASD,EAAWF,EAAO,IAC5D,oBAAqBQ,GAAWL,EAASD,EAAWF,EAAO,IAC3D,SAAU,CAACd,EAAO,GAAGI,EAAS,IAAI1B,GAAML,EAAK,IAAIK,CAAE,CAAE,EAAGwB,CAAG,EACxD,IAAI5B,GAAKA,EAAE,IAAI,EACf,KAAK,GAAG,EACX,eAAgB,OAChB,cAAe,OACf,QAAA+C,GACA,OAAQR,GAAS,MACnB,CAAC,CACH,CACA,OAAOd,CACT,CASA,SAASa,GACPzC,EACAE,EACAuB,EACA2B,EACAC,EACgE,CAChE,IAAMC,EAAO,IAAI,IACbC,EAAM,IACNC,EAAM,KACNC,EAAO,EACX,OAAAzD,EAAM,MAAO,QAAQ,CAAC,EAAGiD,IAAM,CAC7B,IAAMnB,EAAKL,EAAUwB,CAAC,EAAG,IAAIG,CAAO,EAC9BpB,EAAKP,EAAUwB,CAAC,EAAG,IAAII,CAAK,EAClC,GAAIvB,IAAO,QAAaE,IAAO,OAAW,EACpCF,IAAO,QAAaE,IAAO,SAC7ByB,IAEF,MACF,CAGA,IAAMC,EAAQ,EAAE,QAAQ,MAAM,KAAK,IAAI5B,EAAIE,CAAE,EAAI,EAAG,KAAK,IAAIF,EAAIE,CAAE,CAAC,EAChEA,EAAKF,GACP4B,EAAM,QAAQ,EAEhB,IAAIC,EAAK,EACT,QAAWpD,KAAMmD,EACfC,GAAMzD,EAAK,IAAIK,CAAE,GAAG,QAAU,EAEhC,IAAMqD,EAAMF,EAAM,KAAK,GAAG,EACpBG,EAAQP,EAAK,IAAIM,CAAG,GAAK,CAAE,MAAAF,EAAO,GAAAC,EAAI,MAAO,CAAC,CAAE,EACtDE,EAAM,MAAM,KAAK,EAAE,IAAI,EACvBP,EAAK,IAAIM,EAAKC,CAAK,EACnBN,EAAM,KAAK,IAAIA,EAAKI,CAAE,EACtBH,EAAM,KAAK,IAAIA,EAAKG,CAAE,CACxB,CAAC,EACML,EAAK,MAAQG,EAChB,CAAE,IAAAF,EAAK,IAAAC,EAAK,EAAGF,EAAK,KAAM,KAAAG,EAAM,OAAQ,CAAC,GAAGH,EAAK,OAAO,CAAC,CAAE,EAC3D,MACN,CCzMO,SAASQ,GAAeC,EAAcC,EAA6B,CACxE,IAAMC,EAAO,IAAI,IAAID,CAAU,EACzBE,EAAQH,EAAM,MAAM,OAAOI,GAAKF,EAAK,IAAIE,EAAE,IAAI,CAAC,EAChDC,EAAM,IAAI,IAAIF,EAAM,IAAIC,GAAKA,EAAE,EAAE,CAAC,EAClCE,EAAQN,EAAM,OAChB,IAAIO,IAAM,CAAE,GAAGA,EAAG,QAASA,EAAE,QAAQ,OAAOC,GAAMH,EAAI,IAAIG,CAAE,CAAC,CAAE,EAAE,EAClE,OAAOD,GAAKA,EAAE,QAAQ,OAAS,CAAC,EACnC,MAAO,CACL,KAAMP,EAAM,KACZ,MAAAG,EACA,MAAOH,EAAM,MAAM,OAAOS,GAAKJ,EAAI,IAAII,EAAE,IAAI,GAAKJ,EAAI,IAAII,EAAE,EAAE,CAAC,EAC/D,GAAIH,GAAO,OAAS,CAAE,MAAAA,CAAM,EAAI,CAAC,EACjC,YAAaN,EAAM,YACnB,WAAYA,EAAM,WAClB,WAAYA,EAAM,WAClB,cAAeA,EAAM,aACvB,CACF,CCVO,IAAMU,GAAqB,IAAI,IAAI,CACxC,mBACA,cACA,kBACA,aACA,gBACA,eACF,CAAC,EAEKC,GAAkB,0CAKjB,SAASC,GAEdC,EAAaC,EAAe,CAC5B,GAAIA,EACF,OAAOD,EAAO,KAAKE,GAAKA,EAAE,UAAYD,CAAK,EAE7C,IAAME,EAAaH,EAAO,OAAOE,GAAKL,GAAmB,IAAIK,EAAE,WAAW,CAAC,EAC3E,OACEC,EAAW,KAAKD,GACdJ,GAAgB,KAAK,GAAGI,EAAE,OAAO,IAAIA,EAAE,MAAQ,EAAE,EAAE,CACrD,GAAKC,EAAW,CAAC,CAErB,CAIA,SAASC,EAAMC,EAAgBC,EAAuB,CACpD,OAAO,OAAQD,EAAc,KAAQ,WAChCA,EAAc,IAAIC,CAAI,EACtBD,EAA8BC,CAAI,CACzC,CAEA,SAASC,GAAQF,EAAgBG,EAAwC,CAC1DJ,EAAMC,EAAG,MAAM,IACf,QACXG,EAAK,KAAK,CACR,MAAOJ,EAAMC,EAAG,OAAO,EACvB,IAAKD,EAAMC,EAAG,KAAK,CACrB,CAAC,EAEH,IAAMI,EAAWL,EAAMC,EAAG,aAAa,EACvC,QAAWK,KAASD,GAAY,CAAC,EAC/BF,GAAQG,EAAOF,CAAI,CAEvB,CAEA,SAASG,GAAOC,EAA6C,CAC3D,IAAMC,EAAS,CAAC,GAAGD,CAAS,EAAE,KAAK,CAACE,EAAGC,IAAMD,EAAE,MAAQC,EAAE,KAAK,EACxDC,EAAwC,CAAC,EAC/C,QAAWC,KAAMJ,EAAQ,CACvB,IAAMK,EAAOF,EAAI,GAAG,EAAE,EAClBE,GAAQD,EAAG,OAASC,EAAK,IAC3BA,EAAK,IAAM,KAAK,IAAIA,EAAK,IAAKD,EAAG,GAAG,EAEpCD,EAAI,KAAK,CAAE,GAAGC,CAAG,CAAC,CAEtB,CACA,OAAOD,CACT,CAKO,SAASG,GAAeC,EAAsC,CACnE,IAAMC,EAAqB,CAAC,EAC5B,QAAWhB,KAAKe,EAAU,CACxB,IAAMd,EACHF,EAAMC,EAAG,WAAW,GACpBD,EAAMC,EAAG,MAAM,GACfD,EAAMC,EAAG,IAAI,EACViB,EAAQlB,EAAMC,EAAG,OAAO,EACxBkB,EAAMnB,EAAMC,EAAG,KAAK,EAC1B,GAAI,CAACC,GAAQ,EAAEiB,EAAMD,GACnB,SAEF,IAAME,EAA0C,CAAC,EACjDjB,GAAQF,EAAGmB,CAAK,EAChBH,EAAM,KAAK,CACT,KAAAf,EACA,QAASF,EAAMC,EAAG,SAAS,EAC3B,MAAAiB,EACA,IAAAC,EACA,OAASnB,EAAMC,EAAG,QAAQ,GAA4B,EACtD,MAAOmB,EAAM,OAASb,GAAOa,CAAK,EAAI,CAAC,CAAE,MAAAF,EAAO,IAAAC,CAAI,CAAC,CACvD,CAAC,CACH,CACA,OAAOF,CACT,CC/EA,SAASI,GAAOC,EAAc,CAC5B,OAAOA,EAAK,MAAM,GAAG,EAAE,GAAG,EAAE,CAC9B,CAUO,SAASC,GACdC,EACAC,EACAC,EACW,CAKX,IAAMC,EAAW,IAAI,IACfC,EAAW,IAAI,IACrB,QAAWC,KAAQL,EAAM,MACvB,GAAIM,EAAWD,CAAI,GAAKH,EAAUG,EAAK,EAAE,GAAG,OAAQ,CAClD,GAAM,CAAE,QAAAE,CAAQ,EAAIF,EAAK,OACnBP,EACJM,EAAS,IAAIG,CAAO,GACpBH,EAAS,IAAIG,EAASV,GAAOU,CAAO,CAAC,EAAE,IAAIA,CAAO,EAC9CC,EACJL,EAAS,IAAIL,CAAI,GACjBK,EAAS,IAAIL,EAAM,CAAE,MAAO,CAAC,EAAG,MAAO,CAAE,CAAC,EAAE,IAAIA,CAAI,EACtDU,EAAM,MAAM,KAAKH,CAAI,EACrBG,EAAM,MAAQ,KAAK,IAAIA,EAAM,MAAOH,EAAK,MAAM,CACjD,CAEF,OAAW,CAAE,MAAAI,CAAM,IAAKN,EAAS,OAAO,EACtCM,EAAM,KAAK,CAACC,EAAGC,IAAMD,EAAE,OAAO,MAAQC,EAAE,OAAO,KAAK,EAEtD,IAAMC,EAAkB,CAAC,EACzB,QAAWC,KAAQZ,EAAO,CACxB,IAAMa,EAAWX,EAAS,IAAIN,GAAOgB,EAAK,OAAO,CAAC,EAClD,GAAI,CAACC,EACH,SAEF,IAAMC,EAAkB,CAAC,EACrBC,EACAC,EAAa,IACbC,EAAU,EACRC,GAAON,EAAK,MAAQA,EAAK,KAAO,EAChC,CAAE,MAAAJ,EAAO,MAAAW,CAAM,EAAIN,EACzB,QACMO,EAAIC,GAAmBb,EAAOI,EAAK,MAAQO,CAAK,EACpDC,EAAIZ,EAAM,QAAUA,EAAMY,CAAC,EAAG,OAAO,MAAQR,EAAK,IAClDQ,IACA,CACA,IAAMhB,EAAOI,EAAMY,CAAC,EACdE,EAAYlB,EAAK,OAAO,MACxBmB,EAAUD,EAAYlB,EAAK,OACjC,GAAImB,GAAWX,EAAK,MAClB,SAEF,IAAMY,EAAOvB,EAAUG,EAAK,EAAE,EAC9Ba,GAAW,KAAK,IAAIM,EAASX,EAAK,GAAG,EAAI,KAAK,IAAIU,EAAWV,EAAK,KAAK,EACvE,QAAWa,KAAQb,EAAK,MAAO,CAC7B,IAAMH,EAAI,KAAK,IAAIgB,EAAK,MAAOH,CAAS,EAClCZ,EAAI,KAAK,IAAIe,EAAK,IAAKF,CAAO,EACpC,GAAIb,GAAKD,EACP,SAEF,IAAMiB,EAAUC,GACdH,GACCf,EAAIa,GAAalB,EAAK,QACtBM,EAAIY,GAAalB,EAAK,MACzB,EACIsB,EAAQ,SAAW,GACrBA,EAAQ,KAAK,CAAE,GAAGA,EAAQ,CAAC,CAAG,CAAC,EAEjCZ,EAAM,KAAKc,GAAQF,CAAO,CAAC,CAC7B,CACA,IAAMG,EAAQ,KAAK,IAAI,KAAK,IAAIX,EAAKI,CAAS,EAAGC,CAAO,EAClDO,EAAW,KAAK,IAAID,EAAQX,CAAG,EACrC,GAAIY,EAAWd,EAAY,CACzBA,EAAac,EACb,GAAM,CAACC,CAAC,EAAIJ,GACVH,GACCK,EAAQP,GAAalB,EAAK,QAC1ByB,EAAQP,GAAalB,EAAK,MAC7B,EACAW,EAAKgB,CACP,CACF,CACIhB,GACFJ,EAAK,KAAK,CACR,KAAAC,EACA,MAAOE,EAAM,KAAK,EAAE,EACpB,GAAAC,EACA,QAASE,GAAWL,EAAK,IAAMA,EAAK,MACtC,CAAC,CAEL,CACA,OAAOD,CACT,CClGO,SAASqB,GAAaC,EAAmC,CAC9D,GAAI,OAAOA,GAAS,UAAYA,IAAS,KACvC,MAAO,GAET,IAAMC,EAAID,EACV,OACE,OAAOC,EAAE,kBAAqB,YAC9B,OAAOA,EAAE,QAAW,YACpB,wBAAyBA,GACzB,kBAAmBA,CAEvB,CAEA,SAASC,GAAOC,EAA8B,CAC5C,OAAOA,EAAO,OACZ,CAACC,EAAMC,IAAOD,GAAQA,EAAK,IAAMA,EAAK,OAASC,EAAE,IAAMA,EAAE,MAAQD,EAAOC,EACxE,MACF,CACF,CAmBO,SAASC,GAAUN,EAAkBO,EAAwB,CAClE,IAAMC,EAAQN,GACZF,EAAK,cAAc,cAAc,OAC/BK,GACEA,EAAE,UAAYE,EAAO,SAAWF,EAAE,eAAiBE,EAAO,YAC9D,CACF,EACM,CAAE,QAAAE,CAAQ,EAAIT,EACpB,OAAOQ,GAASC,EAAU,EACtB,CACE,MAAO,EAAIA,EACX,WAAYD,EAAM,SAAWR,EAAK,SAAWQ,EAAM,MAAQC,CAC7D,EACA,MACN,CAIO,SAASC,GAAWV,EAA0C,CACnE,IAAMQ,EAAQN,GAAOF,EAAK,cAAc,aAAa,EAC/CW,EACJH,GAAO,uBAAyB,OAC5B,OACAR,EAAK,iBAAiBQ,EAAM,oBAAoB,EACtD,OAAOA,GAASR,EAAK,QAAU,EAC3B,CACE,QAASQ,EAAM,QACf,aAAcA,EAAM,aACpB,MAAOA,EAAM,MACb,IAAKA,EAAM,IACX,QAASR,EAAK,QACd,KAAMA,EAAK,QAAUA,EAAK,MAC1B,YAAaW,GAAW,OAASH,EAAM,MACvC,UAAWG,GAAW,KAAOH,EAAM,GACrC,EACA,MACN,CAIO,SAASI,GACdC,EACAC,EACAC,EAAU,GACM,CAChB,IAAMC,EAAUH,EAAO,IAAMA,EAAO,MAC9BI,EAASF,EACX,KAAK,IAAI,EAAG,KAAK,IAAIF,EAAO,MAAOC,EAAQE,GAAW,CAAC,CAAC,EACxD,EACAE,EAAQ,KAAK,IAAIL,EAAO,YAAa,KAAK,MAAMA,EAAO,MAAQI,CAAM,CAAC,EACtEE,EAAM,KAAK,IAAIN,EAAO,UAAW,KAAK,KAAKA,EAAO,IAAMI,CAAM,CAAC,EAG7DG,EAAOD,EAAMD,EAAQJ,EAC3B,OAAIM,EAAO,IACTF,GAAS,KAAK,MAAME,EAAO,CAAC,EAC5BD,GAAO,KAAK,KAAKC,EAAO,CAAC,GAEpB,CACL,QAASP,EAAO,QAChB,aAAcA,EAAO,aACrB,MAAAK,EACA,IAAAC,CACF,CACF,CAIO,SAASE,GACdC,EACAT,EACAE,EAAU,GACV,CACA,GACEO,GAAK,UAAYT,EAAO,SACxBS,EAAI,eAAiBT,EAAO,aAE5B,MAAO,GAGT,GAAIE,EACF,OAAOO,EAAI,OAAST,EAAO,MAAQ,GAAKA,EAAO,KAAOS,EAAI,IAAM,EAElE,IAAMC,EAAQX,GAAQC,EAAQ,IAAU,EAAK,EAC7C,OACE,KAAK,IAAIS,EAAI,MAAQC,EAAM,KAAK,EAAI,GAAK,KAAK,IAAID,EAAI,IAAMC,EAAM,GAAG,EAAI,CAE7E,CCnJA,SAASC,GAASC,EAAaC,EAAuC,CACpE,IAAMC,EAAUF,EAAI,QAAQ,GAAG,EACzBG,EAAUH,EAAI,QAAQ,IAAKE,EAAU,CAAC,EAC5C,GAAIA,IAAY,IAAMC,IAAY,GAAI,CACpC,IAAMC,EAAOJ,EAAI,MAAM,EAAGE,CAAO,EAC3BG,EAAOL,EAAI,MAAME,EAAU,EAAGC,CAAO,EACrCG,EAAMN,EAAI,MAAMG,EAAU,CAAC,EAC7BE,IAAS,KAAOA,IAAS,IAC3BJ,EAAKG,CAAI,EAAI,CAACE,EACLD,IAAS,MAClBJ,EAAKG,CAAI,EAAIE,EAEjB,CACF,CAEA,SAASC,GAAUC,EAAkB,CACnC,IAAMP,EAAwC,CAAC,EAC/C,QAAWQ,KAASD,EAClBT,GAASU,EAAOR,CAAI,EAEtB,OAAOA,CACT,CAwBO,SAASS,GAAiBC,EAA6C,CAC5E,GAAM,CAAE,GAAAC,EAAI,GAAAC,EAAI,GAAAC,CAAG,EAAIH,EAAK,KAC5B,OAAO,OAAOC,GAAO,UACnB,OAAOC,GAAO,UACd,OAAOC,GAAO,SACZ,CAAE,QAASF,EAAI,MAAOC,EAAI,KAAMC,CAAG,EACnC,MACN,CAgCA,IAAMC,GAAK,GACLC,GAAK,GAUX,SAASC,GAAcC,EAAc,CACnC,IAAMC,EAA6B,CAAC,EAChCC,EAAS,IACTC,EAAQ,EAEZ,SAASC,EAAKC,EAAa,CACrBA,EAAMF,GACRF,EAAS,KAAK,CAAE,GAAID,EAAK,MAAMG,EAAOE,CAAG,EAAG,OAAAH,CAAO,CAAC,CAExD,CACA,QAASI,EAAI,EAAGA,EAAIN,EAAK,OAAQM,IAAK,CACpC,IAAMC,EAAKP,EAAK,WAAWM,CAAC,GACxBC,IAAOV,IAAMU,IAAOT,MACtBM,EAAKE,CAAC,EACNJ,EAASK,IAAOV,GAAK,IAAM,IAC3BM,EAAQG,EAAI,EAEhB,CACA,OAAAF,EAAKJ,EAAK,MAAM,EACTC,CACT,CAWO,SAASO,GAASC,EAAc,CACrC,IAAMC,EAAkB,CACtB,MAAO,CAAC,EACR,MAAO,CAAC,EACR,MAAO,CAAC,EACR,MAAO,CAAC,EACR,OAAQ,CAAC,EACT,GAAI,EACN,EAQA,QAAWC,KAAWF,EAAK,MAAM;AAAA,CAAI,EAAG,CACtC,IAAMG,EAAOD,EAAQ,SAAS,IAAI,EAAIA,EAAQ,MAAM,EAAG,EAAE,EAAIA,EAC7D,GAAIC,EAAK,WAAW,GAAG,EAAG,CACxB,GAAM,CAAC,CAAE,GAAGC,CAAI,EAAID,EAAK,MAAM,GAAI,EACnCF,EAAM,OAAO,KAAKrB,GAAUwB,CAAI,CAAC,CACnC,SAAWD,EAAK,WAAW,GAAG,EAAG,CAC/B,GAAM,CAAC,CAAE1B,EAAM,GAAG2B,CAAI,EAAID,EAAK,MAAM,GAAI,EACrCE,EACAC,EACAC,EACAC,EAAO,GAILC,EAAQL,EAAK,CAAC,GAAK,IAIrB,QAAQ,KAAKK,CAAK,GACpBJ,EAAM,CAACI,EACPH,EAAMF,EAAK,CAAC,GAAK,IACjBG,EAAYH,EAAK,MAAM,CAAC,IAExBI,EAAO,GACPF,EAAMG,EAINJ,EAAMC,IAAQ,IAAM,EAAIA,EAAI,OAC5BC,EAAYH,EAAK,MAAM,CAAC,GAE1B,IAAM9B,EAAOM,GAAU2B,CAAS,EAC5BC,GAAQ,OAAQlC,IAClB+B,EAAM,CAAC/B,EAAK,IAEd2B,EAAM,MAAM,KAAK,CAAE,GAAIxB,EAAO,OAAQ4B,EAAK,SAAUC,EAAK,KAAAhC,CAAK,CAAC,CAClE,SAAW6B,EAAK,WAAW,GAAG,EAAG,CAI/B,GAAM,CAAC,CAAE,CAAEO,EAAQC,EAAQ,CAAE,CAAE,CAAE,CAAEC,EAAO,GAAGR,CAAI,EAAID,EAAK,MAAM,GAAI,EAChEO,GAAUC,GACZV,EAAM,MAAM,KAAK,CACf,OAAQS,EAAO,MAAM,EAAG,EAAE,EAC1B,OAAQC,EAAO,MAAM,EAAG,EAAE,EAC1B,QAASD,EAAO,GAAG,EAAE,EACrB,QAASC,EAAO,GAAG,EAAE,EACrB,MAAOC,GAAS,IAChB,KAAMhC,GAAUwB,CAAI,CACtB,CAAC,CAEL,SAAWD,EAAK,WAAW,GAAG,EAAG,CAC/B,GAAM,CAAC,CAAEO,EAAQG,EAASF,EAAQG,EAASF,EAAO,GAAGR,CAAI,EACvDD,EAAK,MAAM,GAAI,EACbO,GAAUC,GAAUG,GACtBb,EAAM,MAAM,KAAK,CACf,OAAAS,EACA,OAAAC,EACA,QAAAE,EACA,QAAAC,EACA,MAAOF,GAAS,IAChB,KAAMhC,GAAUwB,CAAI,CACtB,CAAC,CAEL,SAAWD,EAAK,WAAW,GAAG,EAAG,CAC/B,GAAM,CAAC,CAAEY,EAAQC,EAAKC,EAAQvB,EAAOE,EAAKL,EAAM,GAAGa,CAAI,EACrDD,EAAK,MAAM,GAAI,EACbY,GAAUC,GAAOC,GAAUvB,GAASE,GAAOL,GAC7CU,EAAM,MAAM,KAAK,CACf,OAAAc,EACA,UAAW,CAACC,EACZ,OAAAC,EACA,MAAOvB,IAAU,IAAM,GAAK,CAACA,EAC7B,IAAKE,IAAQ,IAAM,GAAK,CAACA,EACzB,SAAUN,GAAcC,CAAI,EAC5B,KAAMX,GAAUwB,CAAI,CACtB,CAAC,CAEL,SAAWD,EAAK,WAAW,GAAG,EAAG,CAC/B,GAAM,CAAC,CAAE1B,EAAMyC,EAAM,GAAGd,CAAI,EAAID,EAAK,MAAM,GAAI,EAC3C1B,GAAQyC,GACVjB,EAAM,MAAM,KAAK,CAAE,KAAAxB,EAAM,KAAAyC,EAAM,KAAAd,CAAK,CAAC,CAEzC,CACF,CACA,OAAOH,CACT,CCtNA,SAASkB,GAAeC,EAAoB,CAC1C,IAAMC,EAAY,IAAI,IAChBC,EAAa,IAAI,IACvB,SAASC,EAAMC,EAAYC,EAAmB,CACvCJ,EAAU,IAAIG,CAAE,GACnBH,EAAU,IAAIG,EAAIC,CAAM,CAE5B,CACA,SAASC,EAASF,EAAYC,EAAmB,CAC/CF,EAAMC,EAAIC,CAAM,EAChBH,EAAW,IAAIE,GAAKF,EAAW,IAAIE,CAAE,GAAK,GAAK,CAAC,CAClD,CACA,QAAWG,KAAQP,EAAS,MAC1BG,EAAMI,EAAK,OAAQA,EAAK,UAAY,IAAM,IAAM,GAAG,EACnDJ,EAAMI,EAAK,OAAQA,EAAK,UAAY,IAAM,IAAM,GAAG,EAErD,QAAWC,KAAWR,EAAS,MAC7B,QAAWS,KAAWD,EAAQ,KAAK,MAAM,GAAG,EAC1CF,EAASG,EAAQ,MAAM,EAAG,EAAE,EAAGA,EAAQ,SAAS,GAAG,EAAI,IAAM,GAAG,EAGpE,QAAWC,KAAQV,EAAS,MAC1B,QAAWW,KAAOD,EAAK,SACrBJ,EAASK,EAAI,GAAIA,EAAI,SAAW,IAAM,IAAM,GAAG,EAGnD,MAAO,CAAE,UAAAV,EAAW,WAAAC,CAAW,CACjC,CAMA,SAASU,GAAgBZ,EAAiC,CACxD,MAAO,CACL,GAAGA,EAAS,MAAM,IAAIa,IAAM,CAC1B,GAAGC,GAAWD,EAAE,IAAI,EACpB,MAAOA,EAAE,KAAK,MAAM,GAAG,EAAE,IAAIJ,IAAY,CACvC,GAAIA,EAAQ,MAAM,EAAG,EAAE,EACvB,OAAQA,EAAQ,SAAS,GAAG,EAAK,IAAiB,GACpD,EAAE,CACJ,EAAE,EACF,GAAGT,EAAS,MAAM,IAAIe,IAAM,CAC1B,KAAM,GAAGA,EAAE,MAAM,IAAIA,EAAE,SAAS,IAAIA,EAAE,MAAM,GAI5C,MAAOA,EAAE,QAAU,GAAK,EAAIA,EAAE,MAC9B,MAAOA,EAAE,SAAS,IAAIC,IAAM,CAC1B,GAAIA,EAAE,GACN,OAAQA,EAAE,SAAW,IAAO,IAAiB,GAC/C,EAAE,CACJ,EAAE,CACJ,CACF,CAaA,SAASC,GAAeC,EAAkB,CACxC,IAAMC,EAAKD,EAAQ,KAAK,GACxB,GAAI,OAAOC,GAAO,SAChB,OAEF,IAAMC,EAAUD,EACb,MAAM,GAAG,EACT,IAAIH,GAAKA,EAAE,KAAK,CAAC,EACjB,OAAO,OAAO,EACjB,OAAOI,EAAQ,OAAS,EAAIA,EAAU,MACxC,CAEA,IAAMC,GAAYC,GAChB,OAAOA,GAAM,UAAYA,EAAI,EAAIA,EAAI,OAMvC,SAASC,GAAY,CAAE,KAAAC,EAAM,OAAAC,CAAO,EAAY,CAC9C,IAAMC,EAAQL,GAASG,EAAK,EAAE,GAAKH,GAASG,EAAK,EAAE,GAAKH,GAASG,EAAK,EAAE,EACxE,OACEH,GAASG,EAAK,EAAE,GAChBH,GAASG,EAAK,EAAE,IACfE,IAAU,QAAaD,EAAS,EAAIC,EAAQD,EAAS,OAE1D,CAEA,SAASE,GACPT,EACAb,EACAuB,EACW,CACX,MAAO,CACL,GAAI,GAAGV,EAAQ,EAAE,GAAGb,CAAM,GAC1B,KAAMa,EAAQ,GACd,OAAQA,EAAQ,OAChB,MAAAU,EACA,OAAQC,GAAiBX,CAAO,EAChC,QAASD,GAAeC,CAAO,CACjC,CACF,CAEO,SAASY,GAAkB9B,EAAoB+B,EAAO,eAAgB,CAC3E,IAAMC,EAAqB,CAAC,EACtBC,EAAqB,CAAC,EAEtB,CAAE,UAAAhC,EAAW,WAAAC,CAAW,EAAIH,GAAeC,CAAQ,EAEnDkC,EAAUC,GACd,GAAGA,CAAS,GAAGlC,EAAU,IAAIkC,CAAS,GAAK,GAAG,GAEhD,QAAWjB,KAAWlB,EAAS,MAAO,CACpC,IAAM4B,EACJL,GAAYL,CAAO,GAAK,KAAK,IAAIhB,EAAW,IAAIgB,EAAQ,EAAE,GAAK,EAAG,CAAC,EACrEc,EAAM,KAAKL,GAAST,EAASjB,EAAU,IAAIiB,EAAQ,EAAE,GAAK,IAAKU,CAAK,CAAC,CACvE,CAEA,QAAWrB,KAAQP,EAAS,MAC1BiC,EAAM,KAAK,CACT,KAAMC,EAAO3B,EAAK,MAAM,EACxB,GAAI2B,EAAO3B,EAAK,MAAM,EACtB,WAAYA,EAAK,UAAY,IAAM,IAAM,IACzC,SAAUA,EAAK,UAAY,IAAM,IAAM,GACzC,CAAC,EAGH,IAAM6B,EAAqB,CAAC,EACtBC,EAAiB,IAAI,IAIrBC,EAAU,CAACC,EAAWC,IAAc,CAACD,EAAGC,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,EAEjE,SAASC,EAAgBC,EAAmBX,EAAc,CACxD,QAASY,EAAI,EAAGA,EAAID,EAAQ,OAAS,EAAGC,IAAK,CAC3C,IAAMC,EAAMN,EAAQI,EAAQC,CAAC,EAAID,EAAQC,EAAI,CAAC,CAAE,EAC3CN,EAAe,IAAIO,CAAG,GACzBP,EAAe,IAAIO,EAAK,IAAI,GAAK,EAEnCP,EAAe,IAAIO,CAAG,EAAG,IAAIb,CAAI,CACnC,CACF,CAEA,QAAWvB,KAAWR,EAAS,MAAO,CAGpC,IAAM0C,EAAUlC,EAAQ,KACrB,MAAM,GAAG,EACT,IAAIC,GAAWyB,EAAOzB,EAAQ,MAAM,EAAG,EAAE,CAAC,CAAC,EAC9C2B,EAAM,KAAK,CAAE,KAAM5B,EAAQ,KAAM,QAAAkC,CAAQ,CAAqB,EAC9DD,EAAgBC,EAASlC,EAAQ,IAAI,CACvC,CAEA,QAAWE,KAAQV,EAAS,MAAO,CACjC,IAAM0C,EAAUhC,EAAK,SAAS,IAAIC,GAAOuB,EAAOvB,EAAI,EAAE,CAAC,EAYjDoB,EAAO,GAAGrB,EAAK,MAAM,IAAIA,EAAK,SAAS,IAAIA,EAAK,MAAM,GAC5D0B,EAAM,KAAK,CACT,KAAAL,EACA,QAAAW,EACA,OAAQhC,EAAK,OACb,UAAWA,EAAK,UAChB,OAAQA,EAAK,MACf,CAAqB,EACrB+B,EAAgBC,EAASX,CAAI,CAC/B,CAEA,QAAWc,KAAQZ,EAAO,CACxB,IAAMa,EAAUT,EAAe,IAAIC,EAAQO,EAAK,KAAMA,EAAK,EAAE,CAAC,EAC1DC,GAAWA,EAAQ,KAAO,IAC5BD,EAAK,QAAU,CAAC,GAAGC,CAAO,EAE9B,CAKA,IAAMC,EAAU,IAAI,IAAI/C,EAAS,MAAM,IAAIgD,GAAK,CAACA,EAAE,GAAIA,EAAE,MAAM,CAAC,CAAC,EAC3DC,EACJjD,EAAS,MAAM,OAAS,GAAKA,EAAS,MAAM,OAAS,EACjDkD,GAAYtC,GAAgBZ,CAAQ,EAAGI,GAAM2C,EAAQ,IAAI3C,CAAE,GAAK,CAAC,EACjE,OAEN,MAAO,CACL,KAAA2B,EACA,MAAAC,EACA,MAAAC,EACA,MAAOG,EAAM,OAAS,EAAIA,EAAQ,OAClC,YAAaa,GAAW,YACxB,WAAYA,GAAW,WAGvB,WAAYjB,EAAM,KAAKgB,GAAKA,EAAE,MAAM,EAAI,OAAS,MACnD,CACF,CC5NO,SAASG,GAAkBC,EAAcC,EAAsB,CACpE,IAAMC,EAAWF,EAAM,MAAM,OAC3BG,GAAQC,EAAWD,CAAI,IAAMF,EAAUE,EAAK,EAAE,GAAG,QAAU,GAAK,CAClE,EACA,GAAID,EAAS,OAAS,EACpB,OAAOD,EAET,IAAMI,EAASH,EAAS,IAAIC,GAAQ,CAClC,IAAMG,EAAWL,EAAUE,EAAK,EAAE,EAC5BI,EAAMD,EAAS,KAAK,MAAMA,EAAS,OAAS,CAAC,CAAC,EACpD,MAAO,CACL,EAAGC,EAAI,EACP,EAAGA,EAAI,EACP,EAAGJ,EAAK,OAAQ,MAAQA,EAAK,OAAS,EACtC,EAAGA,EAAK,MACV,CACF,CAAC,EACKK,EAASH,EAAO,OAAO,CAACI,EAAKC,IAAMD,EAAMC,EAAE,EAAG,CAAC,EAC/CC,EAAKN,EAAO,OAAO,CAACI,EAAKC,IAAMD,EAAMC,EAAE,EAAIA,EAAE,EAAG,CAAC,EAAIF,EACrDI,EAAKP,EAAO,OAAO,CAACI,EAAKC,IAAMD,EAAMC,EAAE,EAAIA,EAAE,EAAG,CAAC,EAAIF,EACrDK,EAAKR,EAAO,OAAO,CAACI,EAAKC,IAAMD,EAAMC,EAAE,EAAIA,EAAE,EAAG,CAAC,EAAIF,EAEvDM,EAAM,EACNC,EAAM,EACV,QAAWL,KAAKL,EACdS,GAAOJ,EAAE,GAAKA,EAAE,EAAIC,IAAOD,EAAE,EAAIG,GACjCE,GAAOL,EAAE,GAAKA,EAAE,EAAIE,IAAOF,EAAE,EAAIG,GAGnC,IAAMG,EAAQ,CAAC,KAAK,MAAMD,EAAKD,CAAG,EAC5BG,EAAI,KAAK,IAAID,CAAK,EAClBE,EAAI,KAAK,IAAIF,CAAK,EAElBG,EAAiB,CAAC,EACxB,OAAW,CAACC,EAAId,CAAQ,IAAK,OAAO,QAAQL,CAAS,EACnDkB,EAAIC,CAAE,EAAId,EAAS,IAAII,GAAK,CAC1B,IAAMW,EAAIX,EAAE,EAAIC,EACVW,EAAIZ,EAAE,EAAIE,EAChB,MAAO,CAAE,EAAGD,EAAKM,EAAII,EAAIH,EAAII,EAAG,EAAGV,EAAKM,EAAIG,EAAIJ,EAAIK,CAAE,CACxD,CAAC,EAGH,IAAIC,EAAQ,EACRC,EAAQ,EACZ,QAAWrB,KAAQH,EAAM,MACvB,GAAI,CAAAI,EAAWD,CAAI,EAGnB,QAAWO,KAAKS,EAAIhB,EAAK,EAAE,GAAK,CAAC,EAC3BO,EAAE,EAAIE,EACRW,IAEAC,IAIN,GAAIA,EAAQD,EACV,QAAWjB,KAAY,OAAO,OAAOa,CAAG,EACtC,QAAWT,KAAKJ,EACdI,EAAE,EAAI,EAAIE,EAAKF,EAAE,EAIvB,OAAOS,CACT,CC3DA,IAAMM,GAAW,GAYV,SAASC,GACdC,EACAC,EACmB,CACnB,GAAM,CAAE,KAAAC,CAAK,EAAID,EACXE,EAAQ,IAAI,IAChBF,EAAQ,MAAM,IAAIG,GAAQ,CAACA,EAAK,GAAIC,GAAgBH,EAAME,EAAK,MAAM,CAAC,CAAC,CACzE,EACME,EAAWC,GAAeJ,EAAM,IAAII,CAAE,GAAKL,EAAK,kBAChDM,EAAWR,EAAM,MACpB,OAAOS,CAAU,EACjB,KAAK,CAACC,EAAGC,IAAMD,EAAE,OAAO,MAAQC,EAAE,OAAO,KAAK,EAE3CC,EAAQ,IAAI,IACdC,EAAI,EACR,QAAWT,KAAQI,EACjBI,EAAM,IAAIR,EAAK,GAAI,CAAE,EAAAS,EAAG,EAAG,CAAE,CAAC,EAC9BA,GAAKP,EAAQF,EAAK,EAAE,EAAIF,EAAK,WAG/B,IAAMY,EAAWC,GAAef,CAAK,EAE/BgB,EAAQ,IAAI,IAAIR,EAAS,IAAIJ,GAAQ,CAACA,EAAK,GAAI,CAAC,CAAC,CAAC,EAGlDa,EAAQT,EAAS,IAAIJ,GAAQA,EAAK,EAAE,EAC1C,QAAWG,KAAMU,EAAO,CACtB,IAAMC,EAAKN,EAAM,IAAIL,CAAE,EACjBY,EAAOH,EAAM,IAAIT,CAAE,EAAK,EAC9B,QAAWa,KAAaN,EAAS,IAAIP,CAAE,GAAK,CAAC,EACvCS,EAAM,IAAII,CAAS,IAGvBJ,EAAM,IAAII,EAAWD,CAAI,EACzBP,EAAM,IAAIQ,EAAW,CAAE,EAAGF,EAAG,EAAIZ,EAAQC,CAAE,EAAI,EAAG,EAAGT,GAAWqB,CAAK,CAAC,EACtEF,EAAM,KAAKG,CAAS,EAExB,CAEA,IAAIC,EAAY,EAChB,QAAWjB,KAAQJ,EAAM,MAClBY,EAAM,IAAIR,EAAK,EAAE,GACpBQ,EAAM,IAAIR,EAAK,GAAI,CACjB,EAAIiB,IAAcR,EAAKb,EAAM,MAAM,OACnC,EAAGF,GAAW,CAChB,CAAC,EAGL,OAAOc,CACT,CAEO,SAASU,GACdtB,EACAC,EACc,CACd,IAAMW,EAAQb,GAAeC,EAAOC,CAAO,EAC3C,OAAOA,EAAQ,MAAM,IAAIG,IAAS,CAAE,GAAGA,EAAM,GAAGQ,EAAM,IAAIR,EAAK,EAAE,CAAE,EAAE,CACvE,CC3DO,SAASmB,GACdC,EACAC,EACAC,EAAsD,CAAC,EACvD,CACA,IAAMC,EAAQC,GACZC,GAAkBC,GAASN,CAAI,EAAGC,CAAI,EACtCC,EAAK,aACP,EACA,GAAIC,EAAM,MAAM,SAAW,EACzB,MAAM,IAAI,MAAM,wBAAwBF,CAAI,EAAE,EAEhD,GAAIC,EAAK,WAAa,QAAaC,EAAM,MAAM,OAASD,EAAK,SAC3D,MAAM,IAAI,MACR,4BAA4BC,EAAM,MAAM,OAAO,eAAe,CAAC,iBAAiBD,EAAK,SAAS,eAAe,CAAC,sEAChH,EAEF,OAAOC,CACT,CAyBO,SAASI,GACdJ,EACAK,EACAC,EACe,CACf,IAAMC,EAAWP,EAAM,MAAM,KAAKQ,CAAU,EAC5C,MAAO,CACL,MAAO,CACL,MAAOD,EAAWE,GAAYT,EAAOK,CAAO,EAAIA,EAAQ,MACxD,MAAOL,EAAM,KACf,EACA,QAAS,CACP,QAASM,EAAS,QAClB,aAAcA,EAAS,aACvB,GAAGD,EAAQ,KACX,GAAIE,EAAW,CAAE,iBAAkB,EAAM,EAAI,CAAC,CAChD,CACF,CACF,CAKO,SAASG,GAAUV,EAAcM,EAA0B,CAChE,IAAMC,EAAWP,EAAM,MAAM,KAAKQ,CAAU,EAC5C,MAAO,GAAGF,EAAS,OAAO,IAAIA,EAAS,YAAY,IAAIA,EAAS,YAAY,IAAIC,CAAQ,IAAIP,EAAM,eAAiB,EAAE,EACvH,CAOA,eAAsBW,GACpBX,EACAM,EACAM,EACA,CACA,IAAMC,EAASC,GAAUR,EAAS,YAAY,EACxCS,EAASC,GAAUhB,CAAK,EACxB,CAAE,OAAAiB,EAAQ,SAAAC,CAAS,EAAI,MAAMN,EACjCR,GAAcW,EAAO,MAAOI,GAAcJ,EAAO,MAAOF,CAAM,EAAGP,CAAQ,CAC3E,EACMD,EAAUc,GAAcnB,EAAOa,CAAM,EACrCO,EAAQ,IAAI,IAChBf,EAAQ,MAAM,IAAIgB,GAAK,CAACA,EAAE,GAAIC,GAAgBjB,EAAQ,KAAMgB,EAAE,MAAM,CAAC,CAAC,CACxE,EACME,EAAYC,GAChBP,EAAO,cACPF,EAAO,KACPU,GAAML,EAAM,IAAIK,CAAE,GAAK,CACzB,EACA,MAAO,CACL,OAAQ,CACN,GAAGR,EACH,cAAejB,EAAM,MAAM,KAAKQ,CAAU,EACtCkB,GAAkB1B,EAAOuB,CAAS,EAClCA,CACN,EACA,SAAAL,CACF,CACF,CAEO,SAASS,GACdC,EACQ,CACR,IAAIC,EAAO,IACPC,EAAO,IACPC,EAAO,KACPC,EAAO,KACX,QAAWC,KAAY,OAAO,OAAOL,CAAa,EAChD,QAAWM,KAAOD,EAChBJ,EAAO,KAAK,IAAIA,EAAMK,EAAI,CAAC,EAC3BJ,EAAO,KAAK,IAAIA,EAAMI,EAAI,CAAC,EAC3BH,EAAO,KAAK,IAAIA,EAAMG,EAAI,CAAC,EAC3BF,EAAO,KAAK,IAAIA,EAAME,EAAI,CAAC,EAG/B,MAAO,CAAE,KAAAL,EAAM,KAAAC,EAAM,KAAAC,EAAM,KAAAC,CAAK,CAClC,CAGO,IAAMG,EAAc,GAKrBC,GAAW,KACXC,GAAW,IAEV,SAASC,GAAUC,EAAc,CACtC,OAAO,KAAK,IAAIH,GAAU,KAAK,IAAIC,GAAUE,CAAI,CAAC,CACpD,CAKO,SAASC,GACdC,EACAC,EACAC,EACA,CACA,IAAMC,EAAYF,EAAeD,EAAO,EAAIE,EAC5C,OAAOR,EAAcM,EAAO,KAAOE,EAAS,KAAK,IAAI,EAAGC,CAAS,EAAI,CACvE,CAOO,SAASC,GACdJ,EACAK,EACAC,EACAC,EACA,CACA,IAAMC,EAAcH,EAAYX,EAAc,EACxCO,EAAeK,EAAaZ,EAAc,EAChD,GACEc,GAAe,GACfP,GAAgB,GAChB,EAAED,EAAO,EAAI,GAAM,CAACO,GAAaP,EAAO,EAAI,GAE5C,OAEF,IAAMS,EAAOT,EAAO,EAAI,EAAIQ,EAAcR,EAAO,EAAI,IAC/CU,EAAOV,EAAO,EAAI,EAAIC,EAAeD,EAAO,EAAI,IAChDW,EAAQd,GAAUU,EAAYE,EAAO,KAAK,IAAIA,EAAMC,CAAI,CAAC,EAC/D,MAAO,CACL,MAAAC,EACA,WACEjB,EAAcM,EAAO,KAAOW,GAASH,EAAcR,EAAO,EAAIW,GAAS,EACzE,WAAYZ,GAAiBC,EAAQC,EAAcM,EAAY,EAAII,CAAK,CAC1E,CACF,CAOO,SAASC,GACdC,EACAvD,EAGI,CAAC,EACL,CACA,GAAI,CAAE,KAAA8B,EAAM,KAAAC,EAAM,KAAAC,EAAM,KAAAC,CAAK,EAAIL,GAAa2B,EAAO,aAAa,EAC5D,CAAE,OAAAC,CAAO,EAAIxD,EACfuD,EAAO,eAAiBC,GAAUA,EAAO,IAAMA,EAAO,QACxD1B,EAAO0B,EAAO,MACdxB,EAAOwB,EAAO,KAEhB,IAAMC,EAASzD,EAAK,QAAUuD,EAAO,OACrC,OAAIE,IACF3B,EAAO,KAAK,IAAIA,EAAM2B,EAAO,MAAQ3B,CAAI,EACzCC,EAAO,KAAK,IAAIA,EAAM0B,EAAO,MAAQ1B,CAAI,EACzCC,EAAO,KAAK,IAAIA,EAAMyB,EAAO,MAAQzB,CAAI,EACzCC,EAAO,KAAK,IAAIA,EAAMwB,EAAO,MAAQxB,CAAI,GAEpC,CAAE,KAAAH,EAAM,KAAAC,EAAM,EAAGC,EAAOF,EAAM,EAAGG,EAAOF,CAAK,CACtD,CCzMO,IAAM2B,GAAuB,IAAI,IAAI,CAC1C,aACA,kBACA,gBACA,aACA,iBACF,CAAC,EAEKC,GACJ,gEAEK,SAASC,GAEdC,EAAaC,EAAe,CAC5B,OAAIA,EACKD,EAAO,KAAKE,GAAKA,EAAE,UAAYD,CAAK,EAE1BD,EAAO,OAAOE,GAAKL,GAAqB,IAAIK,EAAE,WAAW,CAAC,EAC3D,KAAKA,GACrBJ,GAAkB,KAAK,GAAGI,EAAE,OAAO,IAAIA,EAAE,MAAQ,EAAE,EAAE,CACvD,CACF,CAIA,SAASC,GAAIC,EAAgBC,EAAuB,CAClD,OAAO,OAAQD,EAAc,KAAQ,WAChCA,EAAc,IAAIC,CAAI,EACtBD,EAA8BC,CAAI,CACzC,CAKA,IAAMC,GAAc,2BAEpB,SAASC,GAAWH,EAAgB,CAClC,IAAMC,EAAOF,GAAIC,EAAG,MAAM,EAC1B,GAAI,SAAOC,GAAS,UAAY,CAACC,GAAY,KAAKD,CAAI,GAGtD,OAAO,OAAO,YACZA,EAAK,MAAM,GAAG,EAAE,IAAIG,GAAQ,CAC1B,IAAMC,EAAID,EAAK,QAAQ,GAAG,EAC1B,MAAO,CAACA,EAAK,MAAM,EAAGC,CAAC,EAAGD,EAAK,MAAMC,EAAI,CAAC,CAAC,CAC7C,CAAC,CACH,CACF,CAIA,SAASC,GAAMN,EAAgBC,EAAuB,CACpD,IAAMM,EAAMR,GAAIC,EAAGC,CAAI,EACvB,OAAIM,IAAQ,OACHA,EAEIR,GAAIC,EAAG,MAAM,IACZC,CAAI,GAAKE,GAAWH,CAAC,IAAIC,CAAI,CAC7C,CAEA,IAAMO,GAAe,CACnB,MACA,SACA,KACA,SACA,QACA,WACA,WACF,EACMC,GAAgB,CACpB,MACA,SACA,SACA,gBACA,OACA,YACF,EACMC,GAAc,CAAC,OAAQ,QAAS,QAAS,KAAM,OAAQ,IAAI,EAKjE,SAASC,GAAMX,EAAgBY,EAAiB,CAC9C,QAAWX,KAAQW,EAAO,CACxB,IAAMC,EAAQP,GAAMN,EAAGC,CAAI,EACrBa,EAAM,QAAQ,MAAM,QAAQD,CAAK,EAAIA,EAAM,CAAC,EAAIA,IAAU,EAAE,EAC/D,MAAM,GAAG,EAAE,CAAC,EACZ,KAAK,EACR,GAAIC,IAAQ,IAAMA,IAAQ,IACxB,OAAOA,CAEX,CAEF,CAEA,IAAMC,GAAQ,yBAGP,SAASC,GAAahB,EAAgB,CAC3C,IAAMiB,EAAS,OAAON,GAAMX,EAAGS,EAAa,CAAC,EAC7C,GAAI,OAAO,SAASQ,CAAM,GAAKA,EAAS,EACtC,OAAO,KAAK,MAAMA,CAAM,EAE1B,IAAMC,EAAQP,GAAMX,EAAGQ,EAAY,EACnC,OAAOU,GAASH,GAAM,KAAKG,CAAK,EAAIA,EAAM,OAAS,MACrD,CAKA,SAASC,GAAQN,EAAgB,CAI/B,OAHuB,MAAM,QAAQA,CAAK,EACtCA,EACA,OAAOA,GAAS,EAAE,EAAE,MAAM,GAAG,GACtB,IAAIC,GAAO,CACpB,IAAMM,EACJN,IAAQ,IAAMA,IAAQ,MAAQA,IAAQ,OAAY,IAAM,OAAOA,CAAG,EACpE,OAAO,OAAO,SAASM,CAAC,EAAIA,EAAI,MAClC,CAAC,CACH,CAEA,SAASC,GAAQrB,EAAgB,CAC/B,IAAMsB,EAAUvB,GAAIC,EAAG,SAAS,EAE1BuB,EAAuC,CAAC,EAC9C,OAAW,CAACC,EAAQC,CAAM,IAAK,OAAO,QAAQH,GAAW,CAAC,CAAC,EAAG,CAC5D,IAAMI,EAAQP,GAAQM,EAAO,EAAE,EACzBE,EAAUR,GAAQM,EAAO,EAAE,EAAE,QAAQ,CAACG,EAAIvB,IAC9CuB,IAAO,QAAaA,EAAK,EACrB,CAACF,EAAMrB,CAAC,IAAM,OAAY,CAAE,GAAAuB,CAAG,EAAI,CAAE,GAAAA,EAAI,cAAeF,EAAMrB,CAAC,CAAE,CAAC,EAClE,CAAC,CACP,EACIsB,EAAQ,OAAS,IACnBJ,EAAOC,CAAM,EAAIG,EAErB,CACA,OAAO,OAAO,KAAKJ,CAAM,EAAE,OAAS,EAAIA,EAAS,MACnD,CAEO,SAASM,GAAiBC,EAAwC,CACvE,IAAMC,EAAwB,CAAC,EAC/B,QAAW/B,KAAK8B,EAAU,CACxB,IAAME,EAAQ1B,GAAMN,EAAG,OAAO,EACxBiC,EAAM3B,GAAMN,EAAG,KAAK,EACpBkC,EAAOlB,GAAahB,CAAC,EAC3B,GAAI,EAAEiC,EAAMD,IAAUE,IAAS,OAC7B,SAEF,IAAMC,EAAU7B,GAAMN,EAAG,SAAS,EAC5BkB,EAAQP,GAAMX,EAAGQ,EAAY,EAC7B4B,EAASzB,GAAMX,EAAGU,EAAW,EACnCqB,EAAO,KAAK,CACV,IAAK,GAAGI,CAAO,IAAIH,CAAK,IAAIC,CAAG,GAC/B,MACGG,GAAU,CAAClC,GAAY,KAAKkC,CAAM,EAAIA,EAAS,UAC/ClB,GAASA,EAAM,QAAU,GACtB,IAAIA,CAAK,KACT,GAAGiB,CAAO,KAAKH,EAAQ,GAAG,eAAe,CAAC,IAAIC,EAAI,eAAe,CAAC,IACxE,QAAAE,EACA,MAAAH,EACA,IAAAC,EACA,KAAAC,EACA,MAAAhB,EACA,MAAOG,GAAQrB,CAAC,CAClB,CAAC,CACH,CACA,OAAO+B,EAAO,KAAK,CAACM,EAAGC,IAAMD,EAAE,MAAQC,EAAE,KAAK,CAChD,CC/KA,IAAMC,GAAUC,GAAqBA,EAAE,IAAMA,EAAE,IAExC,SAASC,GACdC,EACAC,EACQ,CACR,IAAMC,EAAOF,EAAQ,OAAOH,EAAM,EAClC,GAAIK,EAAK,SAAW,EAAG,CACrB,IAAMC,EAAQH,EAAQ,CAAC,EACvB,OAAOG,EAAQ,CAAC,CAAE,GAAIA,EAAM,GAAI,GAAIF,EAAWE,EAAM,GAAG,CAAE,CAAC,EAAI,CAAC,CAClE,CACA,IAAMC,EAASF,EAAK,IAAIJ,IAAM,CAC5B,EAAAA,EACA,GAAIG,EAAWH,EAAE,GAAG,EACpB,GAAIG,EAAWH,EAAE,GAAG,EACpB,SAAU,EACV,UAAW,CACb,EAAE,EACF,QAASO,EAAI,EAAGA,EAAI,EAAID,EAAO,OAAQC,IAAK,CAC1C,IAAMC,EAAOF,EAAOC,CAAC,EACfE,EAAQH,EAAOC,EAAI,CAAC,EAEpBG,EADS,KAAK,IAAID,EAAM,EAAE,GAAKD,EAAK,EAAE,GAAI,EAAU,GAClCC,EAAM,GAAKD,EAAK,IACxC,GAAIE,EAAQ,EAAG,CACb,IAAMC,GAAWH,EAAK,GAAKA,EAAK,IAAM,EAChCI,GAAYH,EAAM,GAAKA,EAAM,IAAM,EACrCI,EAAW,KAAK,IAAIH,EAAQ,EAAGC,CAAO,EACpCG,EAAY,KAAK,IAAIJ,EAAQG,EAAUD,CAAQ,EACrDC,EAAW,KAAK,IAAIH,EAAQI,EAAWH,CAAO,EAC9CH,EAAK,UAAYK,EACjBJ,EAAM,SAAWK,CACnB,CACF,CACA,IAAMC,EAAgB,CAAC,EACvB,OAAW,CAAE,EAAAf,EAAG,GAAAgB,EAAI,GAAAC,EAAI,SAAAC,EAAU,UAAAC,CAAU,IAAKb,EAC/CS,EAAM,KACJ,CAAE,GAAIf,EAAE,GAAI,GAAIgB,EAAKE,CAAS,EAC9B,CAAE,GAAIlB,EAAE,GAAI,GAAIiB,EAAKE,CAAU,CACjC,EAEF,OAAOJ,CACT,CAIO,SAASK,GAAML,EAAwBM,EAAY,CACxD,IAAM,EAAIN,EAAM,OAChB,GAAI,IAAM,EACR,OAAOM,EAET,IAAMhB,EAAQU,EAAM,CAAC,EACfO,EAAOP,EAAM,EAAI,CAAC,EACxB,GAAIM,GAAMhB,EAAM,GACd,OAAOA,EAAM,IAAMgB,EAAKhB,EAAM,IAEhC,GAAIgB,GAAMC,EAAK,GACb,OAAOA,EAAK,IAAMD,EAAKC,EAAK,IAE9B,IAAIC,EAAK,EACLC,EAAK,EAAI,EACb,KAAOA,EAAKD,EAAK,GAAG,CAClB,IAAME,EAAOF,EAAKC,GAAO,EACrBT,EAAMU,CAAG,EAAG,IAAMJ,EACpBE,EAAKE,EAELD,EAAKC,CAET,CACA,IAAMC,EAAIX,EAAMQ,CAAE,EACZI,EAAIZ,EAAMS,CAAE,EACZI,EAAOD,EAAE,GAAKD,EAAE,GACtB,OAAOE,EAAO,EAAIF,EAAE,IAAOL,EAAKK,EAAE,IAAME,GAASD,EAAE,GAAKD,EAAE,IAAMA,EAAE,EACpE,CC5EA,IAAMG,GAAmB,IAGnBC,GAAe,EAKrB,SAASC,GAAQC,EAAyBC,EAAsB,CAC9D,GAAM,CAAE,KAAAC,EAAM,KAAAC,CAAK,EAAIH,EAAQ,OAAO,OAChCI,EAAID,EAAOD,EACjB,OAAOF,EAAQ,SAAWI,EAAI,EAC1B,KAAK,IAAIP,GAAkB,KAAK,IAAI,EAAGI,EAAeG,CAAC,CAAC,EACxD,CACN,CAIO,SAASC,GACdL,EACAM,EACkB,CAClB,GAAM,CAAE,OAAAC,EAAQ,WAAAC,EAAY,WAAAC,CAAW,EAAIH,EACrCI,EAASJ,EAAK,OAASP,GAAQC,EAASM,EAAK,YAAY,EACzDK,EAAKC,IAAgBA,EAAKZ,EAAQ,SAAWU,EAASD,EACtD,CAAE,QAAAI,CAAQ,EAAIb,EACpB,GAAIa,EAAS,CACX,IAAMC,EAAQC,GAAeF,EAASG,GAAMA,EAAKT,EAASC,CAAU,EACpE,MAAO,CAAE,EAAGS,GAAMC,GAAMJ,EAAOG,CAAE,EAAG,EAAAN,EAAG,OAAAD,CAAO,CAChD,CACA,MAAO,CAAE,EAAGO,GAAMA,EAAKV,EAASC,EAAY,EAAAG,EAAG,OAAAD,CAAO,CACxD,CAKO,SAASS,GACdnB,EACA,CAAE,EAAAoB,EAAG,EAAAT,CAAE,EACPU,EACAC,EACA,CACA,IAAIC,EAAqB,KACzB,OAAAvB,EAAQ,OAAO,MAAM,QAAQwB,GAAQ,CAEjCA,EAAK,OAAS,GACdJ,EAAEI,EAAK,EAAI1B,EAAY,GAAKuB,GAC5BA,GAAMD,EAAEI,EAAK,EAAIA,EAAK,WAAa1B,EAAY,GAC/Ca,EAAEa,EAAK,EAAI1B,EAAY,GAAKwB,GAC5BA,GAAMX,EAAEa,EAAK,EAAIA,EAAK,cAAgB1B,EAAY,IAElDyB,EAAMC,EAAK,KAEf,CAAC,EACMD,CACT,CC7DO,SAASE,GAAYC,EAAeC,EAA+B,CACxE,MAAO,CAAE,OAAQD,EAAO,OAAQC,EAAY,EAAID,EAAO,UAAAC,CAAU,CACnE,CAGO,SAASC,GACdC,EACAC,EACAC,EACAC,EACQ,CACR,MAAO,CACL,KAAM,CAACH,EAAE,WAAaC,EAAK,OAC3B,KAAM,CAACD,EAAE,WAAaC,EAAK,OAC3B,MAAOC,EAAQF,EAAE,YAAcC,EAAK,OACpC,MAAOE,EAASH,EAAE,YAAcC,EAAK,MACvC,CACF,CAKO,SAASG,GAAOC,EAAWC,EAAuB,CACvD,IAAMC,GAAKF,EAAE,KAAOA,EAAE,MAAQC,EACxBE,GAAKH,EAAE,KAAOA,EAAE,MAAQC,EAC9B,MAAO,CACL,KAAMD,EAAE,KAAOE,EACf,KAAMF,EAAE,KAAOG,EACf,KAAMH,EAAE,KAAOE,EACf,KAAMF,EAAE,KAAOG,CACjB,CACF,CAEO,SAASC,GAASC,EAAeC,EAAe,CACrD,OACEA,EAAM,MAAQD,EAAM,MACpBC,EAAM,MAAQD,EAAM,MACpBC,EAAM,MAAQD,EAAM,MACpBC,EAAM,MAAQD,EAAM,IAExB,CAKO,SAASE,GACdZ,EACAa,EACAC,EACAC,EACAjB,EACe,CACf,IAAMD,EAAQmB,GAAUhB,EAAE,MAAQa,CAAM,EAClCI,EAAQpB,EAAQG,EAAE,MACxB,MAAO,CACL,MAAAH,EACA,WAAYiB,GAAWA,EAAUd,EAAE,YAAciB,EACjD,WAAYnB,EACRE,EAAE,WACFe,GAAWA,EAAUf,EAAE,YAAciB,CAC3C,CACF,CC7DO,SAASC,GACdC,EACAC,EAC2B,CAC3B,IAAMC,EAAOF,EAAM,OAAO,KAAK,GAAK,EAAE,OAASC,CAAI,EACnD,GAAI,CAACC,EACH,OAEF,IAAMC,EAAO,IAAI,IAAIH,EAAM,MAAM,IAAII,GAAK,CAACA,EAAE,GAAIA,CAAC,CAAC,CAAC,EAC9CC,EAAS,IAAI,IACnBL,EAAM,MAAM,QAAQ,CAACM,EAAGC,IAAM,CAC5BF,EAAO,IAAI,GAAGC,EAAE,IAAI,IAAIA,EAAE,EAAE,GAAIC,CAAC,CACnC,CAAC,EACD,IAAMC,EAAU,IAAI,IAAIN,EAAK,OAAO,EAC9BO,EAAc,IAAI,IACxB,QAASF,EAAI,EAAGA,EAAIL,EAAK,QAAQ,OAAQK,IAAK,CAC5C,IAAMG,EAAIR,EAAK,QAAQK,EAAI,CAAC,EACtBI,EAAIT,EAAK,QAAQK,CAAC,EAClBK,EAAQP,EAAO,IAAI,GAAGK,CAAC,IAAIC,CAAC,EAAE,GAAKN,EAAO,IAAI,GAAGM,CAAC,IAAID,CAAC,EAAE,EAC3DE,IAAU,QACZH,EAAY,IAAIG,CAAK,CAEzB,CACA,IAAMC,EAAQC,GACZA,EAAI,OAAO,CAACC,EAAKC,IAAOD,GAAOZ,EAAK,IAAIa,CAAE,GAAG,QAAU,GAAI,CAAC,EAGxDC,EAAYjB,EAAM,cACpBA,EAAM,MAAO,KAAK,GAAKkB,GAAW,EAAE,IAAI,EAAE,OAASlB,EAAM,aAAa,EACtE,OACJ,MAAO,CACL,KAAAC,EACA,QAAAO,EACA,YAAAC,EACA,MAAOP,EAAK,QAAQ,OACpB,GAAIW,EAAKX,EAAK,OAAO,EACrB,YACEe,GAAaA,IAAcf,EAAOW,EAAKI,EAAU,OAAO,EAAI,MAChE,CACF,CCrBA,SAASE,GAASC,EAAkD,CAClE,OAAO,OAAOA,GAAU,UAAYA,IAAU,IAChD,CAMA,SAASC,GAAcD,EAAsC,CAC3D,OAAOD,GAASC,CAAK,GAAK,OAAOA,EAAM,KAAQ,UACjD,CAKA,SAASE,GAAYC,EAAkB,CACrC,IAAMC,EAAOH,GAAcE,CAAO,EAC9BA,EAAQ,IAAI,MAAM,EAClBJ,GAASI,CAAO,EACdA,EAAQ,KACR,OACN,OAAO,OAAOC,GAAS,SAAWA,EAAO,MAC3C,CAKO,SAASC,GAAaC,EAAwC,CACnE,IAAIC,EACJ,GAAIR,GAASO,CAAO,GAAKP,GAASO,EAAQ,aAAa,EAAG,CACxD,GAAM,CAAE,QAAAE,EAAS,MAAAC,EAAO,OAAAC,EAAQ,aAAAC,CAAa,EAAIL,EAAQ,cACrD,OAAOE,GAAY,UAAY,OAAOC,GAAU,WAClDF,EAAS,CACP,QAAAC,EAIA,MAAO,OAAOE,GAAW,SAAWA,EAASD,EAAQ,EACrD,aACE,OAAOE,GAAiB,SAAWA,EAAe,OACpD,YAAaT,GAAYI,EAAQ,YAAY,CAC/C,EAEJ,CACA,OAAOC,CACT,CAWO,SAASK,GACdC,EACAC,EACA,CACA,OACGD,EAAM,eAAiB,QACtBA,EAAM,eAAiBC,EAAO,eAChCD,EAAM,UAAYC,EAAO,SACzBD,EAAM,OAASC,EAAO,OACtBD,EAAM,MAAQC,EAAO,GAEzB,CAWO,SAASC,GAAgB,CAC9B,MAAAF,EACA,MAAAG,CACF,EAGG,CACD,IAAIC,EACAC,EACJ,QAAWC,KAAQH,EACbG,EAAK,OAASN,EAAM,YACtBI,EAASE,EAAK,GAEdD,IAAY,QACZE,EAAWD,CAAI,GACfN,EAAM,OAASM,EAAK,OAAO,OAC3BN,EAAM,MAAQM,EAAK,OAAO,MAAQA,EAAK,SAEvCD,EAAUC,EAAK,IAInB,OAAOF,GAAUC,GAAW,IAC9B,CCvIA,IAAAG,GAA+B,SCgB/B,SAASC,GAASC,EAAkD,CAClE,OAAO,OAAOA,GAAU,UAAYA,IAAU,IAChD,CAKA,SAASC,GAAaC,EAA4C,CAChE,OACEH,GAASG,CAAI,GACbA,EAAK,OAAS,oBACd,OAAOA,EAAK,IAAO,UACnB,OAAOA,EAAK,gBAAmB,YAC/B,MAAM,QAAQA,EAAK,aAAa,CAEpC,CAMO,SAASC,GAASC,EAAkB,CACzC,OAAOA,EAAM,QAAQF,GACnBH,GAASG,CAAI,GAAK,MAAM,QAAQA,EAAK,KAAK,EACtC,CAACA,EAAM,GAAGA,EAAK,KAAK,EACpB,CAACA,CAAI,CACX,CACF,CAiBO,SAASG,GAAiB,CAC/B,MAAAD,EACA,gBAAAE,EACA,aAAAC,CACF,EAIG,CACD,IAAMC,EAAaL,GAASC,CAAK,EAC9B,OAAOF,GAAQD,GAAaC,CAAI,CAAC,EACjC,OAAOA,GAAQA,EAAK,cAAc,SAASK,CAAY,CAAC,EAE3D,OADkBC,EAAW,KAAKN,GAAQA,EAAK,KAAOI,CAAe,IAChDE,EAAW,SAAW,EAAIA,EAAW,CAAC,EAAI,OACjE,CD5CA,SAASC,GACPC,EACAC,EACAC,EACA,CACAA,EAAO,MAAOC,GAAmB,CAC/B,QAAQ,MAAM,qBAAqBF,CAAI,GAAIE,CAAK,EAChDH,EAAQ,cAAc,GAAGC,CAAI,KAAKE,CAAK,GAAIA,CAAK,CAClD,CAAC,CACH,CAMA,IAAMC,GAAqB,IAEpB,SAASC,GACdC,EACAC,EAAUH,GACK,CACf,IAAMI,EAAM,KAAK,IAAI,EAAG,KAAK,MAAMD,GAAWD,EAAI,IAAMA,EAAI,QAAU,CAAC,CAAC,EACxE,MAAO,CAAE,GAAGA,EAAK,MAAO,KAAK,IAAI,EAAGA,EAAI,MAAQE,CAAG,EAAG,IAAKF,EAAI,IAAME,CAAI,CAC3E,CAqBO,SAASC,GACdC,EACAC,EAGe,CACf,OAAOA,EACHD,EAAa,IAAIE,GACfA,EAAE,OAAS,EACP,CACE,GAAGA,EACH,OAAQD,EAAO,aACf,UAAW,OACX,QAASA,EAAO,QAChB,MAAOA,EAAO,MACd,IAAKA,EAAO,GACd,EACAC,CACN,EACAF,CACN,CAaO,SAASG,GAAiB,CAC/B,QAAAb,EACA,SAAAc,EACA,SAAAC,EACA,gBAAAC,EACA,OAAAC,EAAS,CAAC,CACZ,EAMG,CACD,IAAMC,EAAWC,GAAiB,CAChC,MAAOnB,EAAQ,MACf,gBAAAgB,EACA,aAAcD,CAChB,CAAC,EACGK,EACJ,OAAIF,GACFnB,GACEC,EACA,kBAAkBe,CAAQ,IAAIM,EAASP,CAAQ,CAAC,GAChD,QAAQ,QAAQI,EAAS,eAAeI,GAAUR,CAAQ,EAAGC,CAAQ,CAAC,CACxE,EACAK,EAASF,EAAS,IAElBE,EAASpB,EAAQ,QAAQ,mBAAoB,CAC3C,YAAa,GAAGe,CAAQ,WAAMM,EAASP,CAAQ,CAAC,GAChD,KAAM,CAAE,SAAAC,EAAU,IAAKO,GAAUR,CAAQ,EAAG,OAAAG,CAAO,CACrD,CAAC,EAAE,GAEEG,CACT,CAcO,SAASG,GAAsB,CACpC,QAAAvB,EACA,SAAAc,EACA,SAAAC,EACA,gBAAAC,CACF,EAKG,CACD,IAAMQ,EAASL,GAAiB,CAC9B,MAAOnB,EAAQ,MACf,gBAAAgB,EACA,aAAcD,CAChB,CAAC,EACD,OAAAS,GAAQ,kBAAkB,CACxB,QAASV,EAAS,QAClB,MAAOA,EAAS,MAChB,IAAKA,EAAS,IACd,aAAcC,CAChB,CAAC,EACMS,IAAW,MACpB,CAMA,SAASC,GACPzB,EACA0B,EACA,CACA,GAAIA,IAAY,OACd,MAAO,CAAC,EAEV,IAAMC,EAAQ3B,EAAQ,OAAO,KAC3B4B,MAAK,mBAAeA,EAAG,SAAS,IAAMF,CACxC,EACMG,EAAiBF,KAAQ,mBAAeA,EAAO,eAAe,EAAI,CAAC,EACzE,OAAO,MAAM,QAAQE,CAAK,EAAIA,EAAM,OAAOC,GAAK,OAAOA,GAAM,QAAQ,EAAI,CAAC,CAC5E,CAyBO,SAASC,GAAkB,CAChC,QAAA/B,EACA,aAAAU,EACA,QAAAgB,EACA,aAAAM,CACF,EAKG,CACD,IAAMC,EAAuBR,GAAqBzB,EAASgC,CAAY,EAKjEE,EAAUT,GAAqBzB,EAAS0B,CAAO,EAC/CS,EAAUzB,EAAa,OAAOE,GAAKsB,EAAQ,SAAStB,EAAE,MAAM,CAAC,EAC7DwB,EAASD,EAAQ,QAAU,EAAIA,EAAUzB,EACzC2B,EAAW,CACf,KAAM,CACJ,MAAOD,EAAO,IAAIxB,IAAM,CACtB,SAAUA,EAAE,OACZ,IAAKU,GAAUV,CAAC,EAChB,GAAIoB,IAAiB,QACrBC,EAAqB,SAASrB,EAAE,MAAM,EAClC,CAAE,OAAQ,CAACoB,CAAY,CAAE,EACzB,CAAC,CACP,EAAE,EAMF,OAAQN,EAAUU,EAAO,MAAM,CAAC,EAAE,IAAI,IAAM,CAACV,CAAO,CAAC,EAAI,CAAC,EAC1D,kBAAmB,EACrB,CACF,EAGI1B,EAAQ,WACVD,GACEC,EACA,kCACAA,EAAQ,WAAW,oBAAqBqC,CAAQ,CAClD,EAEArC,EAAQ,QAAQ,oBAAqBqC,CAAQ,CAEjD,CE7QA,IAAAC,GAA+B,SAIzBC,GAAwB,eAmBvB,SAASC,GAAa,CAC3B,QAAAC,EACA,aAAAC,EACA,MAAAC,CACF,EAIG,CACD,IAAMC,EAAkB,CAAC,EACzB,QAAWC,KAASJ,EAAQ,OAAQ,CAClC,IAAMK,KAAgB,mBAAeD,EAAO,MAAM,EAC5CE,KAAyB,mBAAeF,EAAO,eAAe,EAC9DG,KAAmB,mBAAeH,EAAO,SAAS,EAEtDC,IAASP,IACT,MAAM,QAAQQ,CAAa,GAC3BA,EAAc,SAASL,CAAY,GACnC,OAAOM,GAAY,UACnBA,IAAYL,GAEZC,EAAM,KAAKI,CAAO,CAEtB,CACA,OAAOL,EAAQ,CAACA,EAAO,GAAGC,CAAK,EAAIA,CACrC,CChDA,IAAAK,GAA+B,SAC/BC,GAA6B,UAYvBC,GAAqB,eAWpB,SAASC,GACdC,EACAC,EACA,CACA,IAAMC,EAAS,IAAI,IAAID,CAAa,EAC9BE,EAAkC,CAAC,EACzC,QAAWC,KAASJ,EAAQ,OAAQ,CAClC,IAAMK,KAAgB,mBAAeD,EAAO,MAAM,EAC5CE,KAA2B,mBAAeF,EAAO,eAAe,EAChEG,KAAmB,mBAAeH,EAAO,SAAS,EACxD,GACEC,IAASP,IACT,OAAOS,GAAY,UACnB,MAAM,QAAQD,CAAe,EAC7B,CACA,IAAME,EAAWF,EAAgB,OAAOG,GAAQP,EAAO,IAAIO,CAAI,CAAC,EAAE,OAC9DD,GAAY,GACdL,EAAM,KAAK,CAAE,QAAAI,EAAS,QAAM,iBAAaH,EAAOJ,CAAO,EAAG,SAAAQ,CAAS,CAAC,CAExE,CACF,CACA,OAAOL,EAAM,KAAK,CAACO,EAAGC,IAAMA,EAAE,SAAWD,EAAE,QAAQ,CACrD,CrBkFA,IAAME,GAAoB,IAGpBC,GAAoB,IACpBC,GAAqB,IAErBC,GAAkB,eAIxB,SAASC,GAAaC,EAAwC,CAC5D,GAAI,OAAOA,EAAc,KAAQ,SAC/B,OAAOA,EAAc,IAIvB,IAAMC,EAFWD,EAAc,kBAET,IACtB,OAAO,OAAOC,GAAQ,UAAYA,EAAI,SAASH,EAAe,EAC1DG,EAAI,MAAM,EAAG,CAACH,GAAgB,MAAM,EACpC,MACN,CACA,IAAMI,GAAiB,IACjBC,GAAkB,IAClBC,GAAuB,IACvBC,GAAuB,EAGvBC,GAA2B,CAC/B,YAAa,CAAC,EACd,eAAgB,IAAI,IACpB,OAAQ,CAAC,EACT,UAAW,IAAI,IACf,WAAY,CAAC,EACb,cAAe,IAAI,GACrB,EAKA,SAASC,MAAYC,EAAoB,CAAC,CA8BnC,IAAMC,GAAsB,IAG5B,SAASC,EAAaC,EAAY,CACvC,OAAOA,GAAM,IACT,GAAG,EAAEA,EAAK,KAAW,QAAQ,CAAC,CAAC,MAC/B,GAAG,KAAK,MAAMA,EAAK,GAAI,CAAC,KAC9B,CAUO,IAAMC,GAA0B,IAIvC,SAASC,GAAgBC,EAOtB,CACD,OACEA,EAAM,0BAA4B,QAClC,CAACA,EAAM,wBACPA,EAAM,wBAA0BA,EAAM,eACtCA,EAAM,yBAA2BA,EAAM,eAE3C,CAGA,SAASC,GAAeD,EAMb,CACT,OAAOE,GAAWF,EAAOA,EAAM,UAAWA,EAAM,UAAWA,EAAM,YAAY,CAC/E,CAsBA,IAAMG,GAAe,IAAI,QAInBC,GAA0B,EAEhC,SAASC,GAAeC,EAAc,CACpC,IAAIC,EAAQJ,GAAa,IAAIG,CAAK,EAClC,OAAKC,IACHA,EAAQ,IAAI,IACZJ,GAAa,IAAIG,EAAOC,CAAK,GAExBA,CACT,CAOA,SAASC,GAAoBC,EAAaC,EAAW,CACnD,IAAMH,EAAQJ,GAAa,IAAIM,CAAI,EAC/BF,GAASE,IAASC,GACpBP,GAAa,IAAIO,EAAIH,CAAK,CAE9B,CAEA,SAASI,GACPJ,EACAK,EACAC,EACA,CACIN,EAAM,MAAQH,IAChBG,EAAM,OAAOA,EAAM,KAAK,EAAE,KAAK,EAAE,KAAM,EAEzCA,EAAM,IAAIK,EAAKC,CAAM,CACvB,CAEO,SAASC,IAAiB,CAC/B,OAAO,QACJ,QACC,eACA,yBAAqB,EACrB,QAAM,MAAM,CAcV,cAAe,QAAM,SAAS,QAAM,OAAQ,CAAC,EAC7C,aAAc,QAAM,SAAS,QAAM,QAAS,EAAK,EAGjD,WAAY,QAAM,MAAM,QAAM,YAAYC,EAAkB,CAAC,EAC7D,YAAa,QAAM,MAAM,QAAM,YAAYC,EAAmB,CAAC,EAK/D,aAAc,QAAM,SAClB,QAAM,YAAYC,EAAoB,EACtC,MACF,EAEA,UAAW,QAAM,SACf,QAAM,YAAYC,EAAiB,EACnC,OACF,EAGA,YAAa,QAAM,SAAS,QAAM,QAAS,EAAI,EAC/C,kBAAmB,QAAM,SAAS,QAAM,QAAS,EAAK,EAGtD,UAAW,QAAM,SAAS,QAAM,QAAS,EAAI,EAG7C,YAAa,QAAM,SAAS,QAAM,OAAQ,EAAE,EAG5C,cAAe,QAAM,SAAS,QAAM,OAAQ,EAAE,EAG9C,UAAW,QAAM,SAAS,QAAM,OAAQ,EAAE,EAG1C,eAAgB,QAAM,MAAM,QAAM,OAAiB,CAAC,EAGpD,gBAAiB,QAAM,SAAS,QAAM,OAAQ,EAAE,EAOhD,cAAe,QAAM,SAAS,QAAM,OAAQ,EAAE,EAO9C,SAAU,QAAM,SAAS,QAAM,QAAS,EAAK,EAC7C,gBAAiB,QAAM,SAAS,QAAM,OAAQ,CAAC,EAC/C,mBAAoB,QAAM,SAAS,QAAM,OAAQ,CAAC,EAClD,SAAU,QAAM,SAAS,QAAM,QAAS,EAAK,EAC7C,MAAO,QAAM,SAAS,QAAM,OAAQ,CAAC,EACrC,WAAY,QAAM,SAAS,QAAM,OAAQ,CAAC,EAC1C,WAAY,QAAM,SAAS,QAAM,OAAQ,CAAC,EAC1C,UAAW,QAAM,SAAS,QAAM,QAAS,EAAK,EAe9C,WAAY,QAAM,MAAM,QAAM,MAAM,EAGpC,cAAe,QAAM,SAAS,QAAM,OAAQpB,EAAuB,EAGnE,YAAa,QAAM,MACjB,QAAM,OAAuC,CAC/C,EAEA,gBAAiB,QAAM,MAAM,QAAM,MAAM,CAC3C,CAAC,CACH,EACC,SAAS,KAAO,CACf,MAAO,OAEP,YAAa,OACb,aAAc,OAGd,WAAY,CAAE,MAAO,EAAG,OAAQ,CAAE,EAIlC,aAAc,OAEd,aAAc,OAEd,aAAc,OAId,SAAU,CAAC,EAQX,MAAO,OACP,UAAW,GACX,aAAc,GACd,cAAe,GACf,YAAa,KACb,YAAa,KACb,aAAc,KACd,cAAe,EAOf,iBAAkB,EAGlB,gBAAiB,EAGjB,cAAe,OACf,aAAc,KAId,UAAW,GAQX,cAAe,MACf,mBAAoB,OAGpB,oBAAqB,EAMrB,YAAa,OACb,aAAc,OACd,eAAgB,OAChB,wBAAyB,OACzB,uBAAwB,GACxB,uBAAwB,GACxB,sBAAuB,EACzB,EAAE,EACD,MAAMqB,IAAS,CACd,IAAI,mBAAqC,CACvC,MAAO,OACT,EACA,IAAI,oBAAkC,CACpC,MAAO,MACT,EAEA,IAAI,kBAAmB,CACrB,MAAO,EACT,EACA,IAAI,cAAe,CACjB,MAAO,EACT,EAEA,IAAI,aAAiD,CACnD,GAAI,CACF,SAAO,sBAAmBA,CAAI,EAAE,aAClC,MAAQ,CACN,MACF,CACF,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,kBAAmB,CACrB,OAAOA,EAAK,YAAcA,EAAK,iBACjC,EACA,IAAI,mBAAoB,CACtB,OAAOA,EAAK,aAAeA,EAAK,kBAClC,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,WAAY,CACd,SAAO,qBAAkBA,CAAI,EAAE,KACjC,EACA,IAAI,UAAW,CACb,GAAIA,EAAK,MAAO,CACd,IAAMC,EAAI,IAAI,IACd,QAAW,KAAKD,EAAK,MAAM,MACzBC,EAAE,IAAI,EAAE,GAAI,CAAC,EAEf,OAAOA,CACT,CAEF,EAKA,IAAI,aAAc,CAChB,IAAMA,EAAI,IAAI,IACd,QAAW,KAAKD,EAAK,OAAO,OAAS,CAAC,EACpCC,EAAE,IAAI,EAAE,GAAI,EAAE,MAAM,EAEtB,OAAOA,CACT,EACA,IAAI,WAAY,CACd,OAAOD,EAAK,OAAO,MAAM,QAAU,CACrC,EACA,IAAI,WAAY,CACd,OAAOA,EAAK,OAAO,MAAM,QAAU,CACrC,EACA,IAAI,WAAY,CACd,OAAOA,EAAK,OAAO,OAAO,QAAU,CACtC,EACA,IAAI,UAAW,CACb,OAAOA,EAAK,QAAU,MACxB,EACA,IAAI,iBAAkB,CACpB,OAAOpB,GAAgBoB,CAAI,CAC7B,EAIA,IAAI,aAAc,CAChB,OACEA,EAAK,QAAU,SACdA,EAAK,WACJA,EAAK,kBACJA,EAAK,QAAU,QAAa,CAACpB,GAAgBoB,CAAI,EAExD,EAIA,IAAI,eAAgB,CAClB,OAAOA,EAAK,WAAa,CAACA,EAAK,YACjC,EAUA,IAAI,kBAAmB,CACrB,OAAOA,EAAK,MACRE,GAAqBF,EAAK,iBAAkBA,EAAK,KAAK,EACtD,EACN,EAIA,IAAI,iBAAkB,CACpB,OAAOA,EAAK,OAAO,MAAM,KAAKG,GAAKA,EAAE,QAAQ,OAAS,CAAC,GAAK,EAC9D,EAGA,IAAI,aAAc,CAChB,OAAOH,EAAK,OAAO,aAAe,CAAC,CACrC,EAQA,IAAI,YAAa,CACf,IAAMI,EAAQJ,EAAK,OAAO,MAE1B,OADkBA,EAAK,WAAaA,EAAK,cAAc,UACnCI,GAASC,GAAkBD,EAAM,MAAM,EACvDE,GAAWF,CAAK,EAChB,CAAC,CACP,EAEA,IAAI,aAAc,CAChB,IAAMA,EAAQJ,EAAK,OAAO,MAC1B,OAAOI,GAAO,OAASE,GAAWF,CAAK,EAAI,CAAC,CAC9C,EAGA,IAAI,eAAgB,CAClB,OAAOJ,EAAK,OAASA,EAAK,gBACtBO,GAAcP,EAAK,MAAOA,EAAK,eAAe,EAC9C,MACN,EASA,IAAI,oBAAqB,CACvB,OACEA,EAAK,WAAaK,GAAkBL,EAAK,OAAO,OAAO,QAAU,CAAC,CAEtE,EAIA,IAAI,qBAAsB,CACxB,OAAOA,EAAK,OAAO,aACrB,EAGA,IAAI,YAAa,CACf,OAAOA,EAAK,aAAeA,EAAK,WAClC,EAaA,IAAI,sBAA4C,CAC9C,OAAOQ,GAAmBR,EAAK,kBAAmBA,EAAK,KAAK,CAC9D,EACA,IAAI,eAAgB,CAClB,OAAOA,EAAK,cAAc,aAC5B,EACA,IAAI,iBAAkB,CACpB,MAAO,CAACA,EAAK,cAAc,OAC7B,EAGA,IAAI,WAAY,CACd,OAAOA,EAAK,cAAc,WAAa,CAAC,CAC1C,EAKA,IAAI,iBAAkB,CACpB,OAAOA,EAAK,cAAc,iBAAmB,CAAC,CAChD,EAIA,IAAI,WAAY,CACd,OAAOA,EAAK,cAAc,WAAa,EACzC,EACA,IAAI,aAAc,CAChB,MAAO,IAAIA,EAAK,MAAQ,KAAK,QAAQ,CAAC,CAAC,GACzC,EAIA,IAAI,mBAAoB,CACtB,OACEA,EAAK,QAAU,GAAKA,EAAK,aAAe,GAAKA,EAAK,aAAe,CAErE,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,YAAa,CACf,OAAOA,EAAK,SAAS,GAAG,EAAE,CAC5B,EAGA,IAAI,gBAAiB,CACnB,OAAOA,EAAK,MAAQS,GAAiBT,EAAK,KAAK,EAAI,CAAC,CACtD,CACF,EAAE,EACD,MAAMA,IAAS,CAGd,IAAI,SAAU,CACZ,OAAOA,EAAK,cAAc,OACtBA,EAAK,aACLA,EAAK,cACX,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,eAAgB,CAClB,IAAMU,EAAQV,EAAK,YACnB,OAAOU,KACF,kBAAeA,EAAO,SAAS,EAChC,MACN,EACA,IAAI,eAAgB,CAClB,IAAMA,EAAQV,EAAK,YACnB,OAAOU,KAAS,kBAAeA,EAAO,SAAS,EAAe,MAChE,EACA,IAAI,sBAAuB,CACzB,IAAMC,EAASX,EAAK,YACpB,OAAKW,KAGE,cAAWX,CAAI,EACnB,OAAO,OAAOY,MACZ,kBAAeA,EAAG,eAAe,EAAe,SAC/CD,EAAO,YACT,CACF,EACC,IAAIC,IAAM,CACT,QAASA,EAAE,QACX,QAAM,kBAAeA,EAAG,MAAM,EAC9B,eAAc,kBAAeA,EAAG,SAAS,EACtC,IACL,EAAE,EAbK,CAAC,CAcZ,CACF,EAAE,EACD,MAAMZ,IAAS,CACd,IAAI,kBAAmB,CACrB,OAAOA,EAAK,qBAAqB,OAAO,GACtCa,GAAmB,IAAI,EAAE,WAAW,CACtC,CACF,EAGA,IAAI,oBAAqB,CACvB,OAAOb,EAAK,qBAAqB,OAAO,GACtCc,GAAqB,IAAI,EAAE,WAAW,CACxC,CACF,EAEA,IAAI,eAAgB,CAClB,OAAOd,EAAK,cAAgB,CAAC,CAC/B,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,aAAc,CAChB,OAAOe,GAAgBf,EAAK,mBAAoBA,EAAK,aAAa,CACpE,EACA,IAAI,gBAAiB,CACnB,OAAOA,EAAK,cAAc,KAAKgB,GAAKA,EAAE,MAAQhB,EAAK,SAAS,CAC9D,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,eAAgB,CAClB,OAAOA,EAAK,MAAQiB,GAAejB,EAAK,KAAK,EAAI,MACnD,EACA,IAAI,SAAmB,CACrB,OAAOkB,GACLlB,EAAK,MACLA,EAAK,SACLA,EAAK,gBACLA,EAAK,SACP,CACF,EAMA,IAAI,WAAY,CACd,OAAOA,EAAK,OACVA,EAAK,mBAAqB,YAC1B,CAACA,EAAK,cAAc,QAClBmB,GAAcnB,EAAK,KAAK,EACxB,CAAC,CACP,EAGA,IAAI,aAAc,CAChB,GAAIA,EAAK,mBAAqB,YAAc,CAACA,EAAK,MAChD,OAEF,IAAMoB,EAASpB,EAAK,eACdqB,EAAOD,EACTE,GAAStB,EAAK,MAAOoB,EAAQA,EAAO,IAAI,EACxCE,GAAStB,EAAK,MAAOA,EAAK,WAAW,EACzC,GAAI,CAACqB,EACH,OAEF,IAAME,EAAUvB,EAAK,eACfwB,EAAOD,EACTF,EAAK,KACF,OAAOL,GAAKO,EAAQ,SAASP,EAAE,MAAM,CAAC,EACtC,KACC,CAAC,EAAGS,IACFF,EAAQ,QAAQ,EAAE,MAAM,EAAIA,EAAQ,QAAQE,EAAE,MAAM,GACpD,EAAE,MAAM,cAAcA,EAAE,KAAK,CACjC,EACFJ,EAAK,KACT,MAAO,CAAE,GAAGA,EAAM,KAAMK,GAAUF,EAAMJ,GAAQ,KAAK,CAAE,CACzD,EAIA,IAAI,gBAAiB,CACnB,IAAMC,EAAO,KAAK,YAClB,OAAOA,EACH,CAACA,EAAK,UAAW,GAAGA,EAAK,IAAI,EAAE,IAAI,CAACM,EAAKC,KAAO,CAC9C,MAAOD,EAAI,MACX,EAAGC,EAAI,EACT,EAAE,EACF5B,EAAK,SACX,EAGA,IAAI,cAAe,CAEjB,OADgBA,EAAK,mBAAqB,WAAaA,EAAK,QAAU,CAAC,GACxD,IAAI6B,IAAW,CAC5B,OAAAA,EACA,GAAGC,EAAeD,EAAQ7B,EAAK,YAAY,CAC7C,EAAE,CACJ,EAGA,IAAI,UAAW,CACb1B,GAAS0B,EAAK,gBAAgB,EAC9B,IAAM+B,EAAY/B,EAAK,cAAc,cACrC,OAAOA,EAAK,WACVA,EAAK,mBAAqB,YAC1BA,EAAK,mBAAqB,YAC1B,CAACA,EAAK,cAAc,SACpBA,EAAK,OACLA,EAAK,cACL+B,EACEC,GAAShC,EAAK,MAAOA,EAAK,aAAc+B,CAAS,EACjD,CAAC,CACP,EAGA,IAAI,aAAc,CAChBzD,GAAS0B,EAAK,gBAAgB,EAC9B,IAAM+B,EAAY/B,EAAK,cAAc,cACrC,GACE,CAACA,EAAK,aACNA,EAAK,mBAAqB,YAC1BA,EAAK,mBAAqB,YAC1BA,EAAK,cAAc,SACnB,CAACA,EAAK,OACN,CAAC+B,EAED,MAAO,CAAC,EAEV,IAAME,EAAS,IAAI,IAAIjC,EAAK,YAAY,IAAIkC,GAAK,CAACA,EAAE,KAAMA,EAAE,KAAK,CAAC,CAAC,EACnE,OAAOC,GACLnC,EAAK,MACLA,EAAK,QACL+B,EACAK,GAAQH,EAAO,IAAIG,CAAI,GAAKA,EAC5BpC,EAAK,YACP,CACF,EAYA,IAAI,eAAgB,CAClB,OAAOA,EAAK,uBAAyB,sBACnCA,EAAK,OACL,CAACA,EAAK,cAAc,QAClBqC,GAAqBrC,EAAK,MAAOA,EAAK,UAAU,EAChD,MACN,EAYA,IAAI,eAAgB,CAClB,IAAMW,EAASX,EAAK,YACpB,OAAOW,EAAS,CAACA,EAAO,YAAY,EAAI,CAAC,CAC3C,EAaA,IAAI,QAAS,CACX,OAAO2B,GAAYtC,EAAK,MAAOA,EAAK,SAAS,EAAE,MACjD,EACA,IAAI,QAAS,CACX,OAAOsC,GAAYtC,EAAK,MAAOA,EAAK,SAAS,EAAE,MACjD,EAKA,IAAI,WAAY,CACd,OAAOsC,GAAYtC,EAAK,MAAOA,EAAK,SAAS,CAC/C,EAQA,IAAI,cAAe,CACjB,IAAMuC,EAASvC,EAAK,aACdqB,EAAO,KAAK,YAClB,OAAOkB,EACHC,GAAcD,EAAQ,CACpB,OAAQvC,EAAK,SAAS,SAAW,EAAIA,EAAK,YAAc,OACxD,OAAQqB,GAAQkB,EAAO,OAASE,GAAepB,CAAI,EAAI,MACzD,CAAC,EACD,MACN,EAeA,IAAI,cAAe,CACjB,IAAMqB,EAAS,KAAK,aACdC,EAAc3C,EAAK,UAAY4C,EAAc,EAK7CC,EAAU,KAAK,IACnBlF,GACAqC,EAAK,YAActC,EACrB,EACA,GAAI,CAACgF,EACH,OAAOG,EAET,GAAI7C,EAAK,UAAW,CAIlB,IAAM8C,EACJ9C,EAAK,mBAAqB,WACtBpC,GACAD,GACN,OAAO,KAAK,IAAIkF,EAAS,KAAK,IAAIC,EAAOJ,EAAO,EAAIE,EAAc,CAAC,CAAC,CACtE,CACA,OAAOF,EAAO,EAAI,GAAKC,EAAc,EACjC,KAAK,IACHE,EACA,KAAK,IACHlF,GACC+E,EAAO,EAAIC,EAAeD,EAAO,EAAIE,EAAc,CACtD,CACF,EACAC,CACN,CACF,EAAE,EACD,MAAM7C,IAAS,CACd,IAAI,WAAY,CACd,OAAO+C,GAAc/C,EAAK,iBAAkBA,EAAK,WAAW,CAC9D,EAKA,IAAI,qBAAsB,CACxB,OAAO,IAAI,IAAIA,EAAK,UAAU,IAAIgD,GAAK,CAACA,EAAE,UAAWA,EAAE,QAAQ,CAAC,CAAC,CACnE,EACA,IAAI,mBAAoB,CACtB,OAAO,IAAI,IACThD,EAAK,kBAAoB,CAAC,EAAIA,EAAK,UAAU,IAAIgD,GAAKA,EAAE,SAAS,CACnE,CACF,EAWA,IAAI,qBAAsB,CACxB,IAAMC,EAAOjD,EAAK,cAClB,OAAOiD,EACH,CAAE,MAAOA,EAAK,MAAO,IAAKA,EAAK,MAAQA,EAAK,IAAK,EACjD,MACN,EAKA,IAAI,gBAAiB,CACnB,IAAIvD,EAQEiB,EAASX,EAAK,YACdkD,EAASlD,EAAK,YACdmD,EAAWnD,EAAK,SAChBoD,EAAYpD,EAAK,cACvB,GAAIW,GAAUuC,IAAW,MAAQC,GAAYC,EAAW,CACtD,IAAMC,EAAOC,GAAkB,CAAE,OAAAJ,EAAQ,SAAAC,EAAU,UAAAC,CAAU,CAAC,EAC1DC,IACF3D,EAAS,CACP,QAASiB,EAAO,QAChB,aAAcA,EAAO,aACrB,GAAG0C,CACL,EAEJ,CACA,OAAO3D,CACT,CACF,EAAE,EACD,MAAMM,IAAS,CAUd,IAAI,wBAAyB,CAC3B,IAAMb,EAAQa,EAAK,MACbuD,EAAWpE,EAAQqE,GAAcrE,CAAK,EAAI,CAAC,EACjD,OAAOA,EACHsE,GAAuBtE,EAAO,CAC5B,OAAQoE,EAAS,OAAS,EAAIG,GAAaH,CAAQ,EAAI,GACzD,CAAC,EACD,CAAC,CACP,CACF,EAAE,EACD,MAAMvD,IAAS,CAWd,IAAI,kBAAmB,CACrB,GAAM,CAAE,gBAAA2D,CAAgB,KAAI,cAAW3D,CAAI,EACrC4D,EAAQ5D,EAAK,eAAe,oBAE5B6D,EAAW,IAAI,IACnB,OAAO,QAAQD,GAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAACE,EAAKC,CAAM,IAAM,CAACA,EAAQD,CAAG,CAAC,CAClE,EACME,EAAU5B,GACdA,IAAS,QAAauB,EAAgB,IAAIvB,CAAI,EACzCuB,EAAgB,IAAIvB,CAAI,GAAG,MAAQA,EACpC,OACN,OAAQ6B,GACND,EAAOH,EAAS,IAAII,CAAM,CAAC,GAAKD,EAAOC,CAAM,CACjD,CACF,EAAE,EACD,MAAMjE,IAAS,CAMd,IAAI,sBAAuB,CACzB,OAAOkE,GACLC,GAAoBnE,EAAK,uBAAwBA,EAAK,WAAW,EACjEA,EAAK,gBACP,CACF,EAKA,IAAI,0BAA2B,CAC7B,IAAMW,EAASX,EAAK,YACpB,OACEW,IAAW,QACXyD,GAAiB,CACf,MAAO,CAAC,MAAG,cAAWpE,CAAI,EAAE,KAAK,EACjC,gBAAiBA,EAAK,gBACtB,aAAcW,EAAO,YACvB,CAAC,IAAM,MAEX,EAKA,kBAAkBuC,EAAgB,CAChC,IAAMmB,EAAOrE,EAAK,UAAU,IAAIkD,CAAM,EAChCoB,EAAMD,EAAOE,GAAgBF,CAAI,EAAI,OACrCG,EAAcF,EAChBG,GAAwBzE,EAAK,iBAAkBsE,CAAG,EAClD,OACE3D,EAASX,EAAK,YACdmD,EAAWnD,EAAK,SAChBoD,EAAYpD,EAAK,cACjBqD,EACJ1C,GAAUwC,GAAYC,EAClBE,GAAkB,CAAE,OAAAJ,EAAQ,SAAAC,EAAU,UAAAC,CAAU,CAAC,EACjD,OACAsB,EACJ/D,GAAU0C,EACN,CACE,OAAQ1C,EAAO,aACf,UAAW,OACX,QAASA,EAAO,QAChB,GAAG0C,CACL,EACA,OACN,MAAO,CACL,IACEiB,GAAOE,EACH,CAAE,SAAUG,GAAeL,CAAG,EAAG,SAAUE,CAAY,EACvD,OACN,UACE7D,GAAU+D,EACN,CACE,SAAUC,GAAeD,CAAW,EACpC,SAAU/D,EAAO,YACnB,EACA,OAGN,UACEA,GAAU+D,EACN,CAAE,SAAUA,EAAa,SAAU/D,EAAO,YAAa,EACvD,MACR,CACF,CACF,EAAE,EACD,MAAMX,IAAS,CAId,IAAI,MAA+B,CACjC,IAAI4E,EACJ,GAAI,CACFA,KAAO,qBAAkB5E,CAAI,CAC/B,MAAQ,CACN,MACF,CACA,OAAO6E,GAAaD,CAAI,EAAIA,EAAO,MACrC,CACF,EAAE,EACD,MAAM5E,IAAS,CAKd,IAAI,aAAc,CAChB,GAAM,CAAE,KAAA8E,EAAM,YAAanE,EAAQ,aAAc4B,CAAO,EAAIvC,EAC5D,OACE8E,IAAS,QACTA,EAAK,aACLnE,IAAW,QACX4B,GAAQ,gBAAkB,IAC1BvC,EAAK,SAAS,SAAW,GACzB,CAAC8E,EAAK,cAAc,cAAc,KAChCrD,GAAKA,EAAE,UAAYd,EAAO,SAAWc,EAAE,QACzC,CAEJ,CACF,EAAE,EACD,MAAMzB,IAAS,CACd,IAAI,eAAgB,CAClB,OAAO+E,GAAa/E,CAAI,CAC1B,EACA,IAAI,WAAY,CACd,GAAM,CAAE,KAAA8E,EAAM,YAAAE,CAAY,EAAIhF,EAC9B,OAAOA,EAAK,aAAe8E,GAAQE,EAC/BC,GAAUH,EAAME,CAAW,EAC3B,MACN,EACA,IAAI,gBAAiB,CACnB,IAAME,EAAUlF,EAAK,cAAc,QACnC,OAAOkF,EAAUC,GAAeD,EAAQ,MAAM,EAAI,MACpD,EACA,IAAI,cAAe,CACjB,IAAMA,EAAUlF,EAAK,cAAc,QACnC,OAAOkF,EACHE,GAAaF,EAAS,CACpB,OAAQlF,EAAK,OACb,WAAYA,EAAK,WACjB,OAAQA,EAAK,OACb,WAAYA,EAAK,WACjB,aAAcA,EAAK,aAAe4C,EAAc,CAClD,CAAC,EACD,MACN,CACF,EAAE,EACD,MAAM5C,IAAS,CACd,cAAcqF,EAAYC,EAAY,CACpC,IAAMJ,EAAUlF,EAAK,cAAc,QAC7BuF,EAAQvF,EAAK,aACnB,OAAOkF,GAAWK,EAAQC,GAAcN,EAASK,EAAOF,EAAIC,CAAE,EAAI,IACpE,CACF,EAAE,EACD,QAAQtF,IAAS,CAChB,SAASyF,EAAgB,CACvBzF,EAAK,MAAQyF,EACbzF,EAAK,UAAY,GACjBA,EAAK,cAAgB,EACvB,EACA,eAAgB,CACdA,EAAK,UAAY,GACjBA,EAAK,cAAgB,EACvB,EACA,iBAAiB0F,EAAiB,CAChC1F,EAAK,cAAgB0F,CACvB,EACA,WAAWC,EAAY,CACrB3F,EAAK,YAAc2F,CACrB,EACA,YAAYA,EAAY,CACtB3F,EAAK,aAAe2F,CACtB,EACA,mBACEA,EACAC,EACAC,EACA,CACA7F,EAAK,eAAiB2F,EACtB3F,EAAK,wBAA0B4F,EAC/B5F,EAAK,sBAAwBA,EAAK,cAClCA,EAAK,cAAgB6F,EACrB7F,EAAK,iBACP,EACA,YAAY8F,EAAiB,CAC3B9F,EAAK,uBAAyB8F,CAChC,EACA,iBAAiBC,EAAiB,CAChC/F,EAAK,cAAgB+F,CACvB,EACA,gBAAgBC,EAAiB,CAC/BhG,EAAK,aAAegG,CACtB,EACA,cAAcC,EAAuB,CACnCjG,EAAK,WAAaiG,CACpB,EAKA,iBAAiB7D,EAAc,CAC7BpC,EAAK,cAAgBoC,EACrB,IAAMjD,EAAQa,EAAK,MACnB,GAAIb,GAAO,aAAe,QAAS,CACjC,IAAM+G,EAAaC,GAAgBhH,EAAOiD,CAAI,EAC9C/C,GAAoBF,EAAO+G,CAAU,EACrClG,EAAK,MAAQkG,CACf,CACF,EACA,aAAaE,EAAe,CAC1BpG,EAAK,UAAYoG,CACnB,EACA,YAAYC,EAAe,CACzBrG,EAAK,SAAWqG,CAClB,EACA,eAAeC,EAAqB,CAClCtG,EAAK,YAAcsG,CACrB,EACA,gBAAgBC,EAAsB,CACpCvG,EAAK,aAAeuG,CACtB,EACA,aAAaC,EAAkB,CAC7BxG,EAAK,UAAYwG,CACnB,EACA,eAAeH,EAAe,CAC5BrG,EAAK,YAAcqG,CACrB,EACA,qBAAqBA,EAAe,CAClCrG,EAAK,kBAAoBqG,CAC3B,EACA,aAAaA,EAAe,CAC1BrG,EAAK,UAAYqG,CACnB,EACA,eAAeI,EAAiB,CAC9BzG,EAAK,YAAcyG,CACrB,EACA,iBAAiBA,EAAiB,CAChCzG,EAAK,cAAgByG,CACvB,EACA,aAAahH,EAAa,CACxBO,EAAK,UAAYP,CACnB,EACA,mBAAmB2C,EAAc,CAC/BpC,EAAK,gBAAkBoC,CACzB,EAIA,cAAcsE,EAAwB,CACpC1G,EAAK,WAAa0G,CACpB,EACA,kBAAkBnF,EAA+B,CAC/CvB,EAAK,eAAiBuB,CACxB,EASA,mBAAmBoF,EAAgB,CACjC,IAAMC,EAAS5G,EAAK,gBAElB4G,IAAW,QACXC,GAAS,CAAC,MAAG,cAAW7G,CAAI,EAAE,KAAK,CAAC,EAAE,KACpC4E,GAASA,EAA0B,KAAOgC,CAC5C,IAEA5G,EAAK,gBAAkB2G,EAE3B,EACA,eAAezD,EAAuB,CACpClD,EAAK,YAAckD,CACrB,EACA,cAAc4D,EAAyC,CACrD9G,EAAK,WAAa8G,CACpB,EACA,eAAeC,EAAwB,CACrC/G,EAAK,YAAc+G,CACrB,EACA,gBAAgB7D,EAAuB,CACrClD,EAAK,aAAekD,CACtB,EACA,gBAAgBA,EAAuB,CACrClD,EAAK,aAAekD,CACtB,EACA,WAAW8D,EAAkB,CAC3BhH,EAAK,UAAYgH,CACnB,EACA,cAAe,CACbhH,EAAK,aAAe,KACpBA,EAAK,UAAY,EACnB,EAGA,uBAAwB,CACtBA,EAAK,YAAc,KACnBA,EAAK,YAAc,KACnBA,EAAK,aAAe,KACpBA,EAAK,aAAe,KACpBA,EAAK,UAAY,EACnB,EACA,gBAAgBkD,EAAgB,CAC9B,IAAMmB,EAAOrE,EAAK,UAAU,IAAIkD,CAAM,EAChCC,EAAWnD,EAAK,SAChBoD,EAAYpD,EAAK,cACvB,GAAIqE,GAAQlB,GAAYC,EAAW,CACjC,IAAM6D,KAAU,cAAWjH,CAAI,EACzBW,EAASX,EAAK,YAIdqD,EAAO1C,EACT2C,GAAkB,CAAE,OAAAJ,EAAQ,SAAAC,EAAU,UAAAC,CAAU,CAAC,EACjD,UACA,6BAA0B6D,CAAO,GACnCA,EAAQ,WACNA,EAAQ,UAAU,oBAAqB,cAAe,CACpD,YAAa,CACX,GAAI5C,EAAK,GACT,KAAMA,EAAK,KACX,OAAQA,EAAK,OACb,MAAOA,EAAK,MACZ,KAAMA,EAAK,QAAQ,KACnB,WAAYA,EAAK,QAAQ,QAIzB,qBAAsBE,GAAgBF,CAAI,GAAG,OAI7C,sBAAuBE,GAAgBF,CAAI,GAAG,UAM9C,UAAWA,EAAK,SAAS,KAAK,IAAI,EAClC,GAAI1D,GAAU0C,EACV,CACE,QAAS1C,EAAO,QAChB,aAAcA,EAAO,aACrB,MAAO0C,EAAK,MACZ,IAAKA,EAAK,GACZ,EACA,CAAC,CACP,CACF,CAAC,CACH,CAEJ,CACF,EAIA,aAAa6D,EAAWC,EAAYC,EAAY,CAC9C,GAAM,CAAE,KAAAtC,CAAK,EAAI9E,EACbA,EAAK,gBAAkB,QAAU8E,EACnCA,EAAK,iBAAiB9E,EAAK,WAAamH,CAAE,GAE1CnH,EAAK,cAAgB,OACrBA,EAAK,MAAQqH,GAAUH,CAAC,EACxBlH,EAAK,WAAamH,GAEpBnH,EAAK,WAAaoH,CACpB,EACA,KAAKE,EAAgBC,EAAiBC,EAAiB,CACrD,GAAM,CAAE,KAAA1C,CAAK,EAAI9E,EACjB,GAAIA,EAAK,gBAAkB,QAAU8E,EAAM,CACzCA,EAAK,OAAOA,EAAK,QAAUwC,EAAQC,CAAO,EAC1C,MACF,CACAvH,EAAK,cAAgB,OACrB,IAAMY,EAAI6G,GAAUzH,EAAMsH,EAAQC,EAASC,EAASxH,EAAK,SAAS,EAClEA,EAAK,MAAQY,EAAE,MACfZ,EAAK,WAAaY,EAAE,WACpBZ,EAAK,WAAaY,EAAE,UACtB,EACA,kBAAmB,CACjBZ,EAAK,uBAAyB,GAC9BA,EAAK,eACP,EAEA,mBAAoB,CAClBA,EAAK,kBACP,EACA,WAAY,CACV,IAAM0C,EAAS1C,EAAK,aACd0H,EAAe1H,EAAK,aAAe4C,EAAc,EAEvD,GAAI5C,EAAK,gBAAkB,OAAQ,CAC7B0C,GAAUgF,EAAe,IAC3B1H,EAAK,WAAa2H,GAChBjF,EACAgF,EACA1H,EAAK,MACP,GAEF,MACF,CAIA,IAAM4H,EAAMlF,EACRmF,GACEnF,EACA1C,EAAK,UACLA,EAAK,aACLA,EAAK,SACP,EACA,OACA4H,IACF5H,EAAK,MAAQ4H,EAAI,MACjB5H,EAAK,WAAa4H,EAAI,WACtB5H,EAAK,WAAa4H,EAAI,WAE1B,EACA,kBAAmB,CACjB5H,EAAK,YAAc,OACnBA,EAAK,aAAe,OACpBA,EAAK,eAAiB,OACtBA,EAAK,wBAA0B,OAC/BA,EAAK,cAAgB,MACvB,CACF,EAAE,EACD,QAAQA,IAAS,CAIhB,cAAc8H,EAAeC,EAAoB,CAC/C,IAAMC,EAAWhI,EAAK,gBAAkB,OACpCgI,IACFhI,EAAK,cAAgB,QAEvBA,EAAK,MAAQ8H,EACb9H,EAAK,WAAa+H,EACdC,GACFhI,EAAK,UAAU,CAEnB,EAGA,aAAc,CACRA,EAAK,gBAAkB,SACrBA,EAAK,cAAc,cACrBA,EAAK,cAAgB,QAErBA,EAAK,cAAgB,MACrBA,EAAK,UAAU,GAGrB,CACF,EAAE,EACD,QAAQA,GAAQ,CAIf,SAASiI,GAAwB,CAC/B,aAAajI,EAAK,kBAAkB,EACpCA,EAAK,uBAAyB,GAC9BA,EAAK,mBAAqB,WAAW,IAAM,IACrC,WAAQA,CAAI,GACdA,EAAK,iBAAiB,CAE1B,EAAG7B,EAAoB,CACzB,CAQA,SAAS+J,GAA6B,CACpC,qBAAqBlI,EAAK,mBAAmB,EAC7CA,EAAK,oBAAsB,sBAAsB,IAAM,IACjD,WAAQA,CAAI,GACdA,EAAK,kBAAkB,CAE3B,CAAC,CACH,CAEA,MAAO,CAsBL,SAASkD,EAAgBiF,EAAYC,EAAY,CAC/C,IAAMrG,EAAY/B,EAAK,cAAc,cACrC,GAAI+B,IAAYmB,CAAM,EAAG,CACvB,QAAWmF,KAAOtG,EAAUmB,CAAM,EAChCmF,EAAI,GAAKF,EACTE,EAAI,GAAKD,EAEXF,EAA2B,CAC7B,CACF,EACA,sBAAAD,CACF,CACF,CAAC,EACA,QAAQjI,GAAQ,CACf,IAAIsI,EAEJ,SAASC,EAAWC,EAAwB,CAC1C,GAAM,CAAE,WAAAC,CAAW,KAAI,cAAWzI,CAAI,EACtC,OAAOyI,EAAW,QAAK,mBAAgBzI,CAAI,EAAG,qBAAsB,CAClE,GAAGwI,EACH,OAAQF,GAAgB,OAIxB,eAAgBI,GAAU,CACxB1I,EAAK,oBAAiB,qBAAkB0I,CAAM,GAAK,EAAE,CACvD,CACF,CAAC,CACH,CAMA,SAASC,EAAexJ,EAAc,CACpC,OAAOyJ,GAAUzJ,EAAO,CACtB,QAASa,EAAK,cACd,aAAcA,EAAK,aACnB,aAAcA,EAAK,YACrB,CAAC,CACH,CAMA,SAAU6I,EAAc1J,EAAc,CACpC,IAAM2J,EAAQ,YAAY,IAAI,EACxBC,EAAQC,EAAkBhJ,EAAK,gBAAgB,EAAE,IACrDb,EACAa,EAAK,YACLA,EAAK,KAAOA,EAAK,cAAc,WAAa,MAC9C,EACA,GAAI+I,EACF,MAAO,CAAE,OAAQA,EAAO,SAAU,YAAY,IAAI,EAAID,CAAM,EAE9D,IAAM1J,EAAQF,GAAeC,CAAK,EAC5BM,EAAMkJ,EAAexJ,CAAK,EAC1B8J,EAAM7J,EAAM,IAAIK,CAAG,EACzB,GAAIwJ,EACF,MAAO,CAAE,OAAQA,EAAK,SAAU,YAAY,IAAI,EAAIH,CAAM,EAE5D,IAAMI,EAAY,MAAMC,GACtBhK,EACA,CACE,QAASa,EAAK,cACd,aAAcA,EAAK,aACnB,aAAcA,EAAK,YACrB,EACAuI,CACF,EAIA,OAAA/I,GAASJ,EAAOK,EAAKyJ,EAAS,MAAM,EAC7BA,CACT,CAcA,IAAIE,EAAc,EAKlB,SAAUC,EAAWlK,EAAc,CACjC,IAAMqJ,EAAU,EAAEY,EACZE,EAAS,IAAMtJ,EAAK,QAAUb,GAASqJ,IAAYY,EACnDG,EAASjB,GAAgB,OAC3BkB,EACJ,GAAI,CACFA,EAAW,MAAOX,EAAc1J,CAAK,CACvC,OAASsK,EAAG,CAMV,GAAI,CAACH,EAAO,GAAKC,GAAQ,QACvB,MAAO,GAET,MAAME,CACR,CACA,IAAMC,EAAOJ,EAAO,EACpB,OAAII,IACF1J,EAAK,aAAewJ,EAAS,OAC7BxJ,EAAK,YAAYwJ,EAAS,QAAQ,GAE7BE,CACT,CAIA,SAAUC,EACRC,EACAxH,EACAzB,EACAkJ,EAAgB,GAChB,CACA7J,EAAK,iBAAiB,aAAa,EACnC,IAAMb,EAAQ2K,GAAUF,EAAMxH,EAAM,CAClC,cAAepC,EAAK,eAAiBW,GAAQ,aAC7C,SAAUX,EAAK,aACjB,CAAC,EACK+J,EAAWF,EAAgB7J,EAAK,aAAe,KACrDA,EAAK,MAAQb,EACba,EAAK,YAAcW,EACnBX,EAAK,aAAe,OACpBA,EAAK,aAAe,OACpBA,EAAK,aAAe,OACpBA,EAAK,SAAW,CAAC,EAKjBA,EAAK,sBAAsB,EACvB+J,IAAa,MAAQ/J,EAAK,UAAU,IAAI+J,CAAQ,IAClD/J,EAAK,aAAe+J,GAEtB/J,EAAK,iBAAiB,kBAAkB,EACxC,MAAOqJ,EAAWlK,CAAK,CACzB,CAKA,IAAI6K,EAAW,EAEf,SAASC,GAAY,CACnB,IAAMC,EAAO,EAAEF,EACf,OAAA1B,GAAgB,MAAM,EACtBA,EAAiB,IAAI,gBACrBtI,EAAK,aAAe,GACb,CACL,OAAQ,IAAMkK,IAASF,EACvB,OAAQ1B,EAAe,MACzB,CACF,CAQA,SAAU6B,EACRpM,EACA4C,EACA2I,EACA,CAGA,IAAMvF,EAASjG,GAAaC,CAAa,EACzC,GAAI,EAAAA,EAAc,OAAS,oBAAsBgG,IAAW,QAG5D,GAAI,CACF,IAAMqG,EAAY,QAAM,cAAWpK,CAAI,EAAE,WAAW,QAClD,mBAAgBA,CAAI,EACpB,kBACA,CACE,cAAe,CACb,KAAM,yBACN,IAAK,GAAG+D,CAAM,kBACd,QAAShG,EAAc,QACvB,oBAAqBA,EAAc,mBACrC,EACA,QAAS,CAAC4C,CAAM,CAClB,CACF,EACI2I,EAAO,IACTtJ,EAAK,aAAeoK,EAAS,IAAIC,IAAM,CACrC,QAAS1J,EAAO,QAChB,MAAO0J,EAAE,IAAI,OAAO,EACpB,IAAKA,EAAE,IAAI,KAAK,EAChB,aAAcA,EAAE,IAAI,cAAc,EAClC,UAAYA,EAAE,IAAI,WAAW,GAA4B,EACzD,UAAWA,EAAE,IAAI,WAAW,EAC5B,qBAAsBA,EAAE,IAAI,sBAAsB,EAClD,oBAAqBA,EAAE,IAAI,qBAAqB,EAChD,SAAUA,EAAE,IAAI,UAAU,EAC1B,eAAgB,OAChB,cAAe,MACjB,EAAE,EAEN,OAASZ,EAAG,CACV,QAAQ,KAAK,mDAAoDA,CAAC,CACpE,CACF,CAKA,SAAUa,EACR7D,EACA9F,EACA4J,EACA,CACA,IAAMtD,KAAU,cAAWjH,CAAI,EACzBwK,EAAS/D,EACXQ,EAAQ,OAAO,KAAKrG,GAAKA,EAAE,UAAY6F,CAAO,EAC9C,OACJ,GAAK+D,EAGL,GAAI,CACF,OAAQ,MAAMvD,EAAQ,WAAW,QAC/B,mBAAgBjH,CAAI,EACpB,kBACA,CACE,iBAAe,kBAAewK,EAAQ,SAAS,EAC/C,QAAS,CAAC7J,CAAM,CAClB,CACF,CACF,OAAS8I,EAAG,CACV,QAAQ,KAAK,wBAAwBc,CAAI,kBAAmBd,CAAC,EAC7D,MACF,CACF,CAIA,SAAUgB,EAAU9J,EAAwB2I,EAAuB,CACjE,IAAMc,EAAW,MAAOE,EACtBtK,EAAK,WAAW,QAChBW,EACA,OACF,EACIyJ,GAAYd,EAAO,IACrBtJ,EAAK,aAAe0K,GAAeN,CAAQ,EAE/C,CAIA,SAAUO,EAAYhK,EAAwB2I,EAAuB,CACnE,IAAMc,EAAW,MAAOE,EACtBtK,EAAK,aAAa,QAClBW,EACA,SACF,EACIyJ,GAAYd,EAAO,IACrBtJ,EAAK,aAAe4K,GAAiBR,CAAQ,EAEjD,CAMA,SAAUS,EACRzI,EACA0I,EACAnK,EACA,CACA,GAAM,CAAE,OAAA2I,EAAQ,OAAAC,CAAO,EAAIU,EAAU,EACrCjK,EAAK,UAAY,GACjBA,EAAK,MAAQ,OACb,GAAI,CACF,IAAM4J,EACJ,OAAOkB,GAAW,SACdA,EACE,MAAMA,EAAOvB,CAAM,EACvBD,EAAO,IACT,MAAOK,EAAeC,EAAMxH,EAAMzB,CAAM,EAE5C,OAAS8I,EAAG,CACNH,EAAO,IACT,QAAQ,MAAM,iCAAkCG,CAAC,EACjDzJ,EAAK,MAAQyJ,EAEjB,QAAE,CACIH,EAAO,GACTtJ,EAAK,cAAc,CAEvB,CACA,OAAOsJ,EAAO,CAChB,CAEA,SAASyB,GAAY,CACnBzC,GAAgB,MAAM,EACtBA,EAAiB,OACjB0B,IACAZ,GACF,CAEA,MAAO,CAIL,YAAa,CACX2B,EAAU,EACV/K,EAAK,MAAQ,OACbA,EAAK,YAAc,OACnBA,EAAK,aAAe,OACpBA,EAAK,aAAe,OACpBA,EAAK,aAAe,OACpBA,EAAK,aAAe,OACpBA,EAAK,SAAW,CAAC,EACjBA,EAAK,MAAQ,OACbA,EAAK,UAAY,GACjBA,EAAK,aAAe,GACpBA,EAAK,cAAgB,GACrBA,EAAK,sBAAsB,EAC3BA,EAAK,iBAAiB,CACxB,EAEA,UAAW,CACT+K,EAAU,EACV/K,EAAK,UAAY,GACjBA,EAAK,cAAgB,GACrBA,EAAK,aAAe,EACtB,EACA,eAAgB,CACd+K,EAAU,CACZ,EACA,WAAS,QAAK,UAAWnB,EAAcxH,EAAO,eAAgB,CAC5D,MAAOyI,EAAazI,EAAMwH,CAAI,CAChC,CAAC,EACD,uBAAqB,QAAK,UACxBoB,EACArK,EACA,CACAX,EAAK,iBAAiB,cAAc,EACpC,IAAM0J,EAAO,MAAOmB,EAClB,QAASG,EACJA,EAAS,IAAI,MAAM,GAAG,EAAE,IAAI,GAAK,MAClC,MACJzB,MACE,iBAAayB,CAAQ,EAAE,SAAS,CAAE,SAAU,OAAQ,OAAAzB,CAAO,CAAC,EAC9D5I,CACF,EACIA,GAAU+I,GAAQ1J,EAAK,QACzB,MAAO2K,EAAYhK,EAAQ,IAAMX,EAAK,cAAgBW,CAAM,EAEhE,CAAC,EAID,eAAa,QAAK,UAChB5C,EACA4C,EACAsK,EAA2B,CAAC,EAC5B,CACA,GAAM,CAAE,OAAA3B,EAAQ,OAAAC,CAAO,EAAIU,EAAU,EACrCjK,EAAK,UAAY,GACjBA,EAAK,MAAQ,OACbA,EAAK,iBAAiB,mBAAmB,EACzC,GAAI,CACF,IAAMkL,EAAa,YAAY,IAAI,EAC7BC,EAAW,QAAM,cAAWnL,CAAI,EAAE,WAAW,QACjD,mBAAgBA,CAAI,EACpB,cACA,CAAE,cAAAjC,EAAe,OAAA4C,EAAQ,KAAAsK,EAAM,OAAA1B,CAAO,CACxC,EACA,GAAI,CAACD,EAAO,EACV,OAGF,GADAtJ,EAAK,WAAW,YAAY,IAAI,EAAIkL,CAAU,EAC1C,CAACC,EACH,MAAM,IAAI,MACR,iHACF,EAGF,GADA,MAAOxB,EAAewB,EAASC,EAASzK,CAAM,EAAGA,EAAQ,EAAI,EACzD,CAAC2I,EAAO,EACV,OAKFtJ,EAAK,iBAAiB,qBAAqB,EAC3C,MAAM,QAAQ,IAAI,CAChBiL,EAAK,OAAS,SACV,UACA,QAAKd,CAAW,EAAEpM,EAAe4C,EAAQ2I,CAAM,KACnD,QAAKmB,CAAS,EAAE9J,EAAQ2I,CAAM,KAC9B,QAAKqB,CAAW,EAAEhK,EAAQ2I,CAAM,CAClC,CAAC,CACH,OAASG,EAAG,CACNH,EAAO,IACT,QAAQ,MAAM,gCAAiCG,CAAC,EAChDzJ,EAAK,MAAQyJ,EAEjB,QAAE,CACIH,EAAO,GACTtJ,EAAK,cAAc,CAEvB,CACF,CAAC,EAKD,iBAAe,QAAK,WAAa,CAC/B,IAAMW,EAASX,EAAK,YACdyG,EAAUzG,EAAK,aAAa,QAC9BW,IACF,MAAOgK,EACLhK,EACA,IACEX,EAAK,cAAgBW,GACrBX,EAAK,aAAa,UAAYyG,CAClC,EAEJ,CAAC,EACD,eAAa,QAAK,WAAa,CAC7B,IAAM9F,EAASX,EAAK,YACdyG,EAAUzG,EAAK,WAAW,QAC5BW,IACF,MAAO8J,EACL9J,EACA,IACEX,EAAK,cAAgBW,GACrBX,EAAK,WAAW,UAAYyG,CAChC,EAEJ,CAAC,EAMD,aAAW,QAAK,UAAW5E,EAAyB,CAClD,IAAM1C,EAAQa,EAAK,MACnB,GAAI,CAACb,EACH,OAEF,IAAMkM,EAAMC,GAAenM,EAAOoM,GAAiB1J,CAAM,CAAC,EAC1D,GAAIwJ,EAAI,MAAM,SAAW,EAAG,IAC1B,cAAWrL,CAAI,EAAE,OACf,yFACA,MACF,EACA,MACF,CACA,GAAM,CAAE,OAAAsJ,CAAO,EAAIW,EAAU,EAC7BjK,EAAK,SAAW,CACd,GAAGA,EAAK,SACR,CACE,MAAAb,EACA,WAAYa,EAAK,iBACjB,MAAOb,EAAM,KACb,aAAca,EAAK,YACrB,CACF,EACAA,EAAK,aAAe,OACpB,IAAMwL,EAAQ,GAAGC,GAAkB3J,EAAeD,EAAQ7B,EAAK,YAAY,EAAE,IAAI,CAAC,OAAO6B,EAAO,OAAO,IAAIA,EAAO,MAAM,eAAe,CAAC,GACxI7B,EAAK,MAAQ,CAAE,GAAGqL,EAAK,KAAMG,CAAM,EAC/BxL,EAAK,mBAAqB,aAC5BA,EAAK,WAAa,SAEpBA,EAAK,sBAAsB,EAC3BA,EAAK,cAAgB,MACrBA,EAAK,UAAY,GACjB,GAAI,EACE,MAAOqJ,EAAWrJ,EAAK,KAAK,IAC9BA,EAAK,cAAc,CAEvB,OAASyJ,EAAG,CACNH,EAAO,IACTtJ,EAAK,MAAQyJ,EACbzJ,EAAK,cAAc,EAEvB,CACF,CAAC,EACD,eAAa,QAAK,WAAa,CAC7B,IAAMV,EAAOU,EAAK,WAClB,GAAKV,EAGL,CAAAU,EAAK,SAAWA,EAAK,SAAS,MAAM,EAAG,EAAE,EACzCA,EAAK,MAAQV,EAAK,MAClBU,EAAK,WAAaV,EAAK,WACvBU,EAAK,aAAeV,EAAK,aACzBU,EAAK,sBAAsB,EAC3BA,EAAK,cAAgB,MACrBA,EAAK,UAAY,GACjB,GAAI,EACE,MAAOqJ,EAAW/J,EAAK,KAAK,IAC9BU,EAAK,cAAc,CAEvB,OAASyJ,EAAG,CACVzJ,EAAK,MAAQyJ,EACbzJ,EAAK,cAAc,CACrB,EACF,CAAC,EACD,mBAAiB,QAAK,WAAa,CACjC,IAAMb,EAAQa,EAAK,MACnB,GAAKb,EAGL,CAAAa,EAAK,UAAY,GACjBA,EAAK,iBAAiB,kBAAkB,EAExC,GAAI,EAKE,MAAOqJ,EAAWlK,CAAK,IACzBa,EAAK,cAAc,CAEvB,OAASyJ,EAAG,CACV,QAAQ,MAAM,oCAAqCA,CAAC,EACpDzJ,EAAK,MAAQyJ,EACbzJ,EAAK,cAAc,CACrB,EACF,CAAC,CACH,CACF,CAAC,EACA,QAAQA,IAAS,CAChB,sBAAsB0L,EAAmB,CACvC,GAAI,CAAC1L,EAAK,kBAAmB,IAO3B,eACEA,KACA,WAAQ,IAAM,CAEVA,EAAK,iBACL,aAAU,IAAMA,EAAK,gBAAkB,KAAK,GAE5CA,EAAK,UAAU,CAEnB,CAAC,CACH,KAWA,eACEA,KACA,WAAQ,IAAM,CACZ,IAAM2L,EAAQC,MAAa,cAAW5L,CAAI,EAAE,OAAO,KACnD,aAAU,IAAM,CACd,IAAMW,EAASX,EAAK,YACdb,EAAQa,EAAK,MACfW,GAAUxB,GACZa,EAAK,eACH2L,GAASE,GAAcF,EAAOhL,CAAM,EAChCmL,GAAgB,CAAE,MAAAH,EAAO,MAAOxM,EAAM,KAAM,CAAC,EAC7C,IACN,CAEJ,CAAC,CACH,CAAC,CACH,KAyBA,eACEa,KACA,YACE,IAAM,GAAGA,EAAK,KAAK,IAAIA,EAAK,UAAU,IAAIA,EAAK,UAAU,GACzD,IAAM,CACJA,EAAK,eAAe,IAAI,EACxBA,EAAK,eAAe,IAAI,CAC1B,EACA,CAAE,KAAM,iCAAkC,CAC5C,CACF,EAKA,IAAI+L,EAAgB,MACpB,eACE/L,KACA,WAAQ,IAAM,CACZ,GAAM,CAAE,MAAA8H,CAAM,EAAI9H,EACZgM,EAAWlN,GAAekB,CAAI,EACpC,GAAI+L,EAAe,CACjBA,EAAgB,GAChB,MACF,CACA,IAAMlG,KAAQ,aAAU,IAAM7F,EAAK,aAAa,EAC5C6F,GAAO,QAAUiC,GAASmE,GAASpG,EAAM,OAAQmG,CAAQ,GAG7DhM,EAAK,sBAAsB,CAC7B,CAAC,CACH,KAMA,eACEA,KACA,WAAQ,IAAM,CACZ,IAAMyB,EAAIzB,EAAK,wBACTkM,EAAclM,EAAK,YACnBmM,EAAcnM,EAAK,YACnBoM,EAAepM,EAAK,aAE1B,GADA1B,GAAS0B,EAAK,eAAe,EACzByB,EAAG,CACL,IAAM4K,EAAQ,IAAI,IACdD,IAAiB,MACnBC,EAAM,IAAID,EAAclO,EAAe,EAErCgO,IAAgB,MAAQA,IAAgBE,GAC1CC,EAAM,IAAIH,EAAajO,EAAc,EAEvCwD,EAAE,kBAAkB4K,CAAK,EACzB5K,EAAE,iBAAiB0K,EAAalO,EAAc,EAC9C+B,EAAK,UAAU,CACjB,CACF,CAAC,CACH,CACF,CAKAA,EAAK,uBAAiC0L,EAAS,KAAO,CAIpD,OAASjK,GAAgB,CACvBA,EAAE,OAAOzB,EAAK,UAAWA,EAAK,YAAY,EAC1C,IAAMmD,EAAWnD,EAAK,SACtB,GAAIA,EAAK,cAAc,QACrB,OAAA1B,GAAS0B,EAAK,aAAa,EAC3ByB,EAAE,eAAepD,EAAW,EAC5B2B,EAAK,mBAAmB,EAAG,EAAG,CAC5B,SAAO,aAAU,IAAMA,EAAK,KAAK,EACjC,UAAQ,aAAU,IAAMlB,GAAekB,CAAI,CAAC,CAC9C,CAAC,EACM,GAET,GAAIA,EAAK,eAAiBA,EAAK,OAASmD,EAAU,CAIhD7E,GAAS0B,EAAK,cAAeA,EAAK,gBAAgB,EAClD,IAAMsM,EAAgB,YAAY,IAAI,EAChCC,KAAiB,aAAU,IAC/BC,GAAO1N,GAAekB,CAAI,EAAG5B,EAAoB,CACnD,EACMqO,EAAQC,GAAc,CAC1B,cAAe1M,EAAK,cACpB,MAAOA,EAAK,MACZ,SAAAmD,EACA,YAAanD,EAAK,qBAClB,gBAAiBA,EAAK,gBACtB,mBAAoBA,EAAK,mBACzB,UAAWA,EAAK,mBAChB,UAAWA,EAAK,UAChB,UAAWA,EAAK,cAKhB,QAAM,aAAU,IAAMA,EAAK,SAAS,EACpC,aAAcA,EAAK,aACnB,eAAAuM,EAKA,cAAevM,EAAK,cACpB,UAAWA,EAAK,oBAChB,YAAaA,EAAK,kBAGlB,QAASA,EAAK,gBAChB,CAAC,EACD,OAAAyB,EAAE,eAAegL,CAAK,EACtBzM,EAAK,mBACH,YAAY,IAAI,EAAIsM,EACpBG,EAAM,YAAY,OAClB,CAAE,SAAO,aAAU,IAAMzM,EAAK,KAAK,EAAG,OAAQuM,CAAe,CAC/D,EACO,EACT,CAKA,MAAO,EACT,EAEA,OAAS9K,GAAgB,CACvB,GAAI,CAACzB,EAAK,cACR,MAAO,GAST,IAAM2M,KAAM,WAAO,EACnB,OAAAlL,EAAE,gBAAgB,CAChB,OAAQzB,EAAK,OAAS2M,EACtB,OAAQ3M,EAAK,OAAS2M,EACtB,WAAY3M,EAAK,WAAa2M,EAC9B,WAAY3M,EAAK,WAAa2M,EAK9B,IAAAA,CACF,CAAC,EACDlL,EAAE,OAAOzB,EAAK,SAAW,CAAC,IAAM,IAAM,IAAM,CAAC,EAAI,CAAC,EAAG,EAAG,EAAG,CAAC,CAAC,EAC7DA,EAAK,eAAY,aAAU,IAAMA,EAAK,eAAe,CAAC,EAC/C,EACT,CACF,EAAE,CACJ,CACF,EAAE,EACD,QAAQA,IAAS,CAEhB,aAAaiG,EAAuB,CAClC,OAAAjG,EAAK,cAAciG,CAAI,EAChBjG,EAAK,gBAAgB,CAC9B,EACA,YAAa,CACPA,EAAK,gBACPA,EAAK,SAAS,EACdA,EAAK,MAAQ,OAEjB,EAEA,WAAY,CAAC,EACb,aAAc,CAGPA,EAAK,oBACRA,EAAK,cAAgB,OAEzB,CACF,EAAE,EACD,QAAQA,IAAS,CAWhB,iBAAiB4M,EAAuD,CACtE,IAAM3F,KAAU,cAAWjH,CAAI,EACzB0E,EAAckI,EAAO,WAAa5M,EAAK,aAAa,aACpD2G,EAASkG,GAAiB,CAC9B,QAAA5F,EACA,SAAU2F,EAAO,SACjB,SAAUA,EAAO,SACjB,gBAAiB5M,EAAK,gBACtB,OAAQ8M,GAAa,CACnB,QAAA7F,EACA,aAAc2F,EAAO,SACrB,MAAOlI,EAAc1E,EAAK,cAAgB,MAC5C,CAAC,CACH,CAAC,EACG0E,GACF1E,EAAK,mBAAmB2G,CAAM,CAElC,EAIA,sBAAsBiG,EAGnB,CACDG,GAAsB,CACpB,WAAS,cAAW/M,CAAI,EACxB,SAAU4M,EAAO,SACjB,SAAUA,EAAO,SACjB,gBAAiB5M,EAAK,eACxB,CAAC,CACH,EACA,gBAAgByG,EAAiB,CAC/BuG,GAAkB,CAChB,WAAS,cAAWhN,CAAI,EACxB,aAAcA,EAAK,qBACnB,QAAAyG,EACA,aAAczG,EAAK,aACrB,CAAC,CACH,CACF,EAAE,EACD,MAAMA,IAAS,CAId,IAAI,qBAAsB,CACxB,IAAMuB,EAAUvB,EAAK,qBAAqB,IAAIkC,GAAKA,EAAE,MAAM,EAC3D,OAAOX,EAAQ,QAAU,EACrB0L,MAAwB,cAAWjN,CAAI,EAAGuB,CAAO,EACjD,CAAC,CACP,CACF,EAAE,EACD,MAAMvB,IAAS,CAKd,gBAA6B,CAC3B,IAAMkN,EAAQlN,EAAK,YACnB,MAAO,CACL,CACE,MAAO,SACP,QAASmN,GAAa,IAAIlH,IAAS,CACjC,KAAM,QACN,MAAOA,EAAK,MACZ,QAASjG,EAAK,mBAAqBiG,EAAK,MACxC,SAAUjG,EAAK,MAAQ,CAACiG,EAAK,UAAUjG,EAAK,KAAK,EAAI,GACrD,QAAS,IAAM,CACRA,EAAK,aAAaiG,EAAK,KAAK,CACnC,CACF,EAAE,CACJ,EACA,CACE,MAAO,QACP,QAASmH,GAAc,IAAI9G,IAAW,CACpC,KAAM,QACN,MAAOA,EAAO,MACd,QAAStG,EAAK,oBAAsBsG,EAAO,MAC3C,QAAS,IAAM,CACbtG,EAAK,eAAesG,EAAO,KAAK,CAClC,CACF,EAAE,CACJ,EACA,GAAI4G,EAAM,OAAS,EACf,CACE,CACE,MAAO,OACP,QAAS,CACP,CACE,KAAM,QACN,MAAO,OACP,QAASlN,EAAK,kBAAoB,GAClC,QAAS,IAAM,CACbA,EAAK,mBAAmB,EAAE,CAC5B,CACF,EACA,GAAGkN,EAAM,IAAIG,IAAS,CACpB,KAAM,QACN,MAAOA,EAAK,MACZ,QAASrN,EAAK,kBAAoBqN,EAAK,KACvC,QAAS,IAAM,CACbrN,EAAK,mBAAmBqN,EAAK,IAAI,CACnC,CACF,EAAE,CACJ,CACF,CACF,EACA,CAAC,EACL,GAAIrN,EAAK,mBAAqB,YAC9BA,EAAK,cAAc,OAAS,EACxB,CACE,CACE,MAAO,SACP,QAAS,CACP,CACE,KAAM,QACN,MAAO,eACP,QAASA,EAAK,YAAc,GAC5B,QAAS,IAAM,CACbA,EAAK,aAAa,EAAE,CACtB,CACF,EACA,GAAGA,EAAK,cAAc,IAAI,CAAC,CAAE,IAAAP,EAAK,KAAA2C,EAAM,KAAAkL,CAAK,KAAO,CAClD,KAAM,QACN,MAAO,GAAGlL,CAAI,SAAMkL,EAAK,eAAe,CAAC,WACzC,QAAStN,EAAK,YAAcP,EAC5B,QAAS,IAAM,CACbO,EAAK,aAAaP,CAAG,CACvB,CACF,EAAE,CACJ,CACF,CACF,EACA,CAAC,EACL,GAAIO,EAAK,YACL,CAAC,EACD,CACE,CACE,MAAO,UACP,QAAS,IAAM,CACbA,EAAK,KAAK,IAAKA,EAAK,UAAY,EAAGA,EAAK,aAAe,CAAC,CAC1D,CACF,EACA,CACE,MAAO,WACP,QAAS,IAAM,CACbA,EAAK,KACH,EAAI,IACJA,EAAK,UAAY,EACjBA,EAAK,aAAe,CACtB,CACF,CACF,EACA,CACE,MAAO,cACP,QAAS,IAAM,CACbA,EAAK,UAAU,CACjB,CACF,CACF,EACJ,CACE,KAAM,WACN,MAAO,eACP,QAASA,EAAK,YACd,QAAS,IAAM,CACbA,EAAK,eAAe,CAACA,EAAK,WAAW,CACvC,CACF,EACA,CACE,KAAM,WACN,MAAO,sBACP,QAASA,EAAK,kBACd,QAAS,IAAM,CACbA,EAAK,qBAAqB,CAACA,EAAK,iBAAiB,CACnD,CACF,EACA,CACE,KAAM,WACN,MAAO,wBACP,QAASA,EAAK,UACd,QAAS,IAAM,CACbA,EAAK,aAAa,CAACA,EAAK,SAAS,CACnC,CACF,CACF,CACF,EACA,iBAA8B,CAC5B,IAAMuN,EAAoB,CAAC,EAC3B,QAAWC,KAAQC,GAAqB,CACtC,aAAczN,EAAK,qBACnB,cAAeA,EAAK,oBACpB,aAAc4M,GAAU,CACtB5M,EAAK,iBAAiB4M,CAAM,CAC9B,EACA,cAAenG,GAAW,CACxBzG,EAAK,gBAAgByG,CAAO,CAC9B,CACF,CAAC,KACC,2BAAuB8G,EAAOC,CAAI,EAEpC,OAAOD,CACT,CACF,EAAE,CACN,CsB/jFA,IAAAG,GAA+B,SAI/B,SAASC,GAAcC,EAAmC,CACxD,OAAO,MAAM,QAAQA,CAAK,GAAKA,EAAM,MAAMC,GAAQ,OAAOA,GAAS,QAAQ,CAC7E,CAIO,SAASC,GAAWC,EAA8B,CACvD,IAAMC,KAAyB,mBAAeD,EAAO,eAAe,EAC9DE,EAAQN,GAAcK,CAAa,EAAIA,EAAc,MAAM,CAAC,EAAI,CAAC,EACvE,OAAOC,EAAM,OAAS,EAAIA,EAAQ,MACpC,CAMO,SAASC,GAAuBH,EAA8B,CACnE,IAAMC,KAAyB,mBAAeD,EAAO,eAAe,EACpE,OAAOJ,GAAcK,CAAa,EAAIA,EAAc,CAAC,EAAI,MAC3D,CAEO,SAASG,GACdC,EACAC,EACA,CACA,OAAOD,IAAsB,QAAaA,IAAsBC,EAC5D,OACA,sCAAsCD,CAAiB,4BAA4BA,CAAiB,EAC1G,CvBhBe,SAARE,IAAqC,CAC1C,OAAO,QACJ,QACC,kBACA,iBACAC,GAAe,EACf,QAAM,MAAM,CACV,KAAM,QAAM,QAAQ,iBAAiB,EAGrC,YAAa,QAAM,MAAM,QAAM,OAAqB,CAAC,EAErD,aAAc,QAAM,MAAM,QAAM,OAAuB,CAAC,EAIxD,cAAe,QAAM,SAAS,QAAM,OAAQ,EAAE,EAC9C,gBAAiB,QAAM,SAAS,QAAM,OAAQ,CAAC,EAC/C,mBAAoB,QAAM,MAAM,QAAM,OAAiB,CAAC,EACxD,YAAa,QAAM,SAAS,QAAM,OAAQC,EAAmB,CAC/D,CAAC,CACH,EACC,MAAMC,IAAS,CACd,IAAI,cAAe,CACjB,OAAOA,EAAK,EACd,EAEA,IAAI,WAAY,CACd,OAAOA,EAAK,KACd,EACA,IAAI,aAAiD,CACnD,OAAOA,EAAK,iBACR,eAAWA,CAAI,EAAE,OAAO,KAAK,GAAK,EAAE,UAAYA,EAAK,aAAa,EAClE,MACN,EACA,IAAI,cAAe,CACjB,OACEA,EAAK,cAAgB,QACpBA,EAAK,gBAAkB,IAAMA,EAAK,eAAiB,MAExD,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,kBAAmB,CACrB,OAAOA,EAAK,cAAgB,CAACA,EAAK,OAAS,CAACA,EAAK,YACnD,EACA,WAAY,CACV,OAAOA,EAAK,gBAAgB,CAC9B,CACF,EAAE,EACD,QAAQA,GAAQ,CACf,IAAMC,EAAYD,EAAK,WACvB,MAAO,CAGL,YAAa,CACXC,EAAU,EACVD,EAAK,YAAc,OACnBA,EAAK,aAAe,OACpBA,EAAK,cAAgB,EACvB,EACA,MAAO,CACL,IAAME,EAASF,EAAK,aACpB,GAAIA,EAAK,YACP,OAAOA,EAAK,oBAAoBA,EAAK,YAAaE,CAAM,EAE1D,GAAI,CAACF,EAAK,eAAiB,CAACE,EAC1B,OAEF,IAAMC,EAAQH,EAAK,YACbI,EAAOF,EAAO,IAAMA,EAAO,MAC3BG,EAAUF,EACZG,GACEC,GAAuBJ,CAAK,EAC5BD,EAAO,YACT,EACA,OACJ,GAAI,CAACC,EACHH,EAAK,SACH,IAAI,MACF,uCAAuCA,EAAK,aAAa,2BAC3D,CACF,UACSK,EACTL,EAAK,SAAS,IAAI,MAAMK,CAAO,CAAC,UACvBD,EAAOJ,EAAK,YACrBA,EAAK,SACH,IAAI,MACF,qBAAqBQ,EAAaJ,CAAI,CAAC,uCAAkCI,EAAaR,EAAK,WAAW,CAAC,GACzG,CACF,MAEA,QAAOA,EAAK,eAAY,mBAAeG,EAAO,SAAS,EAAGD,EAAQ,CAChE,KAAMF,EAAK,gBACX,WAAYA,EAAK,kBACnB,CAAC,CAGL,CACF,CACF,CAAC,EACA,QAAQA,IAAS,CAChB,WAAY,CACLA,EAAK,KAAK,CACjB,EAIA,aAAc,CACPA,EAAK,OACHA,EAAK,KAAK,CAEnB,CACF,EAAE,CACN,CDtGA,IAAMS,MAAkB,qBAAyB,EAAE,CAAC,CAAC,EAEtC,SAARC,GAAkCC,EAA8B,CACrEA,EAAc,YAAY,IACjB,IAAI,GAAAC,QAAS,CAClB,KAAM,kBACN,YAAa,oBACb,WAAYC,GAAa,EACzB,WAAYJ,GACZ,kBAAgB,SAAK,IAAM,OAAO,sCAA8B,CAAC,CACnE,CAAC,CACF,CACH,CyBxCA,IAAAK,GAAoC,SACpCC,EAIO,SAMP,SAASC,GAAgBC,EAA6C,CACpE,IAAMC,EAAWD,EAAc,uBAC7B,SACF,EACME,EAASC,GAAiBF,EAAS,KAAKG,GAAKA,EAAE,OAASD,CAAI,EAC5DE,EAAQH,EAAM,wBAAwB,EAC5C,MAAO,CACLA,EAAM,oBAAoB,EAC1BA,EAAM,oBAAoB,EAC1BG,GACE,OAAO,OAAO,OAAO,OAAOA,CAAK,EAAkB,CACjD,oBAAqB,CAAC,aAAa,CACrC,CAAC,CACL,EAAE,OAAQD,GAAwBA,IAAM,MAAS,CACnD,CAEe,SAARE,GAA6BN,EAA8B,CAChEA,EAAc,aAAa,IAAM,CAC/B,IAAMO,KAAe,wBACnB,aACA,CAAC,EACD,CACE,qBAAmB,yBAAsBP,CAAa,EACtD,mBAAoB,SACtB,CACF,EACMQ,EAAQ,IAAI,YAAU,CAC1B,KAAM,aACN,YAAa,wBACb,aAAAD,EACA,cAAY,wBACVP,EACA,aACAO,CACF,CACF,CAAC,EACD,QAAWE,KAAWV,GAAgBC,CAAa,EACjDQ,EAAM,eAAeC,CAAO,EAE9B,OAAOD,CACT,CAAC,CACH,CCde,SAARE,GAAwCC,EAA8B,CAE3EA,EAAc,oBAAoB,6BAA8BC,GAAQ,CACtE,GAAM,CAAE,QAAAC,EAAS,GAAGC,CAAK,EAAIF,EAC7B,OAAAC,EAAQ,QAAQ,kBAAmBC,CAAI,EAChCF,CACT,CAAC,CACH,CC7CA,IAAAG,GAAqB,UAErBC,GAAwB,UCFxB,IAAAC,GAAoC,SACpCC,GAA8C,UAC9CC,GAAsB,SAcf,SAASC,IAAsB,CACpC,SAAO,wBACL,qBACA,CAKE,WAAY,CACV,KAAM,aACN,MAAO,SAAM,YAAY,aAAcC,EAAkB,EACzD,aAAc,MAChB,EAIA,YAAa,CACX,KAAM,aACN,MAAO,SAAM,YAAY,cAAeC,EAAmB,EAC3D,aAAc,MAChB,EACA,MAAG,kCAA8B,CAC/B,cAAe,IACf,OAAQ,+CACV,CAAC,CACH,EACA,CAAE,gBAAiB,GAAM,mBAAoB,WAAY,CAC3D,CACF,CC5CA,IAAAC,GAAqB,UAErBC,GAAgD,SAChDC,GAA4B,SAC5BC,GAA2B,SAC3BC,GAA6B,UAC7BC,EAAmC,SACnCC,GAGO,UAEP,IAAAC,GAAyB,UAwBzB,IAAMC,MAA2B,SAC/B,IAAM,OAAO,+CAAuC,CACtD,EAEO,SAASC,GAAkBC,EAA6C,CAC7E,OACE,QACG,QACC,qBACA,kBACA,GAAAC,SAAiB,EACjBC,GAAe,EACf,QAAM,MAAM,CACV,KAAM,QAAM,QAAQ,oBAAoB,EACxC,iBAAe,2BAAuBF,CAAY,EAIlD,KAAM,QAAM,MAAM,QAAM,OAAO,CAAC,EAGhC,UAAW,QAAM,MAAM,QAAM,OAAuB,CAAC,EAGrD,UAAW,QAAM,SAAS,QAAM,QAAS,EAAK,EAG9C,YAAa,QAAM,SAAS,QAAM,OAAQG,EAAmB,EAO7D,gBAAiB,QAAM,SAAS,QAAM,OAAQ,CAAC,EAE/C,mBAAoB,QAAM,MAAM,QAAM,OAAiB,CAAC,CAC1D,CAAC,CACH,EAEC,mBAAmBC,GAAY,CAC9B,GAAM,CAAE,KAAAC,EAAM,GAAGC,CAAK,EAAIF,EAC1B,GAAI,CAACC,EACH,OAAOD,EAET,GAAM,CAAE,KAAMG,EAAO,GAAGC,CAAM,EAAIH,EAClC,MAAO,CAAE,GAAGG,EAAO,GAAGF,CAAK,CAC7B,CAAC,EACA,SAAS,KAAO,CACf,OAAQ,EAER,cAAe,MACjB,EAAE,EACD,MAAMG,IAAS,CACd,IAAI,mBAAqC,CACvC,SAAO,YAAQA,EAAM,YAAY,CACnC,EACA,IAAI,oBAAkC,CACpC,SAAO,YAAQA,EAAM,aAAa,CACpC,EACA,IAAI,cAAe,CACjB,OAAOA,EAAK,MACd,EACA,IAAI,cAAe,CACjB,OAAOA,EAAK,YAAc,MAC5B,EACA,IAAI,kBAAmB,CACrB,OACEA,EAAK,oBACLC,GAAWD,EAAK,YAAY,aAAa,CAE7C,EAGA,IAAI,oBAAqB,CAGvB,IAAME,EAFSF,EAAK,cAAc,QAEZ,aACtB,OAAO,OAAOE,GAAU,SAAWA,EAAQ,MAC7C,CACF,EAAE,EACD,MAAMF,IAAS,CACd,OAAOG,EAA+B,CACpC,IAAMD,EAAQF,EAAK,mBACnB,OAAOE,IAAU,QAAaC,EAAUD,EAAQ,SAAW,MAC7D,EACA,OAAOE,EAAoB,CACzB,OAAOA,IAAS,SAAW,IAAWJ,EAAK,WAC7C,EACA,IAAI,SAAwB,CAC1B,OAAOA,EAAK,WAAaA,EAAK,qBAAuB,OACjD,SACA,MACN,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,cAAe,CACjB,IAAMK,EAAOL,EAAK,cAClB,OAAOK,EAAOL,EAAK,OAAOA,EAAK,OAAOK,EAAK,OAAO,CAAC,EAAI,GACzD,CACF,EAAE,EACD,MAAML,IAAS,CACd,IAAI,gBAAiB,CACnB,IAAMK,EAAOL,EAAK,cAClB,OAAOK,IAAS,QAAaA,EAAK,IAAMA,EAAK,MAAQL,EAAK,YAC5D,EACA,IAAI,sBAAuB,CACzB,IAAMK,EAAOL,EAAK,cAClB,OAAOK,EACH,qCAAqCC,EAAaD,EAAK,IAAMA,EAAK,KAAK,CAAC,SAASC,EAAaN,EAAK,YAAY,CAAC,IAChH,EACN,EACA,IAAI,oBAAqB,CACvB,MAAO,EACT,EACA,IAAI,eAAgB,CAClB,OAAOA,EAAK,YACd,CACF,EAAE,EACD,MAAMA,IAAS,CACd,IAAI,cAAmC,CAGrC,SAAO,8BACL,CACE,eAAgBA,EAAK,eACrB,MAAOA,EAAK,OAASA,EAAK,WAC5B,EACA,OACE,yBACE,CACE,YAAaA,EAAK,YAClB,WAAY,GACZ,cACEA,EAAK,MAAM,cAAgB,IAC3BA,EAAK,KAAK,oBAAsB,GAClC,UAAWA,EAAK,WAAa,CAACA,EAAK,SACnC,cAAeA,EAAK,aACpB,sBAAuB,GACvB,cAAe,GACf,YAAaA,EAAK,SAAWA,EAAK,eACpC,EACA,IAAM,GACN,IAAMA,EAAK,MAAM,sBAAwB,EAC3C,CACJ,CACF,CACF,EAAE,EACD,QAAQA,IAAS,CAEhB,mBAAmBO,EAAc,CAC/BP,EAAK,gBAAkBO,CACzB,EACA,sBAAsBC,EAAkC,CACtDR,EAAK,mBAAqBQ,CAC5B,EACA,eAAeC,EAAY,CACzBT,EAAK,YAAcS,CACrB,EAIA,KAAM,CACJ,IAAMC,EAASV,EAAK,UACpB,GAAI,CAACU,EACH,OAEF,IAAMC,EAAUC,GACdC,GAAuBb,EAAK,YAAY,aAAa,EACrDU,EAAO,YACT,EACA,GAAIC,EAAS,CACXX,EAAK,WAAW,EAChBA,EAAK,SAAS,IAAI,MAAMW,CAAO,CAAC,EAChC,MACF,CACA,IAAMG,EAASd,EAAK,UAAY,SAChC,OAAOA,EAAK,YAAYA,EAAK,cAAeU,EAAQ,CAClD,KAAMI,EAAS,EAAId,EAAK,gBACxB,WAAYA,EAAK,iBACjB,KAAMc,EAAS,SAAW,MAC5B,CAAC,CACH,CACF,EAAE,EACD,QAAQd,IAAS,CAChB,QAAQK,EAAkB,CACxB,IAAMD,EAAOJ,EAAK,OAAOK,EAAK,OAAO,EACrC,OAAAL,EAAK,UAAYI,IAAS,SAC1BJ,EAAK,UAAYe,GACfV,EACAL,EAAK,OAAOI,CAAI,EAChBY,EAAkBhB,EAAK,gBAAgB,EAAE,UAC3C,EACAA,EAAK,SACEA,EAAK,IAAI,CAClB,CACF,EAAE,EACD,QAAQA,IAAS,CAMhB,SAASK,EAAkB,CACzBL,EAAK,cAAgBK,EACrB,IAAMY,EAAUD,EAAkBhB,EAAK,gBAAgB,EAAE,WACzD,OACEA,EAAK,gBACJ,CAACA,EAAK,cACLA,EAAK,OAAOK,EAAK,OAAO,IAAML,EAAK,SACnCkB,GAASlB,EAAK,UAAWK,EAAMY,CAAO,EAEjC,IAEJjB,EAAK,QAAQK,CAAI,EACf,GACT,EACA,aAAac,EAAuB,CAClC,IAAMF,EAAUD,EAAkBhB,EAAK,gBAAgB,EAAE,WACzDA,EAAK,cAAcmB,CAAI,EACvB,IAAMd,EAAOL,EAAK,KAAOoB,GAAWpB,EAAK,IAAI,EAAI,OACjD,OACEK,GACA,CAACL,EAAK,gBACNgB,EAAkBG,CAAI,EAAE,aAAeF,EAEhCjB,EAAK,QAAQK,CAAI,EAEnBL,EAAK,gBAAgB,CAC9B,EACA,WAAY,CACLA,EAAK,IAAI,CAChB,EACA,QAAS,CACFA,EAAK,IAAI,CAChB,EACA,mBAAoB,CAClBA,EAAK,SAAS,CAChB,CACF,EAAE,EACD,QAAQA,IAAS,CAChB,WAAY,CACV,IAAMK,EAAOL,EAAK,cACdK,IACFL,EAAK,eAAe,KAAK,KAAKK,EAAK,IAAMA,EAAK,KAAK,CAAC,EACpDL,EAAK,SAASK,CAAI,EAEtB,EAIA,aAAc,CACRL,EAAK,WAAa,CAACA,EAAK,OACrBA,EAAK,IAAI,KAEhB,eACEA,KACA,aACE,IAAMA,EAAK,YACXqB,GAAU,CACHA,GACHrB,EAAK,YAAY,CAErB,EACA,CAAE,KAAM,kBAAmB,CAC7B,CACF,KACA,eACEA,KACA,aACE,IAAMA,EAAK,UACXsB,GAAS,CACHA,GACFtB,EAAK,cAAcsB,EAAM,MAAOA,EAAM,UAAU,CAEpD,EACA,CACE,OAAQ,CAACC,EAAGC,IACVD,GAAG,QAAUC,GAAG,OAASD,GAAG,aAAeC,GAAG,WAChD,gBAAiB,GACjB,KAAM,gBACR,CACF,CACF,KACA,eACExB,KACA,aACE,IAAMA,EAAK,MAAM,oBACjByB,GAAU,CACR,GAAM,CAAE,KAAAC,CAAK,EAAI1B,EACXK,EAAOoB,GAAUC,EAAON,GAAWM,CAAI,EAAI,OAC7CrB,GACFL,EAAK,SAASK,CAAI,CAEtB,EACA,CAAE,gBAAiB,GAAM,KAAM,iBAAkB,CACnD,CACF,CACF,CACF,EAAE,EACD,MAAML,IAAS,CACd,gBAA6B,CAC3B,MAAO,CACL,GAAGA,EAAK,eAAe,EACvB,CACE,MAAO,WACP,KAAM2B,GACN,QAAS,IAAM,IACb,eAAW3B,CAAI,EAAE,YAAY4B,GAAW,CACtCvC,GACA,CAAE,MAAOW,EAAM,KAAM,GAAM,QAAA4B,CAAQ,CACrC,CAAC,CACH,CACF,EACA,GAAG5B,EAAK,gBAAgB,CAC1B,CACF,CACF,EAAE,CAER,CF1Ve,SAAR6B,GAAqCC,EAA8B,CACxEA,EAAc,eAAe,IAAM,CACjC,IAAMC,EAAeC,GAAoB,EACzC,OAAO,OAAO,OACZ,IAAI,GAAAC,QAAY,CACd,KAAM,qBACN,YAAa,QACb,aAAAF,EACA,WAAYG,GAAkBH,CAAY,EAC1C,UAAW,aACX,SAAU,mBACV,kBAAgB,SAAK,IAAM,OAAO,yCAAiC,CAAC,CACtE,CAAC,EACD,CAAE,oBAAqB,CAAC,aAAa,CAAE,CACzC,CACF,CAAC,CACH,CGzBA,IAAAI,GAAwB,UCAxB,IAAAC,GAAoC,SACpCC,GAAsB,SAyBf,SAASC,GAAkBC,EAA+B,CAC/D,GAAM,CAAE,IAAAC,EAAK,QAAAC,EAAS,IAAAC,CAAI,EAAIH,EAC9B,OAAO,OAAOC,GAAQ,SAClB,CACE,GAAGD,EACH,gBAAiB,CAAE,IAAAC,EAAK,QAAAC,CAAQ,EAChC,MAAO,CACL,UAAWC,EAAM,MAAQ,MACzB,SAAU,CAAE,IAAK,GAAGF,CAAG,IAAIE,EAAM,MAAQ,KAAK,GAAI,QAAAD,CAAQ,CAC5D,CACF,EACAF,CACN,CAEA,IAAMI,MAAyB,wBAC7B,yBACA,CAIE,gBAAiB,CACf,KAAM,eACN,aAAc,CACZ,IAAK,2BACL,aAAc,aAChB,CACF,EAIA,SAAO,wBAAoB,uBAAwB,CAIjD,UAAW,CACT,MAAO,SAAM,YAAY,YAAa,CAAC,MAAO,KAAK,CAAC,EACpD,KAAM,aACN,aAAc,KAChB,EAIA,SAAU,CACR,KAAM,eACN,aAAc,CACZ,IAAK,+BACL,aAAc,aAChB,CACF,CACF,CAAC,EASD,oBAAqB,CACnB,KAAM,SACN,aAAc,CAAC,CACjB,CACF,EACA,CACE,gBAAiB,GAUjB,mBAAoBL,EACtB,CACF,EAMOM,GAAQD,GDtGA,SAARE,GAAyCC,EAA8B,CAC5EA,EAAc,eACZ,IACE,IAAI,GAAAC,QAAY,CACd,KAAM,yBACN,YAAa,+BACb,kBAAAC,GACA,aAAAC,GACA,gBAAiB,CACf,SAAU,gBACZ,EACA,gBAAiB,IACf,OAAO,6CAA6B,EAAE,KAAKC,GAAKA,EAAE,OAAO,CAC7D,CAAC,CACL,CACF,CErBA,IAAAC,GAAwB,UCAxB,IAAAC,GAAoC,SACpCC,GAAsB,SA6BtB,SAASC,GAAIC,EAAaC,EAAkBC,EAAc,CACxD,MAAO,CACL,UAAWA,EAAM,MAAQ,MACzB,SAAU,CAAE,IAAK,GAAGF,CAAG,IAAIE,EAAM,MAAQ,KAAK,GAAI,QAAAD,CAAQ,CAC5D,CACF,CAEA,SAASE,GAAgBC,EAA+B,CACtD,GAAM,CAAE,IAAAJ,EAAK,QAAAC,EAAS,IAAAC,CAAI,EAAIE,EAC9B,OAAO,OAAOJ,GAAQ,SAClB,CACE,GAAGI,EACH,iBAAkB,CAAE,IAAK,GAAGJ,CAAG,eAAgB,QAAAC,CAAQ,EACvD,cAAeF,GAAI,GAAGC,CAAG,eAAgBC,EAASC,CAAG,EACrD,cAAe,CAAE,IAAK,GAAGF,CAAG,gBAAiB,QAAAC,CAAQ,EACrD,WAAYF,GAAI,GAAGC,CAAG,gBAAiBC,EAASC,CAAG,CACrD,EACAE,CACN,CAEO,SAASC,GAAkBD,EAA+B,CAC/D,GAAM,CAAE,OAAAE,EAAQ,QAAAL,CAAQ,EAAIG,EAC5B,OAAOD,GACL,OAAOG,GAAW,UAAYA,IAAW,KACrC,CAAE,GAAGF,EAAM,OAAQD,GAAgB,CAAE,QAAAF,EAAS,GAAGK,CAAO,CAAC,CAAE,EAC3DF,CACN,CACF,CAEA,IAAMG,GAAc,CAACC,EAAcC,OACjC,wBAAoBD,EAAM,CAIxB,UAAW,CACT,MAAO,SAAM,YAAY,YAAa,CAAC,MAAO,KAAK,CAAC,EACpD,KAAM,aACN,aAAc,KAChB,EAIA,SAAU,CACR,KAAM,eACN,aAAc,CAAE,IAAKC,EAAY,aAAc,aAAc,CAC/D,CACF,CAAC,EAEGC,MAAsB,wBAAoB,sBAAuB,CASrE,aAAc,CAAE,KAAM,aAAc,EAIpC,iBAAkB,CAChB,KAAM,eACN,aAAc,CACZ,IAAK,kCACL,aAAc,aAChB,CACF,EAIA,cAAeH,GACb,0BACA,qCACF,EAIA,cAAe,CACb,KAAM,eACN,aAAc,CACZ,IAAK,mCACL,aAAc,aAChB,CACF,EAIA,WAAYA,GACV,uBACA,sCACF,CACF,CAAC,EAEKI,MAAmB,wBACvB,mBACA,CAME,iBAAkB,CAChB,KAAM,eACN,aAAc,CACZ,IAAK,6BACL,aAAc,aAChB,CACF,EAIA,cAAeJ,GACb,oBACA,gCACF,EAMA,cAAe,CACb,KAAM,eACN,aAAc,CACZ,IAAK,8BACL,aAAc,aAChB,CACF,EAIA,WAAYA,GACV,iBACA,iCACF,EAcA,oBAAqB,CACnB,KAAM,SACN,aAAc,CAAC,CACjB,EAQA,OAAQG,EACV,EACA,CACE,gBAAiB,GAUjB,mBAAoBL,EACtB,CACF,EAIOO,GAAQD,GD1MA,SAARE,GAAmCC,EAA8B,CACtEA,EAAc,eACZ,IACE,IAAI,GAAAC,QAAY,CACd,KAAM,mBACN,YAAa,uBACb,kBAAAC,GACA,aAAAC,GAEA,oBAAqB,CAAC,aAAa,EACnC,gBAAiB,CACf,SAAU,gBACZ,EACA,gBAAiB,IACf,OAAO,uCAAuB,EAAE,KAAKC,GAAKA,EAAE,OAAO,CACvD,CAAC,CACL,CACF,CENA,IAAMC,GAAiB,IAAI,IAAI,CAAC,mBAAoB,uBAAuB,CAAC,EAE5E,SAASC,GAAaC,EAAmB,CACvC,MAAO,QAASA,EAAMA,EAAI,IAAM,cAAeA,EAAMA,EAAI,UAAYA,EAAI,IAC3E,CAEO,SAASC,GAAcD,EAAmB,CAC/C,OAAOD,GAAaC,CAAG,EAAE,YAAY,EAAE,SAAS,SAAS,CAC3D,CAIO,SAASE,GAAcC,EAA2B,CACvD,IAAMC,EAAUD,EAAK,QAErB,OAAOC,GAAS,OAAS,yBACvB,CAACA,EAAQ,eAAe,QACxB,MAAM,QAAQD,EAAK,aAAa,GAChCA,EAAK,cAAc,OAAS,EAC1B,CAAE,GAAGA,EAAM,QAAS,CAAE,GAAGC,EAAS,cAAeD,EAAK,aAAc,CAAE,EACtEA,CACN,CAEO,SAASE,GACdC,EACAC,EACAC,EACA,CACA,IACG,CAACA,GAAeA,IAAgB,0BACjCP,GAAcK,CAAI,EAElB,MAAO,CACL,KAAM,wBACN,cAAeA,EACf,GAAIC,EAAQ,CAAE,uBAAwBA,CAAM,EAAI,CAAC,CACnD,EAEF,IACG,CAACC,GAAeA,IAAgB,qBACjCC,GAAmBH,CAAI,EAEvB,OAAOI,GAAmB,CACxB,OAAQ,mBACR,IAAKJ,EACL,SAAUC,EACV,SAAU,GACV,OAAQ,EACV,CAAC,CAGL,CAEe,SAARI,GAAqCC,EAA8B,CACxEA,EAAc,oBAAoB,6BAA8BV,EAAa,EAC7EU,EAAc,oBACZ,+BACCC,GACC,CAACP,EAAMC,EAAOC,IACZH,GAAkBC,EAAMC,EAAOC,CAAW,GAC1CK,EAAKP,EAAMC,EAAOC,CAAW,CACnC,EACAI,EAAc,oBACZ,iCACCC,GACC,CAACC,EAAaR,IACZR,GAAe,IAAIgB,CAAW,EAC1B,aACAD,EAAKC,EAAaR,CAAI,CAChC,CACF,CCvFA,IAAAS,GAA2B,SAC3BC,GAAsB,UACtBC,GAAyB,UACzBC,GAA2B,UCW3B,SAASC,EAASC,EAAkD,CAClE,OAAO,OAAOA,GAAU,UAAYA,IAAU,IAChD,CAEA,SAASC,GAAWD,EAA6C,CAC/D,IAAIE,EACJ,GAAIH,EAASC,CAAK,EAAG,CACnB,GAAM,CAAE,QAAAG,EAAS,MAAAC,EAAO,IAAAC,EAAK,aAAAC,CAAa,EAAIN,EAE5C,OAAOG,GAAY,UACnB,OAAOC,GAAU,UACjB,OAAOC,GAAQ,WAEfH,EAAS,CACP,QAAAC,EACA,MAAAC,EACA,IAAAC,EACA,aACE,OAAOC,GAAiB,SAAWA,EAAe,MACtD,EAEJ,CACA,OAAOJ,CACT,CAWA,SAASK,GACPC,EACAC,EACAC,EACA,CACA,IAAMC,EAAkBH,EAAK,gBAC7B,OACEG,IAAoB,QACpBA,IAAoBF,GACpB,CAACC,EAAY,IAAIC,CAAe,CAEpC,CAkBA,SAASC,GACPV,EACAW,EACA,CACA,OACEX,EAAO,eAAiB,QACxBW,IAAkB,QAClBA,EAAc,SAASX,EAAO,YAAY,CAE9C,CAIA,SAASY,GAAYN,EAA+B,CAElD,OADe,MAAM,QAAQA,EAAK,MAAM,EAAIA,EAAK,OAAS,CAAC,GAC7C,QAASO,GACrBhB,EAASgB,CAAK,GAAK,MAAM,QAAQA,EAAM,QAAQ,EAC3CA,EAAM,SAAS,OACZC,GACCjB,EAASiB,CAAO,GAAKA,EAAQ,OAAS,oBAC1C,EACA,CAAC,CACP,CACF,CAKO,SAASC,GACdC,EACAT,EAGAU,EACsB,CACtB,IAAMC,EAAmC,CAAC,EACpCV,EAAc,IAAI,IACtBW,GAASH,CAAK,EAAE,IAAIV,GAAST,EAASS,CAAI,EAAIA,EAAK,GAAK,MAAU,CACpE,EACA,QAAWA,KAAQU,EAAO,CACxB,GAAInB,EAASS,CAAI,GAAKA,EAAK,KAAOC,EAChC,QAAWO,KAAWF,GAAYN,CAAI,EAAG,CACvC,IAAMN,EAASD,GAAWe,EAAQ,cAAc,EAC5Cd,GAAUU,GAAaV,EAAQiB,CAAmB,GACpDC,EAAW,KAAK,CACd,IAAK,OAAOJ,EAAQ,IAAO,SAAWA,EAAQ,GAAK,cACnD,OAAAd,CACF,CAAC,CAEL,CAEF,GACEH,EAASS,CAAI,GACbA,EAAK,OAAS,mBACdD,GAAYC,EAAMC,EAAcC,CAAW,EAC3C,CACA,IAAMR,EAASD,GAAWO,EAAK,cAAc,EACzCN,GAAUU,GAAaV,EAAQiB,CAAmB,GACpDC,EAAW,KAAK,CACd,IAAK,OAAOZ,EAAK,IAAO,SAAWA,EAAK,GAAK,QAC7C,OAAAN,CACF,CAAC,CAEL,CACF,CACA,OAAOkB,CACT,CD3GI,IAAAE,EAAA,SA1BEC,MAAY,eAAW,EAAEC,IAAU,CACvC,UAAW,CACT,OAAQ,OACR,SAAU,WACV,cAAY,UAAMA,EAAM,QAAQ,QAAQ,KAAM,GAAI,EAClD,WAAY,aAAaA,EAAM,QAAQ,QAAQ,IAAI,GACnD,YAAa,aAAaA,EAAM,QAAQ,QAAQ,IAAI,GACpD,cAAe,OACf,OAAQ,EACV,CACF,EAAE,EAMIC,MAAY,aAAS,SAAmB,CAC5C,MAAAC,EACA,OAAAC,CACF,EAGG,CACD,GAAM,CAAE,QAAAC,CAAQ,EAAIL,GAAU,EACxBM,EAASH,EAAM,mBAAmBC,CAAM,EAC9C,OAAOE,KACL,OAAC,OACC,cAAY,uBACZ,UAAWD,EAAQ,UACnB,MAAO,CAAE,KAAMC,EAAO,KAAM,MAAOA,EAAO,KAAM,EAClD,EACE,IACN,CAAC,EAEKC,MAAqB,aAAS,SAA4B,CAC9D,MAAAJ,CACF,EAEG,CACD,GAAM,CAAE,MAAAK,CAAM,KAAI,eAAWL,CAAK,EAClC,SACE,mBACG,SAAAM,GAAoB,CAAC,GAAGD,CAAK,EAAGL,EAAM,GAAIA,EAAM,aAAa,EAAE,IAC9D,CAAC,CAAE,IAAAO,EAAK,OAAAN,CAAO,OACb,OAACF,GAAA,CAAqC,MAAOC,EAAO,OAAQC,GAA5C,eAAeM,CAAG,EAAkC,CAExE,EACF,CAEJ,CAAC,EAEMC,GAAQJ,GE7CX,IAAAK,GAAA,SAJEC,GAAQ,4CAEC,SAARC,GAAiCC,EAA8B,CACpEA,EAAc,2BAA2BF,GAAO,CAAC,CAAE,MAAAG,CAAM,OACvD,QAACC,GAAA,CAAyD,MAAOD,GAAzC,iCAAgD,CACzE,CACH,CChBO,IAAME,GAAc,kBCFpB,IAAMC,GAAU,Q/CsBvB,IAAqBC,GAArB,cAAmD,GAAAC,OAAO,CAA1D,kCACE,UAAOC,GACP,aAAUC,GAEV,QAAQC,EAA8B,CACpCC,GAAiBD,CAAa,EAC9BE,GAAoBF,CAAa,EACjCG,GAAYH,CAAa,EACzBI,GAAuBJ,CAAa,EACpCK,GAAgBL,CAAa,EAC7BM,GAAkBN,CAAa,EAC/BO,GAAwBP,CAAa,EACrCQ,GAAuBR,CAAa,EACpCS,GAAuBT,CAAa,EACpCU,GAAoBV,CAAa,EACjCA,EAAc,aAAa,IAAM,IAAIW,EAAmBX,CAAa,CAAC,EACtEA,EAAc,aAAa,IAAM,IAAIY,EAAYZ,CAAa,CAAC,CACjE,CAMA,UAAUA,EAA8B,CACtC,GAAI,IACE,0BAAsBA,EAAc,SAAS,GAC/CA,EAAc,UAAU,gBAAgB,CAAC,KAAK,EAAG,CAC/C,MAAO,oBACP,KAAMa,GACN,QAAUC,GAAkC,CAC1CA,EAAQ,QAAQ,kBAAmB,CAAC,CAAC,CACvC,CACF,CAAC,CAEL,OAASC,EAAG,CACV,QAAQ,KAAK,qDAAsDA,CAAC,CACtE,CACF,CACF",
|
|
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[\"@jbrowse/core/pluggableElementTypes/DisplayType\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/trackHeightConfigSchemaFields\"];", "module.exports = JBrowseExports[\"@jbrowse/display-kit/TrackHeightMixin\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/displayPhase\"];", "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 GraphTrackF from './GraphTrack/index'\nimport LaunchGraphGenomeViewF from './LaunchGraphGenomeView'\nimport LinearGraphDisplayF from './LinearGraphDisplay/index'\nimport MinigraphBubbleAdapterF from './MinigraphBubbleAdapter/index'\nimport RgfaTabixAdapterF from './RgfaTabixAdapter/index'\nimport GraphTrackDefaultsF from './graphTrackDefaults/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 GraphTrackF(pluginManager)\n LaunchGraphGenomeViewF(pluginManager)\n GraphHoverSyncF(pluginManager)\n RgfaTabixAdapterF(pluginManager)\n MinigraphBubbleAdapterF(pluginManager)\n GbzBaseSyntenyAdapterF(pluginManager)\n GraphAddTrackWorkflowF(pluginManager)\n GraphTrackDefaultsF(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 display's subgraph.\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 GraphTrack\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 SubgraphRegion {\n refName: string\n assemblyName: string\n start: number\n end: number\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 the pane's cutSubgraph.\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 { EngineRequest } from './GraphGenomeView/pipeline'\nimport type { LayoutResult } from './GraphGenomeView/types'\nimport type { RpcExecuteArgs } from '@jbrowse/core/rpc/RpcRegistry'\n\n// The payload is the pipeline's EngineRequest, the same request a standalone\n// page hands the engine. `sessionId` and `statusCallback` are the CALL's, not\n// the payload's: core's `EntriesDeclaringCallLevelFields` fails the build for a\n// registry entry that declares either. `execute` still receives both, through\n// `RpcExecuteArgs`'s intersection with `RpcCallContext`.\n\ndeclare module '@jbrowse/core/rpc/RpcRegistry' {\n interface RpcRegistry {\n GraphComputeLayout: {\n args: EngineRequest\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 './viewModel'\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 { getSession } from '@jbrowse/core/util'\nimport { types } from '@jbrowse/mobx-state-tree'\n\nimport { GraphPaneMixin, MAX_GRAPH_REGION_BP, formatSpanBp } from './model'\nimport {\n graphReferenceAssembly,\n offReferenceProblem,\n} from '../graphTrackConfig'\n\nimport type { SubgraphRegion } from '../GetSubgraph'\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\nimport type { FileLocation } from '@jbrowse/core/util/types'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\n\nexport default function stateModelFactory() {\n return types\n .compose(\n 'GraphGenomeView',\n BaseViewModel,\n GraphPaneMixin(),\n types.model({\n type: types.literal('GraphGenomeView'),\n // a whole GFA the view loads on attach, kept so the source round-trips\n // through a session snapshot\n gfaLocation: types.maybe(types.frozen<FileLocation>()),\n // the reference window the file or the cut was stated beside\n loadedRegion: types.maybe(types.frozen<SubgraphRegion>()),\n // A session track the view cuts `loadedRegion` from once, as 4.0\n // sessions and the docs' figure specs state it. Following the linear\n // view is the track's job (LinearGraphDisplay).\n loadedTrackId: types.optional(types.string, ''),\n subgraphContext: types.optional(types.number, 1),\n subgraphHaplotypes: types.maybe(types.frozen<string[]>()),\n maxRegionBp: types.optional(types.number, MAX_GRAPH_REGION_BP),\n }),\n )\n .views(self => ({\n get rpcSessionId() {\n return self.id\n },\n // a view is not its own containing view\n get paneWidth() {\n return self.width\n },\n get sourceTrack(): AnyConfigurationModel | undefined {\n return self.loadedTrackId\n ? getSession(self).tracks.find(t => t.trackId === self.loadedTrackId)\n : undefined\n },\n get canRetryLoad() {\n return (\n self.gfaLocation !== undefined ||\n (self.loadedTrackId !== '' && self.loadedRegion !== undefined)\n )\n },\n }))\n .views(self => ({\n get hasPendingSource() {\n return self.canRetryLoad && !self.graph && !self.loadCanceled\n },\n menuItems() {\n return self.launchMenuItems()\n },\n }))\n .actions(self => {\n const clearPane = self.clearGraph\n return {\n // the stated source goes too, or a reloaded session would load the\n // dismissed graph again\n clearGraph() {\n clearPane()\n self.gfaLocation = undefined\n self.loadedRegion = undefined\n self.loadedTrackId = ''\n },\n load() {\n const region = self.loadedRegion\n if (self.gfaLocation) {\n return self.loadGFAFromLocation(self.gfaLocation, region)\n }\n if (!self.loadedTrackId || !region) {\n return\n }\n const track = self.sourceTrack\n const span = region.end - region.start\n const problem = track\n ? offReferenceProblem(\n graphReferenceAssembly(track),\n region.assemblyName,\n )\n : undefined\n if (!track) {\n self.setError(\n new Error(\n `The track this graph was cut from, \"${self.loadedTrackId}\", is not in this session`,\n ),\n )\n } else if (problem) {\n self.setError(new Error(problem))\n } else if (span > self.maxRegionBp) {\n self.setError(\n new Error(\n `Region too large (${formatSpanBp(span)}) \u2014 zoom in to view graph (max ${formatSpanBp(self.maxRegionBp)})`,\n ),\n )\n } else {\n return self.cutSubgraph(readConfObject(track, 'adapter'), region, {\n hops: self.subgraphContext,\n haplotypes: self.subgraphHaplotypes,\n })\n }\n return undefined\n },\n }\n })\n .actions(self => ({\n retryLoad() {\n void self.load()\n },\n // Here rather than when the rendering backend starts: the canvas only\n // mounts once there is a graph, so a view whose graph must be fetched\n // would never fetch it.\n afterAttach() {\n if (!self.graph) {\n void self.load()\n }\n },\n }))\n}\n\nexport type GraphGenomeViewModel = Instance<\n ReturnType<typeof stateModelFactory>\n>\n", "import { readConfObject } from '@jbrowse/core/configuration'\nimport { pushLaunchViewMenuItem } from '@jbrowse/core/ui'\nimport {\n getContainingTrack,\n getContainingView,\n getRpcSessionId,\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 } from './anchoredNodes'\nimport { BUBBLE_SPREAD_VALUES } 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 {\n COLOR_SCHEMES,\n COLOR_SCHEME_VALUES,\n resolveColorScheme,\n} 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 { hostFrame, isLinearHost } from './host'\nimport { layoutLabels } from './labelLayout'\nimport { ROW_HEIGHT_PX } from './layout/rowSpacing'\nimport { walkRowsExtent } from './layout/walkRowLayout'\nimport { walkRows } from './layout/walkRows'\nimport {\n LAYOUT_MODES,\n LAYOUT_MODE_VALUES,\n layoutModeByValue,\n modeUsesLayoutEngine,\n} from './layoutModes'\nimport { NODE_WIDTH_VALUES, nodeInk } from './nodeWidths'\nimport { anchorFromPaths } from './pathAnchoring'\nimport { pathColorsLegible, pathLegend } from './pathColors'\nimport {\n FIT_PADDING,\n clampZoom,\n drawingBounds,\n engineKey,\n fitTransform,\n fittedTranslateY,\n forceLayout,\n loadGraph,\n} from './pipeline'\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 { tubeMapPicture } from './tubeMap/draw'\nimport { tubeMapFrame, tubeMapNodeAt } from './tubeMap/frame'\nimport {\n axisScaleOf,\n contains,\n padded,\n viewportOf,\n zoomAbout,\n} from './viewport'\nimport { walkHighlight } from './walkHighlight'\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'\n\nimport type { BubbleSpread } from './bubbleSpreads'\nimport type { ColorScheme, ResolvedColorScheme } from './colorSchemes'\nimport type { GeneModel } from './genes/geneFeatures'\nimport type { LinearHost } from './host'\nimport type { LayoutModeValue } from './layoutModes'\nimport type { NodeWidth } from './nodeWidths'\nimport type { Bounds, EngineRequest } from './pipeline'\nimport type { RenderBatch, Renderer } from './renderer/types'\nimport type { RepeatArray } from './repeats/repeatFeatures'\nimport type { Graph, GraphNode, LayoutResult } from './types'\nimport type { SubgraphCutOptions, SubgraphRegion } 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 { AnyConfigurationModel } from '@jbrowse/core/configuration'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Feature } from '@jbrowse/core/util'\nimport type { FileLocation } from '@jbrowse/core/util/types'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\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}\nconst HOVER_BRIGHTEN = 1.4\nconst SELECT_BRIGHTEN = 1.6\nconst VIEWPORT_DEBOUNCE_MS = 150\nconst VIEWPORT_PANES_BUILT = 1\n\n// What the canvas draws under the tube map, whose ink is all TubeMapOverlay's\nconst EMPTY_BATCH: RenderBatch = {\n nodeStrokes: [],\n nodeStrokeRuns: new Map(),\n arrows: [],\n arrowRuns: new Map(),\n edgeCurves: [],\n edgeCurveRuns: new Map(),\n}\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\ntype ViewportOwner = 'fit' | 'user' | 'host'\n\nfunction geometryPainted(model: {\n lastGeometryStrokeCount?: number\n viewportRebuildPending: boolean\n geometryViewportDirty: number\n viewportDirty: number\n paintedGeometryVersion: number\n geometryVersion: number\n}) {\n return (\n model.lastGeometryStrokeCount !== undefined &&\n !model.viewportRebuildPending &&\n model.geometryViewportDirty === model.viewportDirty &&\n model.paintedGeometryVersion === model.geometryVersion\n )\n}\n\n// What the pane shows, in layout units.\nfunction paneViewportOf(model: {\n translateX: number\n translateY: number\n paneWidth: number\n axisScale: AxisScale\n canvasHeight: number\n}): Bounds {\n return viewportOf(model, model.axisScale, model.paneWidth, model.canvasHeight)\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 function GraphPaneMixin() {\n return types\n .compose(\n 'GraphPane',\n RenderLifecycleMixin(),\n types.model({\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 // unset takes the host's default: force in a view of its own, the\n // display config's in a track\n layoutMode: types.maybe(types.enumeration(LAYOUT_MODE_VALUES)),\n colorScheme: types.maybe(types.enumeration(COLOR_SCHEME_VALUES)),\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 reference span the reference-position ramp runs over, for a graph\n // with no region of its own to span it\n colorDomain: types.maybe(\n types.frozen<{ start: number; end: number }>(),\n ),\n // the linear view a graph of its own is paired with for the hover sync\n connectedViewId: types.maybe(types.string),\n }),\n )\n .volatile(() => ({\n graph: undefined as Graph | undefined,\n // the reference window the graph on screen was cut for, set with it\n graphRegion: undefined as SubgraphRegion | undefined,\n layoutResult: undefined as LayoutResult | undefined,\n // what the legends in the pane's top-right corner measure, so no label\n // is placed under them\n legendSize: { width: 0, height: 0 },\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 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 paintedGeometryVersion: -1,\n viewportRebuildPending: false,\n geometryViewportDirty: -1,\n }))\n .views(self => ({\n get defaultLayoutMode(): LayoutModeValue {\n return 'force'\n },\n get defaultColorScheme(): ColorScheme {\n return 'auto'\n },\n // a source declared but not yet loaded\n get hasPendingSource() {\n return false\n },\n get canRetryLoad() {\n return false\n },\n // the track the graph is cut from: the one the pane is a display of\n get sourceTrack(): AnyConfigurationModel | undefined {\n try {\n return getContainingTrack(self).configuration\n } catch {\n return undefined\n }\n },\n }))\n .views(self => ({\n get chosenLayoutMode() {\n return self.layoutMode ?? self.defaultLayoutMode\n },\n get chosenColorScheme() {\n return self.colorScheme ?? self.defaultColorScheme\n },\n }))\n .views(self => ({\n get paneWidth() {\n return getContainingView(self).width\n },\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 get geometryPainted() {\n return geometryPainted(self)\n },\n // The app-wide readiness contract (AppReadyMarker, @jbrowse/capture)\n // reads this: a declared source still fetching, or a graph whose\n // settled geometry is not on the canvas yet.\n get showLoading() {\n return (\n self.error === undefined &&\n (self.isLoading ||\n self.hasPendingSource ||\n (self.graph !== undefined && !geometryPainted(self)))\n )\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.chosenLayoutMode, 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 // The tube map colours its tubes whether or not paths are drawn on the\n // nodes, so under it the key follows the tubes.\n get pathLegend() {\n const paths = self.graph?.paths\n const colouring = self.drawPaths || self.layoutResult?.tubeMap\n return colouring && 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 // Undefined for a graph with neither, where the ramp spans the drawn\n // extent (computeReferenceRamp)\n get rampDomain() {\n return self.colorDomain ?? self.graphRegion\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 resolveColorScheme(self.chosenColorScheme, self.graph)\n },\n get nodePositions() {\n return self.layoutResult?.nodePositions\n },\n get labelsNodeSizes() {\n return !self.layoutResult?.tubeMap\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 get sourceAdapter() {\n const track = self.sourceTrack\n return track\n ? (readConfObject(track, 'adapter') as Record<string, unknown>)\n : undefined\n },\n get sourceTrackId() {\n const track = self.sourceTrack\n return track ? (readConfObject(track, 'trackId') as string) : undefined\n },\n get assemblyTrackChoices() {\n const region = self.graphRegion\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 get nodeInk(): NodeInk {\n return nodeInk(\n self.graph,\n self.nodeById,\n self.contigThickness,\n self.nodeWidth,\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 &&\n self.chosenLayoutMode !== 'walkrows' &&\n !self.layoutResult?.tubeMap\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.chosenLayoutMode !== '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.graphRegion)\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.chosenLayoutMode === 'variants' ? self.bubbles : []\n return bubbles.map(bubble => ({\n bubble,\n ...classifyBubble(bubble, self.repeatArrays),\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.chosenLayoutMode !== 'variants' &&\n self.chosenLayoutMode !== 'walkrows' &&\n !self.layoutResult?.tubeMap &&\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.chosenLayoutMode === 'variants' ||\n self.chosenLayoutMode === 'walkrows' ||\n self.layoutResult?.tubeMap ||\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 self.repeatArrays,\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' &&\n self.graph &&\n !self.layoutResult?.tubeMap\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.graphRegion\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 const layout = self.layoutResult\n const bars = this.walkRowBars\n return layout\n ? drawingBounds(layout, {\n region: self.popStack.length === 0 ? self.graphRegion : undefined,\n extent: bars && layout.extent ? walkRowsExtent(bars) : undefined,\n })\n : undefined\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 const bounds = this.layoutBounds\n const usableWidth = self.paneWidth - 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.chosenLayoutMode === '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.graphRegion\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.graphRegion),\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.graphRegion\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.graphRegion\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 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 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, graphRegion: 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 overlayLabels() {\n return layoutLabels(self)\n },\n get hostFrame() {\n const { host, graphRegion } = self\n return self.hostPlacesX && host && graphRegion\n ? hostFrame(host, graphRegion)\n : undefined\n },\n get tubeMapPicture() {\n const drawing = self.layoutResult?.tubeMap\n return drawing ? tubeMapPicture(drawing.layout) : undefined\n },\n get tubeMapFrame() {\n const drawing = self.layoutResult?.tubeMap\n return drawing\n ? tubeMapFrame(drawing, {\n scaleX: self.scaleX,\n translateX: self.translateX,\n scaleY: self.scaleY,\n translateY: self.translateY,\n usableHeight: self.canvasHeight - FIT_PADDING * 2,\n })\n : undefined\n },\n }))\n .views(self => ({\n tubeMapNodeAt(sx: number, sy: number) {\n const drawing = self.layoutResult?.tubeMap\n const frame = self.tubeMapFrame\n return drawing && frame ? tubeMapNodeAt(drawing, frame, sx, sy) : null\n },\n }))\n .actions(self => ({\n setError(error: unknown) {\n self.error = error\n self.isLoading = false\n self.statusMessage = ''\n },\n finishLoading() {\n self.isLoading = false\n self.statusMessage = ''\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.geometryViewportDirty = self.viewportDirty\n self.builtViewport = built\n self.geometryVersion++\n },\n markPainted(version: number) {\n self.paintedGeometryVersion = version\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 },\n setWalkRowSamples(samples: string[] | undefined) {\n self.walkRowSamples = samples\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 setLegendSize(size: { width: number; height: number }) {\n self.legendSize = size\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.graphRegion\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 t = zoomAbout(self, factor, centerX, centerY, self.pixelRows)\n self.scale = t.scale\n self.translateX = t.translateX\n self.translateY = t.translateY\n },\n setViewportDirty() {\n self.viewportRebuildPending = false\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 const bounds = self.layoutBounds\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 beats persisting a negative scale into\n // the session snapshot.\n const fit = bounds\n ? fitTransform(\n bounds,\n self.paneWidth,\n self.canvasHeight,\n self.pixelRows,\n )\n : undefined\n if (fit) {\n self.scale = fit.scale\n self.translateX = fit.translateX\n self.translateY = fit.translateY\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.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 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.viewportRebuildPending = true\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 function callEngine(request: EngineRequest) {\n const { rpcManager } = getSession(self)\n return rpcManager.call(getRpcSessionId(self), 'GraphComputeLayout', {\n ...request,\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 return engineKey(graph, {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n bubbleSpread: self.bubbleSpread,\n })\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.chosenLayoutMode).run(\n graph,\n self.graphRegion,\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 const oriented = (yield forceLayout(\n graph,\n {\n quality: self.layoutQuality,\n linearLayout: self.linearLayout,\n bubbleSpread: self.bubbleSpread,\n },\n callEngine,\n )) as { result: LayoutResult; duration: number }\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.result)\n return oriented\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 region: SubgraphRegion | undefined,\n keepSelection = false,\n ) {\n self.setStatusMessage('Parsing GFA')\n const graph = loadGraph(text, name, {\n referencePath: self.referencePath || region?.assemblyName,\n maxNodes: self.maxGraphNodes,\n })\n const selected = keepSelection ? self.selectedNode : null\n self.graph = graph\n self.graphRegion = region\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 // 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 (adapterConfig.type !== 'RgfaTabixAdapter' || prefix === undefined) {\n return\n }\n try {\n const features = (yield getSession(self).rpcManager.call(\n getRpcSessionId(self),\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(\n getRpcSessionId(self),\n 'CoreGetFeatures',\n {\n adapterConfig: readConfObject(config, 'adapter'),\n regions: [region],\n },\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 // 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. `region` is the window a\n // declared file was stated beside, which the parse anchors on.\n function* loadWholeGFA(\n name: string,\n source: string | ((signal: AbortSignal) => Promise<string>),\n region?: SubgraphRegion,\n ) {\n const { isLive, signal } = beginLoad()\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, region)\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.finishLoading()\n }\n }\n return isLive()\n }\n\n function abortLoad() {\n loadController?.abort()\n loadController = undefined\n liveLoad++\n liveRequest++\n }\n\n return {\n // Back to the import form: drop the graph and everything derived from\n // it. Any load in flight ends here too, or it would land its graph\n // afterwards.\n clearGraph() {\n abortLoad()\n self.graph = undefined\n self.graphRegion = undefined\n self.layoutResult = 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 // The user's stop, leaving whatever is drawn under it\n stopLoad() {\n abortLoad()\n self.isLoading = false\n self.statusMessage = ''\n self.loadCanceled = true\n },\n beforeDestroy() {\n abortLoad()\n },\n loadGFA: flow(function* (text: string, name = 'Imported GFA') {\n yield* loadWholeGFA(name, text)\n }),\n loadGFAFromLocation: flow(function* (\n location: FileLocation,\n region?: SubgraphRegion,\n ) {\n self.setStatusMessage('Fetching GFA')\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 region,\n )\n if (region && live && self.graph) {\n yield* loadRepeats(region, () => self.graphRegion === region)\n }\n }),\n // One cut of a region, laid out with the annotations over it. Whether\n // the region may be cut is the caller's call; overlapping cuts are\n // ordered by liveLoad, so the latest one lands.\n cutSubgraph: flow(function* (\n adapterConfig: Record<string, unknown>,\n region: SubgraphRegion,\n opts: SubgraphCutOptions = {},\n ) {\n const { isLive, signal } = beginLoad()\n self.isLoading = true\n self.error = undefined\n self.setStatusMessage('Fetching subgraph')\n try {\n const fetchStart = performance.now()\n const gfaText = (yield getSession(self).rpcManager.call(\n getRpcSessionId(self),\n 'GetSubgraph',\n { adapterConfig, region, opts, 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 yield* parseAndLayout(gfaText, locLabel(region), region, true)\n if (!isLive()) {\n return\n }\n // Independent remote reads, each landing as it arrives. A coarse\n // cut reads no bubble index: its nodes are the bubbles, and the\n // index over its window can run to a chromosome's rows.\n self.setStatusMessage('Reading annotations')\n yield Promise.all([\n opts.tier === 'coarse'\n ? undefined\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.cutSubgraph]', e)\n self.error = e\n }\n } finally {\n if (isLive()) {\n self.finishLoading()\n }\n }\n }),\n // Re-read one annotation track alone, for a track change. The graph,\n // its 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.graphRegion\n const trackId = self.repeatTrack?.trackId\n if (region) {\n yield* loadRepeats(\n region,\n () =>\n self.graphRegion === region &&\n self.repeatTrack?.trackId === trackId,\n )\n }\n }),\n reloadGenes: flow(function* () {\n const region = self.graphRegion\n const trackId = self.geneTrack?.trackId\n if (region) {\n yield* loadGenes(\n region,\n () =>\n self.graphRegion === 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.chosenLayoutMode,\n label: graph.name,\n indexBubbles: self.indexBubbles,\n },\n ]\n self.indexBubbles = undefined\n const label = `${BUBBLE_KIND_NAMES[classifyBubble(bubble, self.repeatArrays).kind]} at ${bubble.refName}:${bubble.start.toLocaleString()}`\n self.graph = { ...sub, name: label }\n if (self.chosenLayoutMode === '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.finishLoading()\n }\n } catch (e) {\n if (isLive()) {\n self.error = e\n self.finishLoading()\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.finishLoading()\n }\n } catch (e) {\n self.error = e\n self.finishLoading()\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.finishLoading()\n }\n } catch (e) {\n console.error('[GraphGenomeView.recomputeLayout]', e)\n self.error = e\n self.finishLoading()\n }\n }),\n }\n })\n .actions(self => ({\n startRenderingBackend(backend: Renderer) {\n if (!self.autorunsInstalled) {\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.graphRegion\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 = paneViewportOf(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.paneWidth, self.canvasHeight)\n const nodeById = self.nodeById\n if (self.layoutResult?.tubeMap) {\n dependOn(self.viewportDirty)\n b.uploadGeometry(EMPTY_BATCH)\n self.setGeometryMetrics(0, 0, {\n scale: untracked(() => self.scale),\n bounds: untracked(() => paneViewportOf(self)),\n })\n return true\n }\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(paneViewportOf(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 self.markPainted(untracked(() => self.geometryVersion))\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 cancelLoad() {\n if (self.canCancelLoad) {\n self.stopLoad()\n self.graph = undefined\n }\n },\n // loads the source again, for a host that has one\n retryLoad() {},\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 },\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.graphRegion?.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: onReference ? self.sourceTrackId : 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.sourceTrackId,\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 graphMenuItems(): MenuItem[] {\n const walks = self.walkChoices\n return [\n {\n label: 'Layout',\n subMenu: LAYOUT_MODES.map(mode => ({\n type: 'radio' as const,\n label: mode.label,\n checked: self.chosenLayoutMode === mode.value,\n disabled: self.graph ? !mode.available(self.graph) : false,\n onClick: () => {\n void self.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: self.chosenColorScheme === scheme.value,\n onClick: () => {\n self.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: self.highlightedPath === '',\n onClick: () => {\n self.setHighlightedPath('')\n },\n },\n ...walks.map(walk => ({\n type: 'radio' as const,\n label: walk.label,\n checked: self.highlightedPath === walk.name,\n onClick: () => {\n self.setHighlightedPath(walk.name)\n },\n })),\n ],\n },\n ]\n : []),\n ...(self.chosenLayoutMode === 'walkrows' &&\n self.repeatChoices.length > 0\n ? [\n {\n label: 'Repeat',\n subMenu: [\n {\n type: 'radio' as const,\n label: 'Whole window',\n checked: self.repeatKey === '',\n onClick: () => {\n self.setRepeatKey('')\n },\n },\n ...self.repeatChoices.map(({ key, name, unit }) => ({\n type: 'radio' as const,\n label: `${name} \u00B7 ${unit.toLocaleString()} bp unit`,\n checked: self.repeatKey === key,\n onClick: () => {\n self.setRepeatKey(key)\n },\n })),\n ],\n },\n ]\n : []),\n ...(self.hostPlacesX\n ? []\n : [\n {\n label: 'Zoom in',\n onClick: () => {\n self.zoom(1.5, self.paneWidth / 2, self.canvasHeight / 2)\n },\n },\n {\n label: 'Zoom out',\n onClick: () => {\n self.zoom(\n 1 / 1.5,\n self.paneWidth / 2,\n self.canvasHeight / 2,\n )\n },\n },\n {\n label: 'Zoom to fit',\n onClick: () => {\n self.zoomToFit()\n },\n },\n ]),\n {\n type: 'checkbox',\n label: 'Mark bubbles',\n checked: self.showBubbles,\n onClick: () => {\n self.setShowBubbles(!self.showBubbles)\n },\n },\n {\n type: 'checkbox',\n label: 'Show deletion edges',\n checked: self.showDeletionEdges,\n onClick: () => {\n self.setShowDeletionEdges(!self.showDeletionEdges)\n },\n },\n {\n type: 'checkbox',\n label: 'Genes on the backbone',\n checked: self.showGenes,\n onClick: () => {\n self.setShowGenes(!self.showGenes)\n },\n },\n ]\n },\n launchMenuItems(): 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 GraphPaneModel = Instance<ReturnType<typeof GraphPaneMixin>>\n", "import { bubbleSegmentIds, classifyBubble, formatBp } from './classifyBubble'\nimport { isBackbone } from '../anchoredNodes'\nimport { svgPath } from '../util/geometry'\n\nimport type { BubbleKind, RepeatSpan } 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 name goes, in layout units: over the middle of its nodes, at the\n // height of the highest\n labelAt: 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 repeats: readonly RepeatSpan[] = [],\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 minX = Infinity\n let maxX = -Infinity\n let minY = Infinity\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 minX = Math.min(minX, p.x)\n maxX = Math.max(maxX, p.x)\n minY = Math.min(minY, p.y)\n }\n }\n if (nodeIds.length === 0) {\n continue\n }\n const labelAt = { x: (minX + maxX) / 2, y: minY }\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 : labelAt\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, repeats),\n path: parts.join(''),\n labelAt,\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", "import type { SubgraphRegion } from '../GetSubgraph'\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 // whether the view's body is mounted, which an off-screen view's is not\n effectiveBodyMounted?: boolean\n hasVisibleContent?: boolean\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\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(\n window: HostWindow,\n capBp: number,\n margins = true,\n): SubgraphRegion {\n const visible = window.end - window.start\n const margin = margins\n ? Math.max(0, Math.min(window.span, (capBp - visible) / 2))\n : 0\n let start = Math.max(window.regionStart, Math.floor(window.start - margin))\n let end = Math.min(window.regionEnd, Math.ceil(window.end + margin))\n // Rounding outward can put a cut a base or two past the cap, which the load\n // refuses whole; trim it back rather than lose the cut.\n const over = end - start - capBp\n if (over > 0) {\n start += Math.floor(over / 2)\n end -= Math.ceil(over / 2)\n }\n return {\n refName: window.refName,\n assemblyName: window.assemblyName,\n start,\n end,\n }\n}\n\n// With margins, a cut holds while the window is inside it; without, only the\n// window itself does, since the drawing is the window.\nexport function cutHolds(\n cut: SubgraphRegion | undefined,\n window: HostWindow,\n margins = true,\n) {\n if (\n cut?.refName !== window.refName ||\n cut.assemblyName !== window.assemblyName\n ) {\n return false\n }\n // to the base, since a cut trimmed to its cap can end a base inside the window\n if (margins) {\n return cut.start <= window.start + 1 && window.end <= cut.end + 1\n }\n const exact = hostCut(window, Infinity, false)\n return (\n Math.abs(cut.start - exact.start) < 1 && Math.abs(cut.end - exact.end) < 1\n )\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 { 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 { isBackbone } from './anchoredNodes'\nimport { spreadFor } from './bubbleSpreads'\nimport { convertGFAToGraph } from './gfa/gfaConverter'\nimport { drawnNodeLength, layoutScaling } from './layout/drawnScale'\nimport { mergeRuns, splitRuns } from './layout/mergeRuns'\nimport { orientToReference } from './layout/orientToReference'\nimport { seededNodes } from './layout/referenceSeeds'\nimport { anchorGraph } from './pathAnchoring'\nimport { parseGFA } from '../gfa-core/index'\n\nimport type { BubbleSpread } from './bubbleSpreads'\nimport type { LayoutScaling } from './layout/drawnScale'\nimport type { LayoutNode } from './layout/referenceSeeds'\nimport type { Graph, LayoutResult, NodeSegment } from './types'\n\n// The graph a view draws, from GFA text to layouts, with no host in it: the\n// GraphGenomeView model and a standalone page both drive this.\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\n// A general GFA states its coordinates only in its P/W lines, so the walk that\n// recovers them happens before anything reads `stable`; otherwise the anchored\n// layouts see an unanchored graph and hand off to force. `maxNodes` is checked\n// here because it is upstream of everything that scales with the node count.\nexport function loadGraph(\n text: string,\n name: string,\n opts: { referencePath?: string; maxNodes?: number } = {},\n) {\n const graph = anchorGraph(\n convertGFAToGraph(parseGFA(text), name),\n opts.referencePath,\n )\n if (graph.nodes.length === 0) {\n throw new Error(`No graph segments in ${name}`)\n }\n if (opts.maxNodes !== undefined && graph.nodes.length > opts.maxNodes) {\n throw new Error(\n `Graph too large to draw: ${graph.nodes.length.toLocaleString()} nodes (limit ${opts.maxNodes.toLocaleString()}). Zoom in to a smaller region, or raise maxGraphNodes on this view.`,\n )\n }\n return graph\n}\n\n// What the Bandage engine is handed. A node may carry an `x`/`y` seed, which\n// the engine reads as where its chain starts (referenceSeeds.ts).\nexport interface EngineRequest {\n graph: { nodes: LayoutNode[]; edges: Graph['edges'] }\n options: Record<string, unknown>\n}\n\nexport type LayoutEngine = (\n request: EngineRequest,\n) => Promise<{ result: LayoutResult; duration: number }>\n\nexport interface EngineSettings {\n quality: number\n linearLayout: boolean\n bubbleSpread: BubbleSpread\n}\n\n// `scaling.nodes` rather than the graph's own: under a compressing drawn-length\n// law a node's `length` crosses this boundary as a drawn length, which is all\n// the engine ever reads it as. An anchored graph also carries a seed per node,\n// the backbone along x, and asks the engine not to rotate components: FMMM\n// then keeps the reference's coarse shape instead of curling it into a C\n// (docs/layout-experiments.md, experiment 2).\nexport function engineRequest(\n graph: Graph,\n scaling: LayoutScaling,\n settings: EngineSettings,\n): EngineRequest {\n const anchored = graph.nodes.some(isBackbone)\n return {\n graph: {\n nodes: anchored ? seededNodes(graph, scaling) : scaling.nodes,\n edges: graph.edges,\n },\n options: {\n quality: settings.quality,\n linearLayout: settings.linearLayout,\n ...scaling.opts,\n ...(anchored ? { rotateComponents: false } : {}),\n },\n }\n}\n\n// What a force layout of this graph is a function of, as a cache key: the\n// settings `engineRequest` reads, whether it seeds, and the reference path the\n// seeds follow.\nexport function engineKey(graph: Graph, settings: EngineSettings) {\n const anchored = graph.nodes.some(isBackbone)\n return `${settings.quality}|${settings.linearLayout}|${settings.bubbleSpread}|${anchored}|${graph.referencePath ?? ''}`\n}\n\n// The engine lays out the runs, not the nodes: a base-level cut is thousands\n// of nodes in unbranching chains, and one chain per run is the same drawing at\n// a third of the time. Members take their share of the run's polyline by drawn\n// length, so the picture is per node again before anything else sees it, and\n// it is turned so the reference reads left to right.\nexport async function forceLayout(\n graph: Graph,\n settings: EngineSettings,\n engine: LayoutEngine,\n) {\n const spread = spreadFor(settings.bubbleSpread)\n const merged = mergeRuns(graph)\n const { result, duration } = await engine(\n engineRequest(merged.graph, layoutScaling(merged.graph, spread), settings),\n )\n const scaling = layoutScaling(graph, spread)\n const drawn = new Map(\n scaling.nodes.map(n => [n.id, drawnNodeLength(scaling.opts, n.length)]),\n )\n const positions = splitRuns(\n result.nodePositions,\n merged.runs,\n id => drawn.get(id) ?? 0,\n )\n return {\n result: {\n ...result,\n nodePositions: graph.nodes.some(isBackbone)\n ? orientToReference(graph, positions)\n : positions,\n },\n duration,\n }\n}\n\nexport function layoutExtent(\n nodePositions: Record<string, NodeSegment[]>,\n): Bounds {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const segments of Object.values(nodePositions)) {\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 return { minX, minY, maxX, maxY }\n}\n\n// Gap between the drawing and the edge of the pane, on all four sides.\nexport const FIT_PADDING = 40\n\n// The floor keeps a scale positive and finite, and has to clear the smallest\n// scale a real layout asks for: fitting a chromosome-scale rGFA (250 Mbp) into\n// ~720 px needs ~3e-6.\nconst MIN_ZOOM = 1e-6\nconst MAX_ZOOM = 100\n\nexport function clampZoom(zoom: number) {\n return Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom))\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, and centring an\n// overflow would push the top row, the reference backbone, off the top.\nexport function 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\n// The transform that fits `bounds` in the pane, or undefined when there is\n// nothing to fit or nothing to fit into. Each axis constrains the scale only\n// when it has extent, since an anchored window holding only backbone puts every\n// node on row 0. A row layout (`pixelRows`) fits on x alone and keeps y in\n// screen px; an isotropic one fits on whichever axis binds.\nexport function fitTransform(\n bounds: { minX: number; minY: number; w: number; h: number },\n paneWidth: number,\n paneHeight: number,\n pixelRows: boolean,\n) {\n const usableWidth = paneWidth - FIT_PADDING * 2\n const usableHeight = paneHeight - FIT_PADDING * 2\n if (\n usableWidth <= 0 ||\n usableHeight <= 0 ||\n !(bounds.w > 0 || (!pixelRows && bounds.h > 0))\n ) {\n return undefined\n }\n const fitX = bounds.w > 0 ? usableWidth / bounds.w : Infinity\n const fitY = bounds.h > 0 ? usableHeight / bounds.h : Infinity\n const scale = clampZoom(pixelRows ? fitX : Math.min(fitX, fitY))\n return {\n scale,\n translateX:\n FIT_PADDING - bounds.minX * scale + (usableWidth - bounds.w * scale) / 2,\n translateY: fittedTranslateY(bounds, usableHeight, pixelRows ? 1 : scale),\n }\n}\n\n// Extent of the drawing in layout units, which the fit and the pane height\n// read. On a reference-bp layout x is the cut window rather than how far the\n// drawing reaches: an allele anchored far outside it is a fact about the\n// graph, not a reason to draw the window at 6% of the frame. `extent` stands\n// in for the layout's own when an overlay's rows reach further (walk rows).\nexport function drawingBounds(\n layout: LayoutResult,\n opts: {\n region?: { start: number; end: number }\n extent?: LayoutResult['extent']\n } = {},\n) {\n let { minX, minY, maxX, maxY } = layoutExtent(layout.nodePositions)\n const { region } = opts\n if (layout.referenceAxis && region && region.end > region.start) {\n minX = region.start\n maxX = region.end\n }\n const extent = opts.extent ?? layout.extent\n if (extent) {\n minX = Math.min(minX, extent.minX ?? minX)\n minY = Math.min(minY, extent.minY ?? minY)\n maxX = Math.max(maxX, extent.maxX ?? maxX)\n maxY = Math.max(maxY, extent.maxY ?? maxY)\n }\n return { minX, minY, w: maxX - minX, h: maxY - minY }\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 type { TubeMapColumn } from '../layout/tubeMapLayout'\n\n// Tube x to screen x under the reference axis. A column covers the screen span\n// of the reference bp its backbone node covers, which puts the tubes under the\n// linear view's other tracks. But the tube map changes lanes BETWEEN columns,\n// and adjacent reference nodes abut in bp, so the curves would have no width\n// at all. At each boundary the gap the tube map drew (its curves, and any\n// columns of inserted sequence, which cover no reference) gets its tube px\n// back, up to MAX_GAP_PX, where there is room: first from reference no\n// column covers, then from up to half of each neighbouring column. Zoomed in that is a\n// few px off a wide node; zoomed out the columns are thin, the gap gets what\n// they can spare and the curves steepen.\n//\n// Piecewise linear, so a curve's bezier, which lies inside one gap, maps\n// exactly by mapping its control points.\n\nexport interface Knot {\n tx: number\n sx: number\n}\n\n// The most screen a boundary claims. The tube map spaces its columns to keep\n// the lane changes shallow, which with many haplotypes is hundreds of px a\n// gap; on the reference axis the curves steepen instead.\nconst MAX_GAP_PX = 24\n\nconst isReal = (c: TubeMapColumn) => c.bp1 > c.bp0\n\nexport function referenceKnots(\n columns: readonly TubeMapColumn[],\n bpToScreen: (bp: number) => number,\n): Knot[] {\n const real = columns.filter(isReal)\n if (real.length === 0) {\n const first = columns[0]\n return first ? [{ tx: first.x0, sx: bpToScreen(first.bp0) }] : []\n }\n const screen = real.map(c => ({\n c,\n s0: bpToScreen(c.bp0),\n s1: bpToScreen(c.bp1),\n trimLeft: 0,\n trimRight: 0,\n }))\n for (let i = 0; i + 1 < screen.length; i++) {\n const left = screen[i]!\n const right = screen[i + 1]!\n const demand = Math.min(right.c.x0 - left.c.x1, MAX_GAP_PX)\n const short = demand - (right.s0 - left.s1)\n if (short > 0) {\n const capLeft = (left.s1 - left.s0) / 2\n const capRight = (right.s1 - right.s0) / 2\n let fromLeft = Math.min(short / 2, capLeft)\n const fromRight = Math.min(short - fromLeft, capRight)\n fromLeft = Math.min(short - fromRight, capLeft)\n left.trimRight = fromLeft\n right.trimLeft = fromRight\n }\n }\n const knots: Knot[] = []\n for (const { c, s0, s1, trimLeft, trimRight } of screen) {\n knots.push(\n { tx: c.x0, sx: s0 + trimLeft },\n { tx: c.x1, sx: s1 - trimRight },\n )\n }\n return knots\n}\n\n// Beyond the first and last knot the drawing continues at one screen px per\n// tube px, which is where the track stubs past the end nodes go.\nexport function warpX(knots: readonly Knot[], tx: number) {\n const n = knots.length\n if (n === 0) {\n return tx\n }\n const first = knots[0]!\n const last = knots[n - 1]!\n if (tx <= first.tx) {\n return first.sx + (tx - first.tx)\n }\n if (tx >= last.tx) {\n return last.sx + (tx - last.tx)\n }\n let lo = 0\n let hi = n - 1\n while (hi - lo > 1) {\n const mid = (lo + hi) >> 1\n if (knots[mid]!.tx <= tx) {\n lo = mid\n } else {\n hi = mid\n }\n }\n const a = knots[lo]!\n const b = knots[hi]!\n const span = b.tx - a.tx\n return span > 0 ? a.sx + ((tx - a.tx) / span) * (b.sx - a.sx) : a.sx\n}\n", "import { referenceKnots, warpX } from './warp'\n\nimport type { TubeMapDrawing } from '../layout/tubeMapLayout'\n\nexport interface TubeMapView {\n scaleX: number\n translateX: number\n scaleY: number\n translateY: number\n // the pane's height less its fit padding\n usableHeight: number\n}\n\n// Tube coordinates to screen px\nexport interface TubeMapTransform {\n x: (tx: number) => number\n y: (ty: number) => number\n // screen px per tube px in y, which thins the node outlines of a squeezed\n // stack along with its tubes\n yScale: number\n}\n\nconst MIN_TUBE_Y_SCALE = 0.05\n\n// How far a node's box reaches past its tubes (tubemap-core's nodeOutlinePath)\nconst NODE_BOX_PAD = 9\n\n// On the reference axis y is px and a linear view has no vertical pan, so a\n// stack of tubes taller than the track squeezes to fit it; dragging the track\n// taller gives the tubes back their width.\nfunction squeeze(drawing: TubeMapDrawing, usableHeight: number) {\n const { minY, maxY } = drawing.layout.bounds\n const h = maxY - minY\n return drawing.columns && h > 0\n ? Math.max(MIN_TUBE_Y_SCALE, Math.min(1, usableHeight / h))\n : 1\n}\n\n// The tube map's own axis is the layout's x; the reference axis warps each\n// column onto the bp it covers (warp.ts).\nexport function tubeMapFrame(\n drawing: TubeMapDrawing,\n view: TubeMapView,\n): TubeMapTransform {\n const { scaleX, translateX, translateY } = view\n const yScale = view.scaleY * squeeze(drawing, view.usableHeight)\n const y = (ty: number) => (ty - drawing.yOffset) * yScale + translateY\n const { columns } = drawing\n if (columns) {\n const knots = referenceKnots(columns, bp => bp * scaleX + translateX)\n return { x: tx => warpX(knots, tx), y, yScale }\n }\n return { x: tx => tx * scaleX + translateX, y, yScale }\n}\n\n// The node whose box is under a screen point. The canvas's hit test measures\n// from a centreline and a stroke width, and a tube map box is as tall as the\n// tubes through it.\nexport function tubeMapNodeAt(\n drawing: TubeMapDrawing,\n { x, y }: TubeMapTransform,\n sx: number,\n sy: number,\n) {\n let hit: string | null = null\n drawing.layout.nodes.forEach(node => {\n if (\n node.order >= 0 &&\n x(node.x - NODE_BOX_PAD) <= sx &&\n sx <= x(node.x + node.pixelWidth + NODE_BOX_PAD) &&\n y(node.y - NODE_BOX_PAD) <= sy &&\n sy <= y(node.y + node.contentHeight + NODE_BOX_PAD)\n ) {\n hit = node.name\n }\n })\n return hit\n}\n", "import { clampZoom } from './pipeline'\n\nimport type { Bounds } from './pipeline'\nimport type { AxisScale } from './util/geometry'\n\n// The pan and zoom arithmetic a pane runs, in layout units, with no host in it.\n\nexport interface PaneTransform {\n scale: number\n translateX: number\n translateY: number\n}\n\n// The zoom as a pair of axis scales. `pixelRows` rides along rather than being\n// derived downstream: the deletion bow is capped in a row layout and not in\n// the isotropic one, and `scaleY !== scaleX` coincides at one zoom level.\nexport function axisScaleOf(scale: number, pixelRows: boolean): AxisScale {\n return { scaleX: scale, scaleY: pixelRows ? 1 : scale, pixelRows }\n}\n\n// What a pane of `width` x `height` css px shows, in layout units.\nexport function viewportOf(\n t: { translateX: number; translateY: number },\n axis: AxisScale,\n width: number,\n height: number,\n): Bounds {\n return {\n minX: -t.translateX / axis.scaleX,\n minY: -t.translateY / axis.scaleY,\n maxX: (width - t.translateX) / axis.scaleX,\n maxY: (height - t.translateY) / axis.scaleY,\n }\n}\n\n// The window a geometry build covers: the pane plus `panes` whole panes on\n// every side, so an ordinary pan never blanks its margins for the debounce and\n// never rebuilds at all.\nexport function 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\nexport function 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// Zoom by `factor` about a screen point. y only follows when it is on the same\n// scale: a row layout's y is screen px, so moving it by the ratio would slide\n// the rows off under the cursor while their pitch stayed put.\nexport function zoomAbout(\n t: PaneTransform,\n factor: number,\n centerX: number,\n centerY: number,\n pixelRows: boolean,\n): PaneTransform {\n const scale = clampZoom(t.scale * factor)\n const ratio = scale / t.scale\n return {\n scale,\n translateX: centerX - (centerX - t.translateX) * ratio,\n translateY: pixelRows\n ? t.translateY\n : centerY - (centerY - t.translateY) * ratio,\n }\n}\n\n// Where a screen point falls in layout units.\nexport function screenToLayout(\n x: number,\n y: number,\n t: { translateX: number; translateY: number },\n axis: AxisScale,\n) {\n return {\n x: (x - t.translateX) / axis.scaleX,\n y: (y - t.translateY) / axis.scaleY,\n }\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 { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nexport interface TrackScanSession {\n tracks: AnyConfigurationModel[]\n assemblies: AnyConfigurationModel[]\n}\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. The cut region 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 './launchFromGraph'\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.\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 './launchFromGraph'\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'\n\nimport type { AnyConfigurationModel } from '@jbrowse/core/configuration'\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(item => typeof item === 'string')\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", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport {\n TrackType,\n createBaseTrackConfig,\n createBaseTrackModel,\n} from '@jbrowse/core/pluggableElementTypes'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'\n\n// a lanes copy asking for headerLanes, so only gbz-base offers lanes\nfunction displaysInOrder(pluginManager: PluginManager): DisplayType[] {\n const displays = pluginManager.getElementTypesInGroup(\n 'display',\n ) as DisplayType[]\n const named = (name: string) => displays.find(d => d.name === name)\n const lanes = named('MultiWaySyntenyDisplay')\n return [\n named('LinearGraphDisplay'),\n named('LinearBasicDisplay'),\n lanes &&\n Object.assign(Object.create(lanes) as DisplayType, {\n adapterCapabilities: ['headerLanes'],\n }),\n ].filter((d): d is DisplayType => d !== undefined)\n}\n\nexport default function GraphTrackF(pluginManager: PluginManager) {\n pluginManager.addTrackType(() => {\n const configSchema = ConfigurationSchema(\n 'GraphTrack',\n {},\n {\n baseConfiguration: createBaseTrackConfig(pluginManager),\n explicitIdentifier: 'trackId',\n },\n )\n const track = new TrackType({\n name: 'GraphTrack',\n displayName: 'Pangenome graph track',\n configSchema,\n stateModel: createBaseTrackModel(\n pluginManager,\n 'GraphTrack',\n configSchema,\n ),\n })\n for (const display of displaysInOrder(pluginManager)) {\n track.addDisplayType(display)\n }\n return track\n })\n}\n", "import type modelFactory from './GraphGenomeView/viewModel'\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 cuts the\n// `loadedTrackId`/`loadedRegion` pair once, when 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 Object.assign(\n new DisplayType({\n name: 'LinearGraphDisplay',\n displayName: 'Graph',\n configSchema,\n stateModel: stateModelFactory(configSchema),\n // FeatureTrack and SyntenyTrack for the configs and share links that\n // name the graph display on one, which 4.0.7 and before required\n trackType: ['GraphTrack', 'FeatureTrack', 'SyntenyTrack'],\n viewType: 'LinearGenomeView',\n ReactComponent: lazy(() => import('./components/LinearGraphDisplay')),\n }),\n { adapterCapabilities: ['getSubgraph'] },\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: 'the height of the track the graph is drawn in',\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 { getSession } from '@jbrowse/core/util'\nimport TrackHeightMixin from '@jbrowse/display-kit/TrackHeightMixin'\nimport { addDisposer, types } from '@jbrowse/mobx-state-tree'\nimport {\n computeActivityPhase,\n computeDisplayStatusPhase,\n} from '@jbrowse/render-core/displayPhase'\nimport SettingsIcon from '@mui/icons-material/Settings'\nimport { reaction } from 'mobx'\n\nimport { cutHolds, hostCut, hostWindow } from '../GraphGenomeView/host'\nimport { layoutModeByValue } from '../GraphGenomeView/layoutModes'\nimport {\n GraphPaneMixin,\n MAX_GRAPH_REGION_BP,\n formatSpanBp,\n} from '../GraphGenomeView/model'\nimport {\n graphReferenceAssembly,\n offReferenceProblem,\n trackLanes,\n} from '../graphTrackConfig'\n\nimport type { LinearGraphDisplayConfigModel } from './configSchema'\nimport type { SubgraphRegion, SubgraphTier } from '../GetSubgraph'\nimport type { ColorScheme } from '../GraphGenomeView/colorSchemes'\nimport type { HostWindow } from '../GraphGenomeView/host'\nimport type { LayoutModeValue } from '../GraphGenomeView/layoutModes'\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Instance } from '@jbrowse/mobx-state-tree'\nimport type { DisplayStatusPhase } from '@jbrowse/render-core/displayPhase'\n\nconst GraphTrackSettingsDialog = lazy(\n () => import('./components/GraphTrackSettingsDialog'),\n)\n\nexport function stateModelFactory(configSchema: LinearGraphDisplayConfigModel) {\n return (\n types\n .compose(\n 'LinearGraphDisplay',\n BaseDisplay,\n TrackHeightMixin(),\n GraphPaneMixin(),\n types.model({\n type: types.literal('LinearGraphDisplay'),\n configuration: ConfigurationReference(configSchema),\n // A 4.0 entry's nested state, which preProcessSnapshot folds flat and\n // so never holds. Declared so a session spec stating it is not\n // reported as an unknown key.\n pane: types.maybe(types.frozen()),\n // the window the graph on screen was asked for, so a restored\n // session cuts it again\n cutRegion: types.maybe(types.frozen<SubgraphRegion>()),\n // whether that cut came from the track's coarse pair\n // (RgfaTabixAdapter), on which no bp cap applies\n coarseCut: types.optional(types.boolean, false),\n // The bp ceiling on a fine cut, a proxy for node count that only\n // holds at fine granularity; `maxGraphNodes` counts what came back.\n maxRegionBp: types.optional(types.number, MAX_GRAPH_REGION_BP),\n // How far the cut follows links past the region's own segments. At\n // 0 a detour that leaves the backbone before the window and rejoins\n // after it arrives as a fragment, since its interior is indexed only\n // under its own sequence, so one bubble draws as two unrelated\n // insertions. A hop follows alleles only, so it never walks the\n // backbone out of the window.\n subgraphContext: types.optional(types.number, 1),\n // the haplotypes a GBZ cut is for; unset is the lanes the track names\n subgraphHaplotypes: types.maybe(types.frozen<string[]>()),\n }),\n )\n // a 4.0 session nests the graph's state under `pane`\n .preProcessSnapshot(snapshot => {\n const { pane, ...rest } = snapshot as { pane?: { type?: string } }\n if (!pane) {\n return snapshot\n }\n const { type: _type, ...props } = pane\n return { ...props, ...rest } as typeof snapshot\n })\n .volatile(() => ({\n recuts: 0,\n // the host window the settle clock last woke on\n settledWindow: undefined as HostWindow | undefined,\n }))\n .views(self => ({\n get defaultLayoutMode(): LayoutModeValue {\n return getConf(self, 'layoutMode')\n },\n get defaultColorScheme(): ColorScheme {\n return getConf(self, 'colorScheme')\n },\n get canvasHeight() {\n return self.height\n },\n get canRetryLoad() {\n return self.cutRegion !== undefined\n },\n get chosenHaplotypes() {\n return (\n self.subgraphHaplotypes ??\n trackLanes(self.parentTrack.configuration)\n )\n },\n // the linear view's zoom past which the track's coarse pair is cut, or\n // undefined for a track with none\n get coarseAboveBpPerPx() {\n const coarse = self.adapterConfig.coarse as\n { aboveBpPerPx?: unknown } | undefined\n const above = coarse?.aboveBpPerPx\n return typeof above === 'number' ? above : undefined\n },\n }))\n .views(self => ({\n tierAt(bpPerPx: number): SubgraphTier {\n const above = self.coarseAboveBpPerPx\n return above !== undefined && bpPerPx > above ? 'coarse' : 'fine'\n },\n capFor(tier: SubgraphTier) {\n return tier === 'coarse' ? Infinity : self.maxRegionBp\n },\n get cutTier(): SubgraphTier {\n return self.coarseCut && self.coarseAboveBpPerPx !== undefined\n ? 'coarse'\n : 'fine'\n },\n }))\n .views(self => ({\n get settledCapBp() {\n const seen = self.settledWindow\n return seen ? self.capFor(self.tierAt(seen.bpPerPx)) : Infinity\n },\n }))\n .views(self => ({\n get regionTooLarge() {\n const seen = self.settledWindow\n return seen !== undefined && seen.end - seen.start > self.settledCapBp\n },\n get regionTooLargeReason() {\n const seen = self.settledWindow\n return seen\n ? `Region too large for a graph cut (${formatSpanBp(seen.end - seen.start)}, max ${formatSpanBp(self.settledCapBp)})`\n : ''\n },\n get zoomCanReleaseGate() {\n return true\n },\n get fetchCanceled() {\n return self.loadCanceled\n },\n }))\n .views(self => ({\n get displayPhase(): DisplayStatusPhase {\n // A backend that failed is an error the canvas reports with its\n // retry; the status chrome has no renderError phase of its own.\n return computeDisplayStatusPhase(\n {\n regionTooLarge: self.regionTooLarge,\n error: self.error ?? self.renderError,\n },\n () =>\n computeActivityPhase(\n {\n isMinimized: self.isMinimized,\n fetchInert: false,\n viewportEmpty:\n self.host?.initialized === true &&\n self.host.hasVisibleContent === false,\n isLoading: self.isLoading || !self.hasGraph,\n fetchCanceled: self.loadCanceled,\n awaitingDependentData: false,\n rendersCanvas: true,\n canvasDrawn: self.painted && self.geometryPainted,\n },\n () => true,\n () => self.host?.effectiveBodyMounted ?? true,\n ),\n )\n },\n }))\n .actions(self => ({\n // The setters describe the next cut; the caller re-cuts.\n setSubgraphContext(hops: number) {\n self.subgraphContext = hops\n },\n setSubgraphHaplotypes(haplotypes: string[] | undefined) {\n self.subgraphHaplotypes = haplotypes\n },\n setMaxRegionBp(bp: number) {\n self.maxRegionBp = bp\n },\n // Cut `cutRegion` again with the current options. A hop past a coarse\n // cut reaches nothing new: every bubble node's two links are indexed\n // under the backbone either side of it.\n cut() {\n const region = self.cutRegion\n if (!region) {\n return\n }\n const problem = offReferenceProblem(\n graphReferenceAssembly(self.parentTrack.configuration),\n region.assemblyName,\n )\n if (problem) {\n self.clearGraph()\n self.setError(new Error(problem))\n return\n }\n const coarse = self.cutTier === 'coarse'\n return self.cutSubgraph(self.adapterConfig, region, {\n hops: coarse ? 0 : self.subgraphContext,\n haplotypes: self.chosenHaplotypes,\n tier: coarse ? 'coarse' : undefined,\n })\n },\n }))\n .actions(self => ({\n recutAt(seen: HostWindow) {\n const tier = self.tierAt(seen.bpPerPx)\n self.coarseCut = tier === 'coarse'\n self.cutRegion = hostCut(\n seen,\n self.capFor(tier),\n layoutModeByValue(self.chosenLayoutMode).cutMargins,\n )\n self.recuts++\n return self.cut()\n },\n }))\n .actions(self => ({\n // The settle clock. A window the cut still holds fetches nothing; one\n // past its edge re-cuts, on the tier the zoom asks for, with margins\n // when the host places x and the window alone when the layout draws\n // its own picture of it. A canceled cut is re-made by the next move.\n // Returns whether it re-cut.\n settleOn(seen: HostWindow) {\n self.settledWindow = seen\n const margins = layoutModeByValue(self.chosenLayoutMode).cutMargins\n if (\n self.regionTooLarge ||\n (!self.loadCanceled &&\n self.tierAt(seen.bpPerPx) === self.cutTier &&\n cutHolds(self.cutRegion, seen, margins))\n ) {\n return false\n }\n void self.recutAt(seen)\n return true\n },\n switchLayout(mode: LayoutModeValue) {\n const margins = layoutModeByValue(self.chosenLayoutMode).cutMargins\n self.setLayoutMode(mode)\n const seen = self.host ? hostWindow(self.host) : undefined\n if (\n seen &&\n !self.regionTooLarge &&\n layoutModeByValue(mode).cutMargins !== margins\n ) {\n return self.recutAt(seen)\n }\n return self.recomputeLayout()\n },\n retryLoad() {\n void self.cut()\n },\n reload() {\n void self.cut()\n },\n cancelFetchByUser() {\n self.stopLoad()\n },\n }))\n .actions(self => ({\n forceLoad() {\n const seen = self.settledWindow\n if (seen) {\n self.setMaxRegionBp(Math.ceil(seen.end - seen.start))\n self.settleOn(seen)\n }\n },\n // The host's two clocks. The frame clock moves x with every frame of\n // the linear view and fetches nothing; the settle clock wakes on its\n // debounced blocks.\n afterAttach() {\n if (self.cutRegion && !self.graph) {\n void self.cut()\n }\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 .views(self => ({\n trackMenuItems(): MenuItem[] {\n return [\n ...self.graphMenuItems(),\n {\n label: 'Settings',\n icon: SettingsIcon,\n onClick: () => {\n getSession(self).queueDialog(onClose => [\n GraphTrackSettingsDialog,\n { model: self, open: true, onClose },\n ])\n },\n },\n ...self.launchMenuItems(),\n ]\n },\n }))\n )\n}\n\nexport type LinearGraphDisplayStateModel = ReturnType<typeof stateModelFactory>\nexport type LinearGraphDisplayModel = Instance<LinearGraphDisplayStateModel>\n// what the track's own settings read, which the track menu that opens them\n// cannot name without naming itself\nexport type LinearGraphCutModel = Omit<\n LinearGraphDisplayModel,\n 'trackMenuItems'\n>\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 a graph display needs of its track's adapter\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 GraphTrack\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 {\n buildAdapterConfig,\n isSegmentsLocation,\n} from '../GraphAddTrackWorkflow/buildTrackConfig'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { FileLocation } from '@jbrowse/core/util'\nimport type {\n AdapterGuesser,\n TrackTypeGuesser,\n} from '@jbrowse/core/util/tracks'\n\ninterface TrackConfigSnapshot {\n displays?: { type?: string; [key: string]: unknown }[]\n [key: string]: unknown\n}\n\nconst GRAPH_ADAPTERS = new Set(['RgfaTabixAdapter', 'GbzBaseSyntenyAdapter'])\n\nfunction locationName(loc: FileLocation) {\n return 'uri' in loc ? loc.uri : 'localPath' in loc ? loc.localPath : loc.name\n}\n\nexport function isGbzLocation(loc: FileLocation) {\n return locationName(loc).toLowerCase().endsWith('.gbz.db')\n}\n\n// A gbz-base adapter cuts on the first assembly it names. A track added\n// through Add track names that assembly on the track alone.\nexport function withGbzAnchor(snap: TrackConfigSnapshot) {\n const adapter = snap.adapter as\n { type?: string; assemblyNames?: unknown[] } | undefined\n return adapter?.type === 'GbzBaseSyntenyAdapter' &&\n !adapter.assemblyNames?.length &&\n Array.isArray(snap.assemblyNames) &&\n snap.assemblyNames.length > 0\n ? { ...snap, adapter: { ...adapter, assemblyNames: snap.assemblyNames } }\n : snap\n}\n\nexport function guessGraphAdapter(\n file: FileLocation,\n index?: FileLocation,\n adapterHint?: string,\n) {\n if (\n (!adapterHint || adapterHint === 'GbzBaseSyntenyAdapter') &&\n isGbzLocation(file)\n ) {\n return {\n type: 'GbzBaseSyntenyAdapter',\n gbzDbLocation: file,\n ...(index ? { haplotypeIndexLocation: index } : {}),\n }\n }\n if (\n (!adapterHint || adapterHint === 'RgfaTabixAdapter') &&\n isSegmentsLocation(file)\n ) {\n return buildAdapterConfig({\n choice: 'RgfaTabixAdapter',\n loc: file,\n indexLoc: index,\n assembly: '',\n sample: '',\n })\n }\n return undefined\n}\n\nexport default function GraphTrackDefaultsF(pluginManager: PluginManager) {\n pluginManager.addToExtensionPoint('Core-preProcessTrackConfig', withGbzAnchor)\n pluginManager.addToExtensionPoint(\n 'Core-guessAdapterForLocation',\n (next: AdapterGuesser): AdapterGuesser =>\n (file, index, adapterHint) =>\n guessGraphAdapter(file, index, adapterHint) ??\n next(file, index, adapterHint),\n )\n pluginManager.addToExtensionPoint(\n 'Core-guessTrackTypeForLocation',\n (next: TrackTypeGuesser): TrackTypeGuesser =>\n (adapterName, file) =>\n GRAPH_ADAPTERS.has(adapterName)\n ? 'GraphTrack'\n : next(adapterName, file),\n )\n}\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 graph displays in a linear view's own tracks, connected to it by where\n// they sit.\nfunction trackGraphs(view: Record<string, unknown>) {\n const tracks = Array.isArray(view.tracks) ? view.tracks : []\n return tracks.flatMap((track: unknown) =>\n isRecord(track) && Array.isArray(track.displays)\n ? track.displays.filter(\n (display: unknown) =>\n isRecord(display) && display.type === 'LinearGraphDisplay',\n )\n : [],\n ) as Record<string, unknown>[]\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 (isRecord(view) && view.id === linearViewId) {\n for (const display of trackGraphs(view)) {\n const region = readRegion(display.hoverHighlight)\n if (region && isOnAssembly(region, linearAssemblyNames)) {\n highlights.push({\n key: typeof display.id === 'string' ? display.id : 'graph-track',\n region,\n })\n }\n }\n }\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.9'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
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_DisplayType", "__commonJSMin", "exports", "module", "require_trackHeightConfigSchemaFields", "__commonJSMin", "exports", "module", "require_TrackHeightMixin", "__commonJSMin", "exports", "module", "require_displayPhase", "__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_util", "import_mobx_state_tree", "import_configuration", "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", "repeats", "byName", "n", "halos", "bubble", "parts", "minX", "maxX", "minY", "nodeIds", "ends", "bubbleSegmentIds", "node", "line", "isBackbone", "svgPath", "p", "labelAt", "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", "b", "lo", "hi", "from", "to", "backbone", "ends", "boundaries", "node", "ia", "ib", "walkIndex", "p", "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", "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", "isLinearHost", "view", "v", "widest", "blocks", "best", "b", "hostFrame", "region", "block", "bpPerPx", "hostWindow", "displayed", "hostCut", "window", "capBp", "margins", "visible", "margin", "start", "end", "over", "cutHolds", "cut", "exact", "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", "b", "recordPathEdges", "nodeIds", "i", "key", "edge", "pathIds", "lengths", "n", "anchoring", "surveyPaths", "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", "loadGraph", "text", "name", "opts", "graph", "anchorGraph", "convertGFAToGraph", "parseGFA", "engineRequest", "scaling", "settings", "anchored", "isBackbone", "seededNodes", "engineKey", "forceLayout", "engine", "spread", "spreadFor", "merged", "mergeRuns", "result", "duration", "layoutScaling", "drawn", "n", "drawnNodeLength", "positions", "splitRuns", "id", "orientToReference", "layoutExtent", "nodePositions", "minX", "minY", "maxX", "maxY", "segments", "seg", "FIT_PADDING", "MIN_ZOOM", "MAX_ZOOM", "clampZoom", "zoom", "fittedTranslateY", "bounds", "usableHeight", "yScale", "leftoverY", "fitTransform", "paneWidth", "paneHeight", "pixelRows", "usableWidth", "fitX", "fitY", "scale", "drawingBounds", "layout", "region", "extent", "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", "isReal", "c", "referenceKnots", "columns", "bpToScreen", "real", "first", "screen", "i", "left", "right", "short", "capLeft", "capRight", "fromLeft", "fromRight", "knots", "s0", "s1", "trimLeft", "trimRight", "warpX", "tx", "last", "lo", "hi", "mid", "a", "b", "span", "MIN_TUBE_Y_SCALE", "NODE_BOX_PAD", "squeeze", "drawing", "usableHeight", "minY", "maxY", "h", "tubeMapFrame", "view", "scaleX", "translateX", "translateY", "yScale", "y", "ty", "columns", "knots", "referenceKnots", "bp", "tx", "warpX", "tubeMapNodeAt", "x", "sx", "sy", "hit", "node", "axisScaleOf", "scale", "pixelRows", "viewportOf", "t", "axis", "width", "height", "padded", "v", "panes", "w", "h", "contains", "outer", "inner", "zoomAbout", "factor", "centerX", "centerY", "clampZoom", "ratio", "walkHighlight", "graph", "name", "path", "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", "MAX_CANVAS_HEIGHT", "MIN_CANVAS_HEIGHT", "VARIANT_MAP_HEIGHT", "SEGMENTS_SUFFIX", "bubblePrefix", "adapterConfig", "uri", "HOVER_BRIGHTEN", "SELECT_BRIGHTEN", "VIEWPORT_DEBOUNCE_MS", "VIEWPORT_PANES_BUILT", "EMPTY_BATCH", "dependOn", "_values", "MAX_GRAPH_REGION_BP", "formatSpanBp", "bp", "DEFAULT_MAX_GRAPH_NODES", "geometryPainted", "model", "paneViewportOf", "viewportOf", "forceLayouts", "FORCE_LAYOUT_CACHE_SIZE", "forceLayoutsOf", "graph", "cache", "inheritForceLayouts", "from", "to", "remember", "key", "result", "GraphPaneMixin", "LAYOUT_MODE_VALUES", "COLOR_SCHEME_VALUES", "BUBBLE_SPREAD_VALUES", "NODE_WIDTH_VALUES", "self", "m", "modeUsesLayoutEngine", "n", "paths", "pathColorsLegible", "pathLegend", "walkHighlight", "resolveColorScheme", "bubblesFromGraph", "track", "region", "t", "GENE_ADAPTER_TYPES", "REPEAT_ADAPTER_TYPES", "pickRepeatTrack", "r", "buildNeighbors", "nodeInk", "deletionEdges", "repeat", "bars", "walkRows", "samples", "rows", "b", "withCalls", "row", "i", "bubble", "classifyBubble", "positions", "genePins", "labels", "c", "bubbleHalos", "name", "computeReferenceRamp", "axisScaleOf", "layout", "drawingBounds", "walkRowsExtent", "bounds", "usableWidth", "FIT_PADDING", "ceiling", "floor", "pickGeneTrack", "d", "ramp", "nodeId", "nodeById", "neighbors", "span", "nodeReferenceSpan", "backbone", "backboneNodes", "contributingAssemblies", "backboneSpan", "assemblyManager", "panSN", "byPrefix", "asm", "prefix", "loaded", "sample", "resolveContributors", "withReferenceRegion", "linearViewTarget", "node", "own", "nodeOwnLocation", "ownAssembly", "resolveLocationAssembly", "onReference", "paddedLocation", "view", "isLinearHost", "host", "layoutLabels", "graphRegion", "hostFrame", "drawing", "tubeMapPicture", "tubeMapFrame", "sx", "sy", "frame", "tubeMapNodeAt", "error", "message", "ms", "strokeCount", "built", "version", "quality", "linear", "mode", "reanchored", "anchorFromPaths", "draw", "show", "scheme", "spread", "width", "trackId", "px", "viewId", "paired", "withRows", "size", "edgeIdx", "panning", "session", "s", "tx", "ty", "clampZoom", "factor", "centerX", "centerY", "zoomAbout", "usableHeight", "fittedTranslateY", "fit", "fitTransform", "scale", "translateX", "engaging", "scheduleViewportDirty", "requestPositionsDirtyFrame", "dx", "dy", "seg", "loadController", "callEngine", "request", "rpcManager", "status", "forceLayoutKey", "engineKey", "computeLayout", "start", "local", "layoutModeByValue", "hit", "oriented", "forceLayout", "liveRequest", "layoutInto", "isLive", "signal", "computed", "e", "live", "parseAndLayout", "text", "keepSelection", "loadGraph", "selected", "liveLoad", "beginLoad", "load", "loadBubbles", "features", "f", "trackFeatures", "what", "config", "loadGenes", "geneModelsFrom", "loadRepeats", "repeatArraysFrom", "loadWholeGFA", "source", "abortLoad", "location", "opts", "fetchStart", "gfaText", "locLabel", "sub", "bubbleSubgraph", "bubbleSegmentIds", "label", "BUBBLE_KIND_NAMES", "backend", "hover", "readLgvHover", "hoverInRegion", "nodeForLgvHover", "firstViewport", "viewport", "contains", "hoveredNode", "hoveredEdge", "selectedNode", "nodes", "geometryStart", "viewportBounds", "padded", "batch", "buildGeometry", "dpr", "target", "showInLinearView", "launchTracks", "highlightInLinearView", "launchSyntenyView", "launchableSyntenyTracks", "walks", "LAYOUT_MODES", "COLOR_SCHEMES", "walk", "unit", "items", "item", "graphLaunchMenuItems", "import_configuration", "isStringArray", "value", "item", "trackLanes", "track", "assemblyNames", "lanes", "graphReferenceAssembly", "offReferenceProblem", "referenceAssembly", "assemblyName", "stateModelFactory", "GraphPaneMixin", "MAX_GRAPH_REGION_BP", "self", "clearPane", "region", "track", "span", "problem", "offReferenceProblem", "graphReferenceAssembly", "formatSpanBp", "graphLaunchKeys", "GraphGenomeViewF", "pluginManager", "ViewType", "stateModelFactory", "import_configuration", "import_pluggableElementTypes", "displaysInOrder", "pluginManager", "displays", "named", "name", "d", "lanes", "GraphTrackF", "configSchema", "track", "display", "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_displayPhase", "import_mobx", "GraphTrackSettingsDialog", "stateModelFactory", "configSchema", "TrackHeightMixin", "GraphPaneMixin", "MAX_GRAPH_REGION_BP", "snapshot", "pane", "rest", "_type", "props", "self", "trackLanes", "above", "bpPerPx", "tier", "seen", "formatSpanBp", "hops", "haplotypes", "bp", "region", "problem", "offReferenceProblem", "graphReferenceAssembly", "coarse", "hostCut", "layoutModeByValue", "margins", "cutHolds", "mode", "hostWindow", "places", "frame", "a", "b", "blocks", "host", "Settings_default", "onClose", "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", "GRAPH_ADAPTERS", "locationName", "loc", "isGbzLocation", "withGbzAnchor", "snap", "adapter", "guessGraphAdapter", "file", "index", "adapterHint", "isSegmentsLocation", "buildAdapterConfig", "GraphTrackDefaultsF", "pluginManager", "next", "adapterName", "import_util", "import_material", "import_mobx_react", "import_mui", "isRecord", "value", "readRegion", "region", "refName", "start", "end", "assemblyName", "isConnected", "view", "linearViewId", "liveViewIds", "connectedViewId", "isOnAssembly", "assemblyNames", "trackGraphs", "track", "display", "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", "GraphTrackF", "LaunchGraphGenomeViewF", "GraphHoverSyncF", "RgfaTabixAdapterF", "MinigraphBubbleAdapterF", "GbzBaseSyntenyAdapterF", "GraphAddTrackWorkflowF", "GraphTrackDefaultsF", "GraphComputeLayout", "GetSubgraph", "BubbleChart_default", "session", "e"]
|
|
7
7
|
}
|