jbrowse-plugin-graphgenomeviewer 3.0.4 → 3.1.0

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.
Files changed (58) hide show
  1. package/README.md +29 -2
  2. package/dist/chunks/GbzBaseSyntenyAdapter-MIOUYHOF.js +8 -0
  3. package/dist/chunks/GbzBaseSyntenyAdapter-MIOUYHOF.js.map +7 -0
  4. package/dist/chunks/GraphGenomeView-TM7WYDLH.js +14 -0
  5. package/dist/chunks/GraphGenomeView-TM7WYDLH.js.map +7 -0
  6. package/dist/chunks/MinigraphBubbleAdapter-A7SBF2XY.js +2 -0
  7. package/dist/chunks/MinigraphBubbleAdapter-A7SBF2XY.js.map +7 -0
  8. package/dist/chunks/RgfaTabixAdapter-7ZGKJJYW.js +3 -0
  9. package/dist/chunks/RgfaTabixAdapter-7ZGKJJYW.js.map +7 -0
  10. package/dist/chunks/{chunk-X3X74OGB.js → chunk-7T2HTRP3.js} +2 -2
  11. package/dist/chunks/{chunk-X3X74OGB.js.map → chunk-7T2HTRP3.js.map} +3 -3
  12. package/dist/chunks/{chunk-JCG7RLMB.js → chunk-HOM2Y6XS.js} +2 -2
  13. package/dist/chunks/chunk-HOM2Y6XS.js.map +7 -0
  14. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +2 -2
  15. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +4 -4
  16. package/package.json +21 -22
  17. package/src/GbzBaseSyntenyAdapter/GbzBaseSyntenyAdapter.test.ts +69 -0
  18. package/src/GbzBaseSyntenyAdapter/GbzBaseSyntenyAdapter.ts +13 -10
  19. package/src/GetSubgraph.ts +5 -1
  20. package/src/GraphGenomeView/components/FollowControl.test.tsx +69 -0
  21. package/src/GraphGenomeView/components/FollowControl.tsx +42 -0
  22. package/src/GraphGenomeView/components/GraphStats.tsx +1 -0
  23. package/src/GraphGenomeView/components/GraphToolbar.tsx +2 -0
  24. package/src/GraphGenomeView/follow.test.ts +624 -0
  25. package/src/GraphGenomeView/follow.ts +129 -0
  26. package/src/GraphGenomeView/layout/sampleRowLayout.test.ts +16 -0
  27. package/src/GraphGenomeView/layout/sampleRowLayout.ts +13 -4
  28. package/src/GraphGenomeView/layoutModes.ts +3 -1
  29. package/src/GraphGenomeView/model.ts +361 -55
  30. package/src/GraphGenomeView/types.ts +4 -0
  31. package/src/MinigraphBubbleAdapter/MinigraphBubbleAdapter.ts +1 -1
  32. package/src/RgfaTabixAdapter/RgfaTabixAdapter.test.ts +73 -0
  33. package/src/RgfaTabixAdapter/RgfaTabixAdapter.ts +48 -16
  34. package/src/RgfaTabixAdapter/configSchema.ts +70 -3
  35. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.tier1000.links.bed.gz +0 -0
  36. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.tier1000.links.bed.gz.tbi +0 -0
  37. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.tier1000.segs.bed.gz +0 -0
  38. package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.tier1000.segs.bed.gz.tbi +0 -0
  39. package/src/launchSubgraph/index.ts +12 -6
  40. package/src/launchSubgraph/launchSubgraph.test.ts +89 -3
  41. package/src/launchSubgraph/launchSubgraphView.ts +14 -6
  42. package/src/launchSubgraph/linearViewMenuItems.ts +3 -0
  43. package/src/launchSubgraph/subgraphMenuItems.ts +7 -0
  44. package/src/launchSubgraph/testEnv.ts +2 -0
  45. package/src/panSNTabix.ts +6 -5
  46. package/src/synteny/ComparativeAdapterBase.ts +13 -10
  47. package/src/synteny/clipFeatureToRegion.test.ts +41 -1
  48. package/src/synteny/clipFeatureToRegion.ts +22 -5
  49. package/src/version.ts +1 -1
  50. package/dist/chunks/GbzBaseSyntenyAdapter-6UTRY4CV.js +0 -8
  51. package/dist/chunks/GbzBaseSyntenyAdapter-6UTRY4CV.js.map +0 -7
  52. package/dist/chunks/GraphGenomeView-UD5WCV3Q.js +0 -14
  53. package/dist/chunks/GraphGenomeView-UD5WCV3Q.js.map +0 -7
  54. package/dist/chunks/MinigraphBubbleAdapter-B75VQK7T.js +0 -2
  55. package/dist/chunks/MinigraphBubbleAdapter-B75VQK7T.js.map +0 -7
  56. package/dist/chunks/RgfaTabixAdapter-IT7I5RR3.js +0 -3
  57. package/dist/chunks/RgfaTabixAdapter-IT7I5RR3.js.map +0 -7
  58. package/dist/chunks/chunk-JCG7RLMB.js.map +0 -7
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["global-externals:@jbrowse/render-core/useRenderingBackend", "global-externals:@mui/material/Button", "global-externals:@jbrowse/render-core/renderingBackendBase", "../../src/GraphGenomeView/components/GraphGenomeView.tsx", "../../src/GraphGenomeView/components/GraphCanvas.tsx", "../../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/Info.mjs", "../../src/GraphGenomeView/components/BubbleHalos.tsx", "../../src/GraphGenomeView/components/overlayLabels.ts", "../../src/GraphGenomeView/components/LabelChip.tsx", "../../src/GraphGenomeView/components/BubbleOverlay.tsx", "../../src/GraphGenomeView/components/GenePins.tsx", "../../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/CropFree.mjs", "../../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/ZoomIn.mjs", "../../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/ZoomOut.mjs", "../../src/GraphGenomeView/components/GraphToolbar.tsx", "../../src/GraphGenomeView/components/ColorSchemeSelect.tsx", "../../src/GraphGenomeView/components/FollowControl.tsx", "../../src/GraphGenomeView/components/GraphStats.tsx", "../../src/GraphGenomeView/components/LayoutSelect.tsx", "../../src/GraphGenomeView/components/RepeatSelect.tsx", "../../src/GraphGenomeView/components/SettingsMenu.tsx", "../../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/Delete.mjs", "../../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/MoreVert.mjs", "../../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/Settings.mjs", "../../src/GraphGenomeView/components/GraphSettingsDialog.tsx", "../../src/GraphGenomeView/components/SubgraphContextSelect.tsx", "../../src/GraphGenomeView/components/SubgraphHaplotypesField.tsx", "../../src/GraphGenomeView/components/WalkSelect.tsx", "../../src/GraphGenomeView/components/WalkRowsOverlay.tsx", "../../src/GraphGenomeView/graphLabels.ts", "../../src/GraphGenomeView/renderer/Canvas2DRenderer.ts", "../../src/GraphGenomeView/renderer/GraphRenderer.ts", "../../src/GraphGenomeView/util/SpatialIndex.ts", "../../src/GraphGenomeView/util/hitDetection.ts", "../../src/GraphGenomeView/util/wheelZoom.ts", "../../src/GraphGenomeView/components/GraphLoadStatus.tsx", "../../src/GraphGenomeView/components/ImportForm.tsx"],
4
+ "sourcesContent": ["module.exports = JBrowseExports[\"@jbrowse/render-core/useRenderingBackend\"];", "module.exports = JBrowseExports[\"@mui/material/Button\"];", "module.exports = JBrowseExports[\"@jbrowse/render-core/renderingBackendBase\"];", "import { observer } from 'mobx-react'\n\nimport GraphCanvas from './GraphCanvas'\nimport GraphLoadStatus from './GraphLoadStatus'\nimport ImportForm from './ImportForm'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst GraphGenomeView = observer(function GraphGenomeView({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n if (model.hasGraph) {\n return <GraphCanvas model={model} />\n }\n // A launched or restored view has a source of its own, so the import form\n // has nothing to offer it when that source fails\n if (model.canRetryLoad && (model.error || model.loadCanceled)) {\n return <GraphLoadStatus model={model} />\n }\n // Hidden rather than unmounted, so a typed URL survives a failed load\n return (\n <>\n {model.isLoading ? <GraphLoadStatus model={model} /> : null}\n <div hidden={model.isLoading}>\n <ImportForm model={model} />\n </div>\n </>\n )\n})\n\nexport default GraphGenomeView\n", "import { useEffect, useRef, useState } from 'react'\n\nimport { ErrorBanner, LoadingOverlay, Menu } from '@jbrowse/core/ui'\nimport { useRenderingBackend } from '@jbrowse/render-core/useRenderingBackend'\nimport InfoIcon from '@mui/icons-material/Info'\nimport { observer } from 'mobx-react'\n\nimport BubbleHalos from './BubbleHalos'\nimport BubbleOverlay from './BubbleOverlay'\nimport GenePins from './GenePins'\nimport GraphToolbar from './GraphToolbar'\nimport WalkRowsOverlay, { WalkRowsLegend } from './WalkRowsOverlay'\nimport { locLabel, nodeOwnLocation } from '../../launchFromGraph/contributors'\nimport { nodeLaunchMenuItems } from '../../launchFromGraph/graphMenuItems'\nimport { formatBp, graphLabels, rowLabelBox } from '../graphLabels'\nimport { REFERENCE_RAMP_MAX_HUE } from '../renderer/GeometryBuilder'\nimport { createGraphRenderer } from '../renderer/GraphRenderer'\nimport { findHoveredEdge, findHoveredNode } from '../util/hitDetection'\nimport { wheelZoomFactor } from '../util/wheelZoom'\n\nimport type { GraphGenomeViewModel } from '../model'\n\n// Bottom RIGHT, not bottom left: the row labels of a row-structured layout are\n// pinned to the left edge, so a bottom-left tooltip lands on top of them and\n// covers the one thing that says which haplotype a row is. Nothing is drawn\n// against the right edge.\nconst tooltipStyle = {\n position: 'absolute' as const,\n bottom: 8,\n right: 8,\n background: 'rgba(0,0,0,0.75)',\n color: 'white',\n padding: '4px 8px',\n borderRadius: 4,\n fontSize: 12,\n pointerEvents: 'none' as const,\n}\n\nconst wrapperStyle = { position: 'relative' as const }\n\n// The overlay origin has to be the canvas, not the wrapper: the wrapper also\n// holds the toolbar, so anything positioned against it is offset by the\n// toolbar's height, and a row label lands a whole row off the row it names.\nconst canvasAreaStyle = { position: 'relative' as const, lineHeight: 0 }\n\n// Above the labels, pins and legends drawn over the canvas, and with the line\n// height the canvas area zeroes, which would collapse the overlay's label\nconst loadingLayerStyle = {\n position: 'absolute' as const,\n inset: 0,\n zIndex: 10,\n lineHeight: 'normal',\n pointerEvents: 'none' as const,\n}\n\nconst rowLabelStyle = {\n position: 'absolute' as const,\n left: 6,\n transform: 'translateY(-50%)',\n background: 'rgba(255,255,255,0.82)',\n padding: '0 4px',\n borderRadius: 3,\n fontSize: 11,\n // explicit, because the canvas container zeroes line-height to kill the\n // inline-block gap under the canvas. Inherited, that collapses the label to a\n // zero-height box: it still paints, but it has no layout, so anything asking\n // whether it is visible (a screenshot spec's waitForVisible, a11y tooling)\n // is told no.\n lineHeight: '16px',\n whiteSpace: 'nowrap' as const,\n pointerEvents: 'none' as const,\n zIndex: 4,\n}\n\n// Names the rows of a row-structured layout (anchored, sample rows). Without\n// them the layout draws real structure that cannot be read: a stack of bars\n// where every row means something and nothing says what.\n//\n// Positioned from the layout's own `rowLabels` through the same transform the\n// canvas draws with, so a label tracks its row across pan and zoom instead of\n// sitting at a measured pixel. It therefore has to be mounted in the canvas's\n// own positioning context (canvasAreaStyle) \u2014 against the wrapper it picks up\n// the toolbar's height and names the wrong row. Pinned to the left edge rather\n// than to the row's own x, because a row's leftmost node is wherever its first\n// allele happens to branch. Rows scrolled out of the canvas are dropped rather\n// than clamped to the edge, so a label never points at a row that is not\n// there.\nconst RowLabels = observer(function RowLabels({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const rowLabels = model.drawnRowLabels\n return (\n <>\n {rowLabels.map(({ label, y }) => {\n const screenY = y * model.scaleY + model.translateY\n return screenY >= 0 && screenY <= model.canvasHeight ? (\n // Keyed on the row, not on the label: two rows can carry the same\n // words. The sample-row layout names row 0 for the assembly the\n // backbone comes from and the rows below it for the samples\n // contributing alleles, and a diploid reference is in both lists \u2014\n // one haplotype is the reference path, the other walks alleles of its\n // own, and `parsePanSN` reduces both to one sample name.\n <div\n key={y}\n data-testid=\"graph-row-label\"\n style={{ ...rowLabelStyle, top: screenY }}\n >\n {label}\n </div>\n ) : null\n })}\n </>\n )\n})\n\n// Top RIGHT: the row labels own the left edge and the hover tooltip owns the\n// bottom right, so this is the one corner nothing else claims. A row-structured\n// layout draws its band across the middle, which is what the keys are for. The\n// two keys stack rather than each claiming the corner, since a path-coloured\n// graph can also be reference-position coloured.\nconst legendStackStyle = {\n position: 'absolute' as const,\n top: 6,\n right: 6,\n display: 'flex',\n flexDirection: 'column' as const,\n alignItems: 'flex-end',\n gap: 4,\n pointerEvents: 'none' as const,\n zIndex: 4,\n}\n\nconst legendBoxStyle = {\n background: 'rgba(255,255,255,0.82)',\n padding: '4px 6px',\n borderRadius: 3,\n fontSize: 11,\n lineHeight: '15px',\n whiteSpace: 'nowrap' as const,\n}\n\nconst pathLegendRowStyle = { display: 'flex', alignItems: 'center', gap: 5 }\n\nconst pathSwatchStyle = { width: 18, height: 3, borderRadius: 2 }\n\n// Which haplotype each ribbon colour is. The ribbons are one stroke per path\n// fanned across every edge the path crosses, so without this the drawing states\n// that the alleles are taken by *different* samples and never which.\nconst PathLegend = observer(function PathLegend({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { pathLegend } = model\n return pathLegend.length > 0 ? (\n <div style={legendBoxStyle} data-testid=\"graph-path-legend\">\n {pathLegend.map(({ name, label, color }) => (\n <div key={name} style={pathLegendRowStyle}>\n <div style={{ ...pathSwatchStyle, backgroundColor: color }} />\n <span>{label}</span>\n </div>\n ))}\n </div>\n ) : null\n})\n\n// What the lifted walk carries through the window, against the reference walk\n// where the graph has one: the number the array's loops are drawn for.\nconst WalkReadout = observer(function WalkReadout({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const h = model.walkHighlight\n if (!h) {\n return null\n }\n const label = model.walkChoices.find(c => c.name === h.name)?.label ?? h.name\n const delta =\n h.referenceBp === undefined\n ? ''\n : h.bp === h.referenceBp\n ? ', the reference length'\n : `, ${h.bp > h.referenceBp ? '+' : '\u2212'}${Math.abs(h.bp - h.referenceBp).toLocaleString()} bp against the reference`\n return (\n <div style={legendBoxStyle} data-testid=\"graph-walk-readout\">\n <strong>{label}</strong>: {h.steps.toLocaleString()} steps,{' '}\n {h.bp.toLocaleString()} bp{delta}\n </div>\n )\n})\n\n// The reference-position ramp, as a strip labelled with the interval it runs\n// over. Nothing on screen used to say that red-to-magenta means left-to-right of\n// the cut window, so two tutorials carried that sentence in prose and a reader\n// arriving at a figure had no way to know it at all.\n//\n// The gradient is built from the same three numbers `getNodeColor` paints with\n// (hue 0 to REFERENCE_RAMP_MAX_HUE at 70%/50%), rather than from a picked pair\n// of hex stops, so it cannot come to describe a ramp the drawing stopped using.\nconst rampStripStyle = {\n minWidth: 90,\n height: 8,\n borderRadius: 2,\n background: `linear-gradient(to right, ${Array.from(\n { length: 7 },\n (_, i) =>\n `hsl(${(i / 6) * REFERENCE_RAMP_MAX_HUE}, 70%, 50%) ${(i / 6) * 100}%`,\n ).join(', ')})`,\n}\n\nconst rampEndsStyle = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 8,\n}\n\nconst ReferenceRampLegend = observer(function ReferenceRampLegend({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const domain = model.referenceRampDomain\n return domain ? (\n <div style={legendBoxStyle} data-testid=\"graph-ramp-legend\">\n <div style={rampStripStyle} />\n <div style={rampEndsStyle}>\n <span>{domain.start.toLocaleString()}</span>\n <span>({formatBp(domain.end - domain.start)})</span>\n <span>{domain.end.toLocaleString()}</span>\n </div>\n </div>\n ) : null\n})\n\nconst nodeLabelStyle = {\n position: 'absolute' as const,\n transform: 'translate(-50%, -50%)',\n background: 'rgba(255,255,255,0.78)',\n padding: '0 3px',\n borderRadius: 3,\n fontSize: 10,\n lineHeight: '13px',\n whiteSpace: 'nowrap' as const,\n pointerEvents: 'none' as const,\n zIndex: 3,\n}\n\n// Matches EDGE_DELETION_COLOR, so the words and the arc they name are one thing.\nconst deletionLabelStyle = {\n ...nodeLabelStyle,\n color: '#18181c',\n fontWeight: 600,\n}\n\n// Under the labels (z 3) and over the canvas, so a leader runs beneath the text\n// it points from rather than across it. `overflow: visible` because a label\n// displaced past the canvas edge is culled by the placement, not clipped here.\nconst leaderStyle = {\n position: 'absolute' as const,\n left: 0,\n top: 0,\n pointerEvents: 'none' as const,\n overflow: 'visible' as const,\n zIndex: 2,\n}\n\n// Writes each drawn thing's size onto it, which is what Bandage's Length label\n// does and what the tooltip alone could not: a graph you have to hover to read\n// is a graph nobody reads. graphLabels applies the same transform the canvas\n// draws with \u2014 so a label tracks its node across pan and zoom \u2014 and decides\n// which labels fit; this only paints them, mounted in the canvas's own\n// positioning context for the reason RowLabels is.\nconst GraphSizeLabels = observer(function GraphSizeLabels({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { nodePositions } = model\n if (!nodePositions) {\n return null\n }\n const labels = graphLabels({\n nodePositions,\n nodeLengths: model.nodeLengths,\n deletions: model.showDeletionEdges ? model.deletions : [],\n alleleDeletions: model.alleleDeletions,\n axis: model.axisScale,\n translateX: model.translateX,\n translateY: model.translateY,\n width: model.width,\n height: model.canvasHeight,\n // RowLabels paints over this overlay, so its boxes are occupied space\n reserved: model.drawnRowLabels.map(({ label, y }) =>\n rowLabelBox(label, y * model.scaleY + model.translateY),\n ),\n // A drag moves the position objects without replacing them, so nothing\n // else here can report it: reading this is both what invalidates the\n // per-layout caches inside `graphLabels` and what re-renders this overlay,\n // which is why a dragged node used to leave its own size label behind.\n version: model.positionsVersion,\n })\n // A label too wide for the arc it names is placed clear of it and tethered\n // back; nothing else in the drawing carries one.\n const leaders = labels.flatMap(({ key, leader }) =>\n leader ? [{ key, ...leader }] : [],\n )\n return (\n <>\n {leaders.length > 0 ? (\n <svg\n style={leaderStyle}\n width={model.width}\n height={model.canvasHeight}\n >\n {leaders.map(({ key, arcX, arcY, labelX, labelY }) => (\n <line\n key={key}\n data-testid=\"graph-label-leader\"\n x1={arcX}\n y1={arcY}\n x2={labelX}\n y2={labelY}\n stroke=\"#18181c\"\n strokeWidth={1}\n />\n ))}\n </svg>\n ) : null}\n {labels.map(({ key, text, x, y, kind }) => (\n <div\n key={key}\n data-testid=\"graph-size-label\"\n style={{\n ...(kind === 'deletion' ? deletionLabelStyle : nodeLabelStyle),\n left: x,\n top: y,\n }}\n >\n {text}\n </div>\n ))}\n </>\n )\n})\n\nconst HoverTooltips = observer(function HoverTooltips({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const hoveredNodeData = model.hoveredNode\n ? model.nodeById?.get(model.hoveredNode)\n : null\n\n const hoveredEdgeData =\n model.hoveredEdge !== null && model.graph\n ? model.graph.edges[model.hoveredEdge]\n : null\n\n const ownLocation = hoveredNodeData\n ? nodeOwnLocation(hoveredNodeData)\n : undefined\n\n const hoveredDeletion =\n model.hoveredEdge === null\n ? undefined\n : model.deletions.find(d => d.edgeIndex === model.hoveredEdge)\n\n return (\n <>\n {hoveredNodeData ? (\n <div style={tooltipStyle}>\n <strong>{hoveredNodeData.name}</strong> \u2014 length:{' '}\n {hoveredNodeData.length.toLocaleString()}, depth:{' '}\n {hoveredNodeData.depth.toFixed(1)}\n {/* Which assembly contributed this segment, and where it sits on it.\n rGFA states both, and until now neither reached the screen: the\n node was a bare id in a picture. */}\n {ownLocation ? (\n <>\n <br />\n {ownLocation.sample} {locLabel(ownLocation)} (rank{' '}\n {hoveredNodeData.stable?.rank})\n </>\n ) : null}\n </div>\n ) : null}\n {hoveredEdgeData ? (\n <div style={tooltipStyle}>\n {/* A deletion edge is the one link that means something on its own \u2014\n the backbone it skips is sequence some haplotype does not carry \u2014\n so it says how much rather than just naming its endpoints. */}\n {hoveredDeletion ? (\n <>\n <strong>Deletion</strong> {hoveredDeletion.bp.toLocaleString()} bp\n <br />\n {hoveredDeletion.refName}:{hoveredDeletion.start.toLocaleString()}\n -{hoveredDeletion.end.toLocaleString()}\n </>\n ) : (\n <>\n Edge: {hoveredEdgeData.from} \u2192 {hoveredEdgeData.to}\n </>\n )}\n </div>\n ) : null}\n </>\n )\n})\n\n// Flat rather than under a \"Launch view\" submenu: this menu is two or three\n// items long and every one of them is contextual to the node just clicked. The\n// submenu grouping earns its keep in the long view and track menus.\nconst NodeContextMenu = observer(function NodeContextMenu({\n model,\n nodeId,\n top,\n left,\n onClose,\n}: {\n model: GraphGenomeViewModel\n nodeId: string\n top: number\n left: number\n onClose: () => void\n}) {\n const { own, reference, highlight } = model.nodeLaunchTargets(nodeId)\n return (\n <Menu\n open\n anchorReference=\"anchorPosition\"\n anchorPosition={{ top, left }}\n onClose={() => {\n onClose()\n }}\n onMenuItemClick={callback => {\n callback()\n }}\n menuItems={[\n {\n label: 'Node details',\n icon: InfoIcon,\n onClick: () => {\n model.showNodeDetails(nodeId)\n },\n },\n ...nodeLaunchMenuItems({\n own,\n reference,\n highlight,\n onShowLinear: target => {\n model.showInLinearView(target)\n },\n onHighlight: model.canHighlightInLinearView\n ? target => {\n model.highlightInLinearView(target)\n }\n : undefined,\n }),\n ]}\n />\n )\n})\n\nconst GraphCanvas = observer(function GraphCanvas({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { canvasRef, canvas } = useRenderingBackend(createGraphRenderer, model)\n // Where the pointer was last, and whether it has travelled since mousedown \u2014\n // per-gesture scratch that nothing renders from, which is what a ref is for.\n // Whether a drag is in progress is model state (`isPanning`/`draggingNode`),\n // because the cursor renders from it.\n const lastMouseRef = useRef({ x: 0, y: 0 })\n const hasMovedRef = useRef(false)\n // A pan and a hover are applied once per frame, not once per mousemove:\n // mousemove fires in bursts well above the frame rate, and each pan step\n // repainted the canvas and re-placed every overlay label, while each hover\n // step ran both hit indexes. The pending pan is the summed delta; the pending\n // hover is the last pointer position, since only the last one can be right.\n const pendingRef = useRef<{\n frame: number\n pan: { dx: number; dy: number } | null\n hover: { x: number; y: number } | null\n }>({ frame: 0, pan: null, hover: null })\n useEffect(\n () => () => {\n cancelAnimationFrame(pendingRef.current.frame)\n },\n [],\n )\n const [contextNode, setContextNode] = useState<\n { nodeId: string; top: number; left: number } | undefined\n >(undefined)\n\n // wheel events need passive:false to call preventDefault \u2014 React registers\n // wheel listeners as passive, so we must add this imperatively\n useEffect(() => {\n if (canvas) {\n const c = canvas\n function handleWheel(e: WheelEvent) {\n e.preventDefault()\n const rect = c.getBoundingClientRect()\n model.zoom(\n wheelZoomFactor(e),\n e.clientX - rect.left,\n e.clientY - rect.top,\n )\n }\n c.addEventListener('wheel', handleWheel, { passive: false })\n return () => {\n c.removeEventListener('wheel', handleWheel)\n }\n }\n return undefined\n }, [canvas, model])\n\n function screenToGraph(screenX: number, screenY: number) {\n return {\n x: (screenX - model.translateX) / model.scaleX,\n y: (screenY - model.translateY) / model.scaleY,\n }\n }\n\n function getMouseCoord(e: React.MouseEvent) {\n const rect = (e.currentTarget as HTMLCanvasElement).getBoundingClientRect()\n return screenToGraph(e.clientX - rect.left, e.clientY - rect.top)\n }\n\n // The node at a graph coordinate, which mousedown, mousemove and click all\n // need. Takes the coordinate rather than the event so a caller that already\n // has one does not pay for a second getBoundingClientRect.\n function nodeAt(x: number, y: number) {\n const { nodePositions } = model\n return nodePositions\n ? findHoveredNode(\n nodePositions,\n x,\n y,\n model.axisScale,\n model.positionsVersion,\n model.nodeInk,\n )\n : null\n }\n\n function handleMouseDown(e: React.MouseEvent) {\n if (e.button === 0) {\n hasMovedRef.current = false\n const { x, y } = getMouseCoord(e)\n const node = nodeAt(x, y)\n if (node) {\n model.setDraggingNode(node)\n } else {\n model.setPanning(true)\n }\n lastMouseRef.current = { x: e.clientX, y: e.clientY }\n }\n }\n\n function applyPending() {\n const pending = pendingRef.current\n pending.frame = 0\n if (pending.pan) {\n model.setTransform(\n model.scale,\n model.translateX + pending.pan.dx,\n model.translateY + pending.pan.dy,\n )\n pending.pan = null\n }\n if (pending.hover && model.nodePositions && model.graph) {\n const { x, y } = screenToGraph(pending.hover.x, pending.hover.y)\n pending.hover = null\n const node = nodeAt(x, y)\n model.setHoveredNode(node)\n model.setHoveredEdge(\n node\n ? null\n : findHoveredEdge(\n model.nodePositions,\n model.graph,\n x,\n y,\n model.axisScale,\n // resolved, so the hit index bounds the ribbons that are\n // actually drawn \u2014 see effectiveDrawPaths\n model.effectiveDrawPaths,\n model.positionsVersion,\n model.deletionEdgeIndexes,\n model.hiddenEdgeIndexes,\n ),\n )\n }\n }\n\n function scheduleFrame() {\n if (!pendingRef.current.frame) {\n pendingRef.current.frame = requestAnimationFrame(applyPending)\n }\n }\n\n function dropPending() {\n const pending = pendingRef.current\n cancelAnimationFrame(pending.frame)\n pending.frame = 0\n pending.pan = null\n pending.hover = null\n }\n\n function handleMouseMove(e: React.MouseEvent) {\n const dx = e.clientX - lastMouseRef.current.x\n const dy = e.clientY - lastMouseRef.current.y\n lastMouseRef.current = { x: e.clientX, y: e.clientY }\n\n if (Math.abs(dx) > 1 || Math.abs(dy) > 1) {\n hasMovedRef.current = true\n }\n\n const pending = pendingRef.current\n if (model.draggingNode) {\n model.moveNode(model.draggingNode, dx / model.scaleX, dy / model.scaleY)\n } else if (model.isPanning) {\n pending.pan = {\n dx: (pending.pan?.dx ?? 0) + dx,\n dy: (pending.pan?.dy ?? 0) + dy,\n }\n scheduleFrame()\n } else {\n const rect = (\n e.currentTarget as HTMLCanvasElement\n ).getBoundingClientRect()\n pending.hover = { x: e.clientX - rect.left, y: e.clientY - rect.top }\n scheduleFrame()\n }\n }\n\n function handleMouseUp() {\n model.stopDragging()\n }\n\n function handleMouseLeave() {\n dropPending()\n model.stopDragging()\n model.setHoveredNode(null)\n model.setHoveredEdge(null)\n }\n\n // Right-clicking a node is the gesture that asks \"where is this?\", and until\n // now the graph had no answer: a node named an assembly and an offset in its\n // tags that nothing surfaced. The items come from the model's launch targets,\n // so what is offered is what can actually be opened.\n function handleContextMenu(e: React.MouseEvent) {\n const { x, y } = getMouseCoord(e)\n const node = nodeAt(x, y)\n if (node) {\n e.preventDefault()\n setContextNode({ nodeId: node, top: e.clientY, left: e.clientX })\n }\n }\n\n function handleClick(e: React.MouseEvent) {\n // a click that ended a drag selects nothing\n if (!hasMovedRef.current) {\n const { x, y } = getMouseCoord(e)\n const node = nodeAt(x, y)\n model.setSelectedNode(node)\n if (node) {\n model.showNodeDetails(node)\n }\n }\n }\n\n return (\n <div style={wrapperStyle}>\n <GraphToolbar model={model} />\n\n <div style={canvasAreaStyle}>\n <canvas\n ref={canvasRef}\n data-testid=\"graph-genome-canvas\"\n style={{\n width: model.width,\n height: model.canvasHeight,\n cursor: model.isPanning || model.draggingNode ? 'grabbing' : 'grab',\n display: 'block',\n }}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseLeave}\n onClick={handleClick}\n onContextMenu={handleContextMenu}\n />\n\n <RowLabels model={model} />\n <GraphSizeLabels model={model} />\n <BubbleHalos model={model} />\n <GenePins model={model} />\n <BubbleOverlay model={model} />\n <WalkRowsOverlay model={model} />\n <div style={legendStackStyle}>\n <ReferenceRampLegend model={model} />\n <PathLegend model={model} />\n <WalkRowsLegend model={model} />\n <WalkReadout model={model} />\n </div>\n\n <div style={loadingLayerStyle}>\n <LoadingOverlay\n isVisible={model.isLoading}\n immediate={!model.layoutResult}\n statusMessage={model.statusMessage}\n onCancel={\n model.canCancelLoad\n ? () => {\n model.cancelLoad()\n }\n : undefined\n }\n />\n </div>\n </div>\n\n <HoverTooltips model={model} />\n\n {contextNode ? (\n <NodeContextMenu\n model={model}\n nodeId={contextNode.nodeId}\n top={contextNode.top}\n left={contextNode.left}\n onClose={() => {\n setContextNode(undefined)\n }}\n />\n ) : null}\n\n {model.error ? (\n <ErrorBanner\n error={model.error}\n onReset={\n model.canRetryLoad\n ? () => {\n model.retryLoad()\n }\n : undefined\n }\n />\n ) : null}\n </div>\n )\n})\n\nexport default GraphCanvas\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-6h2zm0-8h-2V7h2z\"\n}), 'Info');", "import { observer } from 'mobx-react'\n\nimport LabelChip from './LabelChip'\nimport { LABEL_CHAR_PX, placeLabels } from './overlayLabels'\nimport { BUBBLE_KIND_COLORS } from '../bubbles/classifyBubble'\n\nimport type { LabelCandidate } from './overlayLabels'\nimport type { BubbleHalo, RouteLabel } from '../bubbles/bubbleHalos'\nimport type { GraphGenomeViewModel } from '../model'\n\n// The bubbles over a node drawing: each a translucent halo along its nodes,\n// drawn once in layout units and moved with the canvas by one transform, a\n// label at its highest node that opens the bubble, and a chip on each route\n// the walks take through it naming who takes it. The halo itself takes no\n// pointer events, so the nodes under it still hover and drag.\n\nconst svgStyle = {\n position: 'absolute' as const,\n left: 0,\n top: 0,\n pointerEvents: 'none' as const,\n overflow: 'hidden' as const,\n zIndex: 2,\n}\n\nconst HALO_FACTOR = 3.4\nconst LEGEND_CORNER_PX = 240\n// routes whose own stretches are drawn on top of each other stack their chips\nconst ROUTE_STACK = 8\n\nconst BubbleHalos = observer(function BubbleHalos({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { bubbleHalos, walkHighlight } = model\n if (bubbleHalos.length === 0) {\n return null\n }\n const {\n scaleX,\n scaleY,\n translateX,\n translateY,\n width,\n canvasHeight,\n contigThickness,\n } = model\n const halo = contigThickness * HALO_FACTOR\n const screen = (p: { x: number; y: number }) => ({\n x: p.x * scaleX + translateX,\n y: p.y * scaleY + translateY,\n })\n // a lifted walk dims the bubbles it never enters and the routes it does not\n // take, halo and chip alike\n const dimmedBubble = (h: BubbleHalo) =>\n walkHighlight !== undefined &&\n !h.nodeIds.some(id => walkHighlight.nodeIds.has(id))\n const dimmedRoute = (r: RouteLabel) =>\n walkHighlight !== undefined && !r.route.walks.includes(walkHighlight.name)\n\n // the legends own the top-right corner, and the Back button of a popped\n // graph the top-left\n const reserved = [{ x0: width - LEGEND_CORNER_PX, x1: width, y0: 0, y1: 60 }]\n if (model.poppedFrom) {\n reserved.push({\n x0: 0,\n x1: 60 + model.poppedFrom.label.length * LABEL_CHAR_PX * 1.2,\n y0: 0,\n y1: 44,\n })\n }\n // bubble labels place first, biggest bubble first, above the halo; route\n // chips place after them, on the far point of their loops\n const byBubble = [...bubbleHalos].sort((a, b) => b.members - a.members)\n const candidates: LabelCandidate<{ halo: BubbleHalo; route?: RouteLabel }>[] =\n [\n ...byBubble.map(h => {\n const { x, y } = screen(h.top)\n return { item: { halo: h }, x, y: y - halo / 2 - 6, text: h.label }\n }),\n ...byBubble.flatMap(h =>\n h.routes.map(r => {\n const { x, y } = screen(r.at)\n return {\n item: { halo: h, route: r },\n x,\n y: y + 4,\n text: r.text,\n stack: ROUTE_STACK,\n }\n }),\n ),\n ]\n const labels = placeLabels(\n candidates,\n { width, height: canvasHeight },\n reserved,\n )\n\n return (\n <svg\n style={svgStyle}\n width={width}\n height={canvasHeight}\n data-testid=\"graph-bubble-halos\"\n >\n <g\n transform={`translate(${translateX} ${translateY}) scale(${scaleX} ${scaleY})`}\n >\n {bubbleHalos\n .filter(h => !h.whole)\n .map(h => (\n <path\n key={`${h.bubble.start}-${h.bubble.end}`}\n d={h.path}\n fill=\"none\"\n stroke={BUBBLE_KIND_COLORS[h.kind]}\n strokeOpacity={dimmedBubble(h) ? 0.06 : 0.22}\n strokeWidth={halo}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n vectorEffect=\"non-scaling-stroke\"\n />\n ))}\n </g>\n {labels.map(({ item: { halo: h, route }, x, y, w, text }) =>\n route ? (\n <LabelChip\n key={`${h.bubble.start}-${h.bubble.end}-${route.route.steps.join(',')}`}\n x={x}\n y={y}\n w={w}\n text={text}\n color={BUBBLE_KIND_COLORS[h.kind]}\n small\n dimmed={dimmedRoute(route)}\n title={`${route.route.walks.length} walk(s): ${route.route.walks.join(', ')}`}\n testId=\"graph-route-label\"\n />\n ) : (\n <LabelChip\n key={`${h.bubble.start}-${h.bubble.end}-label`}\n x={x}\n y={y}\n w={w}\n text={text}\n color={BUBBLE_KIND_COLORS[h.kind]}\n dimmed={dimmedBubble(h)}\n title={`${h.label}\\n${h.bubble.segmentCount} segments \u00B7 click to open`}\n testId=\"graph-bubble-halo-label\"\n onClick={() => {\n void model.popBubble(h.bubble)\n }}\n />\n ),\n )}\n </svg>\n )\n})\n\nexport default BubbleHalos\n", "// Label placement shared by the SVG overlays: a label is a box in screen px,\n// and one that would land on a placed box, or off the pane, is dropped. Callers\n// order their candidates so the ones that matter place first.\n\nexport interface Box {\n x0: number\n x1: number\n y0: number\n y1: number\n}\n\nexport const LABEL_PX = 11\nexport const LABEL_CHAR_PX = 6.2\nexport const LABEL_PAD = 4\n\nexport interface LabelCandidate<T> {\n item: T\n x: number\n y: number\n text: string\n // how many rows down to try when the spot is taken, for a label that is\n // worth a column beside its neighbours rather than dropping\n stack?: number\n}\n\nexport interface PlacedLabel<T> extends LabelCandidate<T> {\n w: number\n}\n\nexport function labelWidth(text: string) {\n return text.length * LABEL_CHAR_PX + LABEL_PAD * 2\n}\n\nfunction overlaps(a: Box, b: Box) {\n return a.x0 < b.x1 && a.x1 > b.x0 && a.y0 < b.y1 && a.y1 > b.y0\n}\n\nexport function placeLabels<T>(\n candidates: LabelCandidate<T>[],\n frame: { width: number; height: number },\n reserved: Box[] = [],\n): PlacedLabel<T>[] {\n const placed = [...reserved]\n const out: PlacedLabel<T>[] = []\n const row = LABEL_PX + LABEL_PAD * 2\n for (const c of candidates) {\n const w = labelWidth(c.text)\n // a stack grows away from the nearer edge of the pane\n const direction = c.y > frame.height / 2 ? -1 : 1\n for (let tries = 0; tries <= (c.stack ?? 0); tries++) {\n const y = c.y + direction * tries * row\n const box = {\n x0: c.x - w / 2,\n x1: c.x + w / 2,\n y0: y - LABEL_PX - LABEL_PAD,\n y1: y + LABEL_PAD,\n }\n if (\n box.x1 < 0 ||\n box.x0 > frame.width ||\n box.y1 < 0 ||\n box.y0 > frame.height\n ) {\n break\n }\n if (placed.some(p => overlaps(p, box))) {\n continue\n }\n placed.push(box)\n out.push({ ...c, y, w })\n break\n }\n }\n return out\n}\n", "import { LABEL_PAD, LABEL_PX } from './overlayLabels'\n\n// One label on an overlay: a white chip with a coloured edge and text, its\n// baseline at y and centred on x.\nexport default function LabelChip({\n x,\n y,\n w,\n text,\n color,\n italic,\n small,\n dimmed,\n title,\n testId,\n onClick,\n}: {\n x: number\n y: number\n w: number\n text: string\n color: string\n italic?: boolean\n small?: boolean\n dimmed?: boolean\n title?: string\n testId?: string\n onClick?: () => void\n}) {\n return (\n <g\n style={{\n pointerEvents: onClick ? 'auto' : 'none',\n cursor: onClick ? 'pointer' : undefined,\n opacity: dimmed ? 0.35 : 1,\n }}\n data-testid={testId}\n onClick={onClick}\n >\n {title ? <title>{title}</title> : null}\n <rect\n x={x - w / 2}\n y={y - LABEL_PX - LABEL_PAD + 2}\n width={w}\n height={LABEL_PX + LABEL_PAD * 2 - 2}\n rx={3}\n fill=\"rgba(255,255,255,0.85)\"\n stroke={color}\n strokeWidth={small ? 0.6 : 1}\n />\n <text\n x={x}\n y={y}\n fontSize={small ? LABEL_PX - 1 : LABEL_PX}\n fontFamily=\"sans-serif\"\n fontStyle={italic ? 'italic' : undefined}\n fontWeight={italic ? 600 : undefined}\n fill={color}\n textAnchor=\"middle\"\n >\n {text}\n </text>\n </g>\n )\n}\n", "import { Button } from '@mui/material'\nimport { observer } from 'mobx-react'\n\nimport { LABEL_CHAR_PX } from './overlayLabels'\nimport { BUBBLE_KIND_COLORS } from '../bubbles/classifyBubble'\n\nimport type { GraphGenomeViewModel } from '../model'\n\n// The variant map's glyphs: one lens per bubble on the reference line the\n// canvas draws, sized by the longest route through it, coloured and labelled\n// by what kind of variation it is. SVG over the canvas rather than geometry in\n// it, because a glyph is text plus a shape that reads at one screen size\n// whatever the zoom, which is what the row labels are too.\n//\n// Height is in screen px and adapts to the room above the line, so a pane of\n// any height shows every glyph; a bubble shorter than the reference it\n// replaces also gets a dashed chord under the line, the deletion's side.\n\nconst svgStyle = {\n position: 'absolute' as const,\n left: 0,\n top: 0,\n pointerEvents: 'none' as const,\n overflow: 'visible' as const,\n zIndex: 3,\n}\n\nconst backButtonStyle = {\n position: 'absolute' as const,\n left: 8,\n top: 8,\n zIndex: 5,\n background: 'rgba(255,255,255,0.9)',\n textTransform: 'none' as const,\n}\n\nconst LABEL_ROW_PX = 15\nconst MAX_LABEL_ROWS = 8\nconst LEGEND_PX = 150\nconst MIN_GLYPH_PX = 10\n\nfunction glyphHeight(bp: number, room: number) {\n const raw = 12 + 26 * Math.log10(1 + bp)\n return Math.min(raw, room)\n}\n\nconst BubbleOverlay = observer(function BubbleOverlay({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { bubbleGlyphs, poppedFrom } = model\n const back = poppedFrom ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n style={backButtonStyle}\n data-testid=\"graph-unpop-bubble\"\n onClick={() => {\n void model.unpopBubble()\n }}\n >\n \u25C0 Back to {poppedFrom.label}\n </Button>\n ) : null\n if (bubbleGlyphs.length === 0) {\n return back\n }\n const { scaleX, translateX, translateY, width, canvasHeight } = model\n const lineY = translateY\n const X = (bp: number) => bp * scaleX + translateX\n // Labels stack in rows above the tallest glyph, biggest bubbles first so a\n // crowded window keeps the labels that matter; one that finds no row is left\n // to its tooltip rather than written over another.\n const maxRows = Math.max(\n 2,\n Math.min(MAX_LABEL_ROWS, Math.floor((lineY - 60) / LABEL_ROW_PX)),\n )\n const glyphs = [...bubbleGlyphs].sort(\n (a, b) => b.bubble.end - b.bubble.start - (a.bubble.end - a.bubble.start),\n )\n const rowEnd: number[] = []\n const labels = glyphs.flatMap(g => {\n const bx = (X(g.bubble.start) + X(g.bubble.end)) / 2\n if (bx < 0 || bx > width) {\n return []\n }\n const half = (g.label.length * LABEL_CHAR_PX) / 2\n // the top-right corner is the legend's\n const cx = Math.min(Math.max(bx, half + 4), width - LEGEND_PX - half)\n const row = rowEnd.findIndex(end => end < cx - half - 10)\n const at = row === -1 ? rowEnd.length : row\n if (at >= maxRows) {\n return []\n }\n rowEnd[at] = cx + half\n return [{ g, bx, cx, y: 14 + at * LABEL_ROW_PX }]\n })\n // glyphs take whatever the rows actually used leave above the line\n const room = Math.max(24, lineY - rowEnd.length * LABEL_ROW_PX - 14)\n\n return (\n <>\n {back}\n <svg\n style={svgStyle}\n width={width}\n height={canvasHeight}\n data-testid=\"graph-bubble-overlay\"\n >\n {glyphs.map(({ bubble, kind, label }) => {\n const x0 = X(bubble.start)\n const x1 = X(bubble.end)\n const w = Math.max(x1 - x0, MIN_GLYPH_PX)\n const cx = (x0 + x1) / 2\n const h = glyphHeight(bubble.longestAlleleLength, room)\n const color = BUBBLE_KIND_COLORS[kind]\n const refSpan = bubble.end - bubble.start\n const skipped = refSpan - bubble.shortestAlleleLength\n const dip = skipped > 0 ? 6 + 10 * Math.log10(1 + skipped) : 0\n const key = `${bubble.start}-${bubble.end}`\n return (\n <g key={key}>\n <path\n d={`M${cx - w / 2},${lineY} C${cx - w / 2},${lineY - h} ${cx + w / 2},${lineY - h} ${cx + w / 2},${lineY} Z`}\n fill={color}\n fillOpacity={0.18}\n stroke={color}\n strokeWidth={2}\n style={{ pointerEvents: 'auto', cursor: 'pointer' }}\n onClick={() => {\n void model.popBubble(bubble)\n }}\n >\n <title>{`${label}\\n${bubble.segmentCount} segments \u00B7 click to open`}</title>\n </path>\n {dip > 0 ? (\n <path\n d={`M${cx - w / 2},${lineY} C${cx - w / 2},${lineY + dip} ${cx + w / 2},${lineY + dip} ${cx + w / 2},${lineY}`}\n fill=\"none\"\n stroke={color}\n strokeWidth={2}\n strokeDasharray=\"4 3\"\n />\n ) : null}\n </g>\n )\n })}\n {labels.map(({ g, bx, cx, y }) => {\n const color = BUBBLE_KIND_COLORS[g.kind]\n const top = lineY - glyphHeight(g.bubble.longestAlleleLength, room)\n return (\n <g key={`${g.bubble.start}-${g.bubble.end}-label`}>\n <line\n x1={bx}\n x2={bx}\n y1={y + 4}\n y2={top - 2}\n stroke={color}\n strokeWidth={0.7}\n strokeOpacity={0.5}\n />\n <text\n x={cx}\n y={y}\n fontSize={11}\n fontFamily=\"sans-serif\"\n fill={color}\n textAnchor=\"middle\"\n >\n {g.label}\n </text>\n </g>\n )\n })}\n </svg>\n </>\n )\n})\n\nexport default BubbleOverlay\n", "import { observer } from 'mobx-react'\n\nimport LabelChip from './LabelChip'\nimport { LABEL_PX, placeLabels } from './overlayLabels'\n\nimport type { GraphGenomeViewModel } from '../model'\n\n// The session's genes drawn onto the graph: exons as dark stretches along the\n// backbone nodes that carry them, and each gene's name pinned under the\n// backbone at its midpoint. Drawn once in layout units and moved by the same\n// transform the halos use; the labels sit below the line where the halo\n// labels sit above it.\n\nconst svgStyle = {\n position: 'absolute' as const,\n left: 0,\n top: 0,\n pointerEvents: 'none' as const,\n overflow: 'hidden' as const,\n zIndex: 3,\n}\n\nconst EXON_COLOR = '#1c1c22'\n\nconst GenePins = observer(function GenePins({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { genePins } = model\n if (genePins.length === 0) {\n return null\n }\n const {\n scaleX,\n scaleY,\n translateX,\n translateY,\n width,\n canvasHeight,\n contigThickness,\n } = model\n const labels = placeLabels(\n [...genePins]\n .sort((a, b) => b.gene.end - b.gene.start - (a.gene.end - a.gene.start))\n .map(pin => ({\n item: pin,\n x: pin.at.x * scaleX + translateX,\n y: pin.at.y * scaleY + translateY + contigThickness + 18,\n text: pin.covered < 0.98 ? `${pin.gene.name} \u2026` : pin.gene.name,\n })),\n { width, height: canvasHeight },\n )\n\n return (\n <svg\n style={svgStyle}\n width={width}\n height={canvasHeight}\n data-testid=\"graph-gene-pins\"\n >\n <g\n transform={`translate(${translateX} ${translateY}) scale(${scaleX} ${scaleY})`}\n >\n {genePins.map(pin =>\n pin.exons ? (\n <path\n key={`${pin.gene.name}-${pin.gene.start}`}\n d={pin.exons}\n fill=\"none\"\n stroke={EXON_COLOR}\n strokeOpacity={0.9}\n strokeWidth={contigThickness * 0.55}\n strokeLinecap=\"round\"\n vectorEffect=\"non-scaling-stroke\"\n />\n ) : null,\n )}\n </g>\n {labels.map(({ item: pin, x, y, w, text }) => (\n <g key={`${pin.gene.name}-${pin.gene.start}-label`}>\n <line\n x1={x}\n x2={x}\n y1={y - LABEL_PX - 2}\n y2={pin.at.y * scaleY + translateY + contigThickness / 2}\n stroke={EXON_COLOR}\n strokeWidth={0.8}\n strokeOpacity={0.6}\n />\n <LabelChip\n x={x}\n y={y}\n w={w}\n text={text}\n color={EXON_COLOR}\n italic\n title={`${pin.gene.name} ${pin.gene.refName}:${pin.gene.start.toLocaleString()}-${pin.gene.end.toLocaleString()}${pin.covered < 0.98 ? ', runs past the cut' : ''}`}\n testId=\"graph-gene-pin-label\"\n />\n </g>\n ))}\n </svg>\n )\n})\n\nexport default GenePins\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M3 5v4h2V5h4V3H5c-1.1 0-2 .9-2 2m2 10H3v4c0 1.1.9 2 2 2h4v-2H5zm14 4h-4v2h4c1.1 0 2-.9 2-2v-4h-2zm0-16h-4v2h4v4h2V5c0-1.1-.9-2-2-2\"\n}), 'CropFree');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon([/*#__PURE__*/_jsx(\"path\", {\n d: \"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14\"\n}, \"0\"), /*#__PURE__*/_jsx(\"path\", {\n d: \"M12 10h-2v2H9v-2H7V9h2V7h1v2h2z\"\n}, \"1\")], 'ZoomIn');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14M7 9h5v1H7z\"\n}), 'ZoomOut');", "import CropFreeIcon from '@mui/icons-material/CropFree'\nimport ZoomInIcon from '@mui/icons-material/ZoomIn'\nimport ZoomOutIcon from '@mui/icons-material/ZoomOut'\nimport { IconButton, Tooltip, Typography } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport ColorSchemeSelect from './ColorSchemeSelect'\nimport FollowControl from './FollowControl'\nimport GraphStats from './GraphStats'\nimport LayoutSelect from './LayoutSelect'\nimport RepeatSelect from './RepeatSelect'\nimport SettingsMenu from './SettingsMenu'\nimport WalkSelect from './WalkSelect'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n toolbar: {\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n padding: '2px 4px',\n borderBottom: '1px solid #ddd',\n },\n})\n\nconst ZoomDisplay = observer(function ZoomDisplay({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n return <Typography variant=\"body2\">{model.zoomPercent}</Typography>\n})\n\nconst GraphToolbar = observer(function GraphToolbar({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n return (\n <div className={classes.toolbar}>\n <LayoutSelect model={model} />\n <ColorSchemeSelect model={model} />\n <WalkSelect model={model} />\n <RepeatSelect model={model} />\n <Tooltip title=\"Zoom in\">\n <IconButton\n size=\"small\"\n onClick={() => {\n model.zoom(1.5, model.width / 2, model.canvasHeight / 2)\n }}\n >\n <ZoomInIcon />\n </IconButton>\n </Tooltip>\n <Tooltip title=\"Zoom out\">\n <IconButton\n size=\"small\"\n onClick={() => {\n model.zoom(1 / 1.5, model.width / 2, model.canvasHeight / 2)\n }}\n >\n <ZoomOutIcon />\n </IconButton>\n </Tooltip>\n <Tooltip title=\"Zoom to fit\">\n <IconButton\n size=\"small\"\n onClick={() => {\n model.zoomToFit()\n }}\n >\n <CropFreeIcon />\n </IconButton>\n </Tooltip>\n <ZoomDisplay model={model} />\n <FollowControl model={model} />\n <GraphStats model={model} />\n <SettingsMenu model={model} />\n </div>\n )\n})\n\nexport default GraphToolbar\n", "import { FormControl, InputLabel, MenuItem, Select } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport { COLOR_SCHEMES } from '../colorSchemes'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n formControl: {\n minWidth: 100,\n },\n})\n\nconst ColorSchemeSelect = observer(function ColorSchemeSelect({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n return (\n <FormControl size=\"small\" className={classes.formControl}>\n <InputLabel>Color</InputLabel>\n <Select\n value={model.colorScheme}\n label=\"Color\"\n onChange={e => {\n model.setColorScheme(e.target.value)\n }}\n >\n {COLOR_SCHEMES.map(({ value, label }) => (\n <MenuItem key={value} value={value}>\n {label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n )\n})\n\nexport default ColorSchemeSelect\n", "import { Button, Typography } from '@mui/material'\nimport { observer } from 'mobx-react'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst FollowControl = observer(function FollowControl({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { followLinearView, followState } = model\n const note = followState.active ? model.followNote : undefined\n const state = !followLinearView\n ? 'Pinned'\n : followState.active\n ? (note ??\n `Following the linear view${model.cutTier === 'coarse' ? ', coarse tier' : ''}`)\n : followState.reason\n return model.loadedTrackId ? (\n <>\n <Button\n size=\"small\"\n onClick={() => {\n model.setFollowLinearView(!followLinearView)\n }}\n >\n {followLinearView ? 'Pin' : 'Follow'}\n </Button>\n {state ? (\n <Typography\n variant=\"caption\"\n color={note ? 'warning.main' : 'text.secondary'}\n data-testid=\"graph-follow-status\"\n >\n {state}\n </Typography>\n ) : null}\n </>\n ) : null\n})\n\nexport default FollowControl\n", "import { Typography } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n stats: {\n marginLeft: 'auto',\n },\n perf: {\n color: '#888',\n fontVariantNumeric: 'tabular-nums',\n },\n})\n\nfunction fmtMs(ms: number | undefined) {\n return ms === undefined ? '\u2013' : `${Math.round(ms)}ms`\n}\n\n// The perf READOUT, off by default: it sat in the toolbar of every published\n// graph figure, where one machine's fetch time is a number a reader has to work\n// out is not about them. `showPerf` is in the settings menu.\n//\n// The numbers themselves are not conditional \u2014 they ride the stats element's\n// data-* attributes either way (see GraphStats), so a browser test asserting a\n// timing budget never depends on a display setting, and turning the text off\n// costs no coverage. They live there rather than on this element so that\n// switching the text off leaves no empty node behind to carry them.\nconst GraphPerf = observer(function GraphPerf({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n const { lastFetchMs, lastLayoutMs, lastGeometryMs } = model\n const hasMetrics =\n lastFetchMs !== undefined ||\n lastLayoutMs !== undefined ||\n lastGeometryMs !== undefined\n return model.showPerf && hasMetrics ? (\n <Typography\n variant=\"caption\"\n className={classes.perf}\n data-testid=\"graph-perf-stats\"\n >\n fetch {fmtMs(lastFetchMs)} \u00B7 layout {fmtMs(lastLayoutMs)} \u00B7 geom{' '}\n {fmtMs(lastGeometryMs)}\n </Typography>\n ) : null\n})\n\nconst GraphStats = observer(function GraphStats({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n return (\n <div\n className={classes.stats}\n data-testid=\"graph-stats\"\n data-node-count={model.nodeCount}\n data-edge-count={model.edgeCount}\n data-path-count={model.pathCount}\n // unconditional, whatever `showPerf` is doing to the text beside them \u2014\n // see GraphPerf\n data-fetch-ms={model.lastFetchMs ?? ''}\n data-layout-ms={model.lastLayoutMs ?? ''}\n data-geometry-ms={model.lastGeometryMs ?? ''}\n data-geometry-strokes={model.lastGeometryStrokeCount ?? ''}\n data-follow-recuts={model.followRecuts}\n >\n <Typography variant=\"body2\" component=\"span\">\n {model.nodeCount} nodes, {model.edgeCount} edges\n {model.pathCount > 0 ? `, ${model.pathCount} paths` : ''}\n </Typography>{' '}\n <GraphPerf model={model} />\n </div>\n )\n})\n\nexport default GraphStats\n", "import {\n FormControl,\n InputLabel,\n MenuItem,\n Select,\n Tooltip,\n} from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport { LAYOUT_MODES } from '../layoutModes'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n formControl: {\n minWidth: 150,\n },\n})\n\nconst LayoutSelect = observer(function LayoutSelect({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n const { graph } = model\n return (\n <FormControl size=\"small\" className={classes.formControl}>\n <InputLabel>Layout</InputLabel>\n <Select\n value={model.layoutMode}\n label=\"Layout\"\n data-testid=\"graph-layout-select\"\n onChange={e => {\n model.setLayoutMode(e.target.value)\n void model.recomputeLayout()\n }}\n >\n {LAYOUT_MODES.map(({ value, label, description, available }) => (\n <MenuItem\n key={value}\n value={value}\n disabled={graph ? !available(graph) : false}\n >\n <Tooltip title={description} placement=\"right\">\n <span>{label}</span>\n </Tooltip>\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n )\n})\n\nexport default LayoutSelect\n", "import { FormControl, InputLabel, MenuItem, Select } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n formControl: {\n minWidth: 130,\n maxWidth: 240,\n },\n})\n\n// Which tandem repeat array the walk rows measure between and tile by, from\n// the session's repeat annotation over the window. Only the walk rows read it.\nconst RepeatSelect = observer(function RepeatSelect({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n const { repeatChoices } = model\n if (model.layoutMode !== 'walkrows' || repeatChoices.length === 0) {\n return null\n }\n const keys = new Set(repeatChoices.map(r => r.key))\n return (\n <FormControl size=\"small\" className={classes.formControl}>\n <InputLabel>Repeat</InputLabel>\n <Select\n value={keys.has(model.repeatKey) ? model.repeatKey : ''}\n label=\"Repeat\"\n data-testid=\"graph-repeat-select\"\n onChange={e => {\n model.setRepeatKey(e.target.value)\n }}\n >\n <MenuItem value=\"\">\n <em>Whole window</em>\n </MenuItem>\n {repeatChoices.map(({ key, name, unit }) => (\n <MenuItem key={key} value={key}>\n {name} \u00B7 {unit.toLocaleString()} bp unit\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n )\n})\n\nexport default RepeatSelect\n", "import { useState } from 'react'\n\nimport { CascadingMenuButton } from '@jbrowse/core/ui'\nimport DeleteIcon from '@mui/icons-material/Delete'\nimport MoreVertIcon from '@mui/icons-material/MoreVert'\nimport SettingsIcon from '@mui/icons-material/Settings'\nimport { observer } from 'mobx-react'\n\nimport GraphSettingsDialog from './GraphSettingsDialog'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst SettingsMenu = observer(function SettingsMenu({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const [settingsOpen, setSettingsOpen] = useState(false)\n\n return (\n <>\n <CascadingMenuButton\n size=\"small\"\n menuItems={[\n {\n type: 'checkbox' as const,\n label: 'Linear layout',\n checked: model.linearLayout,\n onClick: () => {\n model.setLinearLayout(!model.linearLayout)\n void model.recomputeLayout()\n },\n },\n {\n type: 'checkbox' as const,\n label: 'Show deletion edges',\n checked: model.showDeletionEdges,\n onClick: () => {\n model.setShowDeletionEdges(!model.showDeletionEdges)\n },\n },\n {\n type: 'checkbox' as const,\n label: 'Show timings',\n checked: model.showPerf,\n onClick: () => {\n model.setShowPerf(!model.showPerf)\n },\n },\n { type: 'divider' as const },\n {\n label: 'Settings',\n icon: SettingsIcon,\n onClick: () => {\n setSettingsOpen(true)\n },\n },\n { type: 'divider' as const },\n {\n label: 'Return to import form',\n icon: DeleteIcon,\n onClick: () => {\n model.clearGraph()\n },\n },\n ]}\n >\n <MoreVertIcon />\n </CascadingMenuButton>\n\n <GraphSettingsDialog\n model={model}\n open={settingsOpen}\n onClose={() => {\n setSettingsOpen(false)\n }}\n />\n </>\n )\n})\n\nexport default SettingsMenu\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z\"\n}), 'Delete');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2m0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2m0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2\"\n}), 'MoreVert');", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6\"\n}), 'Settings');", "import { Dialog } from '@jbrowse/core/ui'\nimport {\n DialogActions,\n DialogContent,\n FormControl,\n FormControlLabel,\n FormLabel,\n InputLabel,\n MenuItem,\n Radio,\n RadioGroup,\n Select,\n Switch,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport Button from '@mui/material/Button'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport { MAX_PATH_COLORS } from '../pathColors'\nimport SubgraphContextSelect from './SubgraphContextSelect'\nimport SubgraphHaplotypesField from './SubgraphHaplotypesField'\nimport { BUBBLE_SPREADS } from '../bubbleSpreads'\nimport { COLOR_SCHEMES } from '../colorSchemes'\nimport { NODE_WIDTHS } from '../nodeWidths'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n section: {\n marginBottom: 24,\n },\n formControl: {\n minWidth: 200,\n },\n})\n\nconst qualityLabels = ['Lowest', 'Low', 'Medium', 'High', 'Highest'] as const\n\n// Why a control is on and the drawing did not move. Layout quality and bubble\n// spread are the engine's own inputs, and an anchored layout places a node from\n// its coordinates without ever reaching the engine \u2014 so on those layouts these\n// two settings do nothing, which the user guide has to state in prose because\n// the dialog did not. Same reason and same shape as the `drawPaths` warning\n// below. Left enabled rather than disabled: the setting is real and is what the\n// force layout will be drawn with, so it is worth being able to choose it\n// before switching, and the dropdown that makes it live is one row up in the\n// toolbar.\nconst EngineOnly = observer(function EngineOnly({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n return model.hasGraph && !model.usesLayoutEngine ? (\n <Typography variant=\"caption\" color=\"warning.main\">\n No effect on this layout: only the force-directed layout is drawn by the\n engine that reads this.\n </Typography>\n ) : null\n})\n\nconst GraphSettingsDialog = observer(function GraphSettingsDialog(props: {\n model: GraphGenomeViewModel\n open: boolean\n onClose: () => void\n}) {\n const { model, open, onClose } = props\n const { classes } = useStyles()\n\n return (\n <Dialog open={open} onClose={onClose} title=\"Graph settings\">\n <DialogContent>\n <div className={classes.section}>\n <FormControl fullWidth>\n <FormLabel component=\"legend\">Layout quality</FormLabel>\n <RadioGroup\n value={model.layoutQuality}\n onChange={e => {\n const quality = parseInt(e.target.value)\n model.setLayoutQuality(quality)\n void model.recomputeLayout()\n }}\n >\n {qualityLabels.map((label, i) => (\n <FormControlLabel\n key={label}\n value={i}\n control={<Radio />}\n label={label}\n />\n ))}\n </RadioGroup>\n <Typography variant=\"caption\" color=\"text.secondary\">\n FMMM&apos;s iteration budget, the same scale Bandage exposes.\n Higher is slower: on a thousand-node cut the top setting is\n seconds rather than tenths.\n </Typography>\n <EngineOnly model={model} />\n </FormControl>\n </div>\n\n <div className={classes.section}>\n <FormControlLabel\n control={\n <Switch\n checked={model.drawPaths}\n onChange={e => {\n model.setDrawPaths(e.target.checked)\n }}\n />\n }\n label=\"Draw paths\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n Color each node and connector by the paths through it, one lane per\n path in legend order, so a path that skips a node leaves its lane\n empty\n </Typography>\n {/* Why the switch is on and nothing changed. Past MAX_PATH_COLORS the\n hues are a degree apart and the key is taller than the drawing, so\n the setting resolves to off (see effectiveDrawPaths) \u2014 and a\n control that silently does nothing is worse than one that says it\n cannot. Left switchable rather than disabled: the graph can be\n recut to fewer paths without touching this dialog. */}\n {model.drawPaths && !model.effectiveDrawPaths ? (\n <Typography variant=\"caption\" color=\"warning.main\">\n {model.pathCount > MAX_PATH_COLORS\n ? `Off: ${model.pathCount.toLocaleString()} paths is past the ${MAX_PATH_COLORS} this can tell apart`\n : 'Off: this graph states no paths'}\n </Typography>\n ) : null}\n </div>\n\n <div className={classes.section}>\n <FormControlLabel\n control={\n <Switch\n checked={model.showBubbles}\n onChange={e => {\n model.setShowBubbles(e.target.checked)\n }}\n />\n }\n label=\"Mark bubbles\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n A halo along the nodes of each bubble, labelled by what it is, with\n the label opening the bubble on its own\n </Typography>\n </div>\n\n <div className={classes.section}>\n <FormControlLabel\n control={\n <Switch\n checked={model.showGenes}\n onChange={e => {\n model.setShowGenes(e.target.checked)\n }}\n />\n }\n label=\"Genes on the backbone\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n Exons along the reference nodes that carry them and each gene's name\n at its midpoint, from the assembly's annotation track\n </Typography>\n {model.geneTrackChoices.length > 1 ? (\n <FormControl className={classes.formControl} sx={{ mt: 1 }}>\n <InputLabel>Gene track</InputLabel>\n <Select\n value={model.geneTrack?.trackId ?? ''}\n label=\"Gene track\"\n data-testid=\"graph-gene-track-select\"\n onChange={e => {\n model.setGeneTrackId(e.target.value)\n void model.reloadGenes()\n }}\n >\n {model.geneTrackChoices.map(({ trackId, name }) => (\n <MenuItem key={trackId} value={trackId}>\n {name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n ) : null}\n {model.repeatTrackChoices.length > 1 ? (\n <FormControl className={classes.formControl} sx={{ mt: 1 }}>\n <InputLabel>Repeat track</InputLabel>\n <Select\n value={model.repeatTrack?.trackId ?? ''}\n label=\"Repeat track\"\n data-testid=\"graph-repeat-track-select\"\n onChange={e => {\n model.setRepeatTrackId(e.target.value)\n void model.reloadRepeats()\n }}\n >\n {model.repeatTrackChoices.map(({ trackId, name }) => (\n <MenuItem key={trackId} value={trackId}>\n {name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n ) : null}\n </div>\n\n {model.anchorPaths.length > 1 ? (\n <div className={classes.section}>\n <FormControl className={classes.formControl}>\n <InputLabel>Reference path</InputLabel>\n <Select\n value={model.activeReferencePath ?? ''}\n label=\"Reference path\"\n data-testid=\"graph-reference-path-select\"\n onChange={e => {\n model.setReferencePath(e.target.value)\n void model.recomputeLayout()\n }}\n >\n {model.anchorPaths.map(({ name }) => (\n <MenuItem key={name} value={name}>\n {name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n <Typography variant=\"caption\" color=\"text.secondary\">\n Which path the anchored layouts draw x against. A GFA with no rGFA\n tags states its coordinates only in its paths, and marks none of\n them as the reference.\n </Typography>\n </div>\n ) : null}\n\n <div className={classes.section}>\n <FormControl className={classes.formControl}>\n <InputLabel>Bubble spread</InputLabel>\n <Select\n value={model.bubbleSpread}\n label=\"Bubble spread\"\n data-testid=\"graph-bubble-spread-select\"\n onChange={e => {\n model.setBubbleSpread(e.target.value)\n void model.recomputeLayout()\n }}\n >\n {BUBBLE_SPREADS.map(({ value, label, description }) => (\n <MenuItem key={value} value={value}>\n <Tooltip title={description} placement=\"right\">\n <span>{label}</span>\n </Tooltip>\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n <Typography variant=\"caption\" color=\"text.secondary\">\n How far the force layout opens a bubble. A pangenome allele is a few\n bp, so at Bandage&apos;s own scale both arms land inside one node\n thickness and the graph draws as a rope.\n </Typography>\n <EngineOnly model={model} />\n </div>\n\n <div className={classes.section}>\n <FormControl className={classes.formControl}>\n <InputLabel>Node width</InputLabel>\n <Select\n value={model.nodeWidth}\n label=\"Node width\"\n data-testid=\"graph-node-width-select\"\n onChange={e => {\n model.setNodeWidth(e.target.value)\n }}\n >\n {NODE_WIDTHS.map(({ value, label, description }) => (\n <MenuItem key={value} value={value}>\n <Tooltip title={description} placement=\"right\">\n <span>{label}</span>\n </Tooltip>\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n <Typography variant=\"caption\" color=\"text.secondary\">\n Bandage&apos;s depth as width. In a cut with walks, depth is how\n many haplotypes carry the node.\n </Typography>\n </div>\n\n <SubgraphContextSelect model={model} />\n\n <SubgraphHaplotypesField model={model} />\n\n <div className={classes.section}>\n <FormControl className={classes.formControl}>\n <InputLabel>Color scheme</InputLabel>\n <Select\n value={model.colorScheme}\n label=\"Color scheme\"\n onChange={e => {\n model.setColorScheme(e.target.value)\n }}\n >\n {COLOR_SCHEMES.map(({ value, label }) => (\n <MenuItem key={value} value={value}>\n {label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </div>\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"contained\" color=\"primary\" onClick={onClose}>\n Close\n </Button>\n </DialogActions>\n </Dialog>\n )\n})\n\nexport default GraphSettingsDialog\n", "import {\n FormControl,\n InputLabel,\n MenuItem,\n Select,\n Typography,\n} from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n section: {\n marginBottom: 24,\n },\n formControl: {\n minWidth: 200,\n },\n})\n\n// Hops past the region's own segments, each costing a tabix query per\n// off-reference segment already reached. One is the default: at none a single\n// detour draws as two unrelated stubs, which is a wrong picture of the graph\n// rather than a cheaper one. Two exists for a graph whose alleles have alleles\n// (HPRC's amylase window keeps growing at 2, the E. coli paa locus does not), and\n// it stops there because a frontier is still not a bubble decomposition. See\n// subgraphContext in the model, and cut an exact slice with gfatools when that is\n// what is wanted.\nconst SUBGRAPH_CONTEXTS = [\n { value: 0, label: 'None' },\n { value: 1, label: '1 hop' },\n { value: 2, label: '2 hops' },\n]\n\n// Only a graph cut from a track has a cut to widen: a file-loaded graph is\n// already whatever its file holds, and re-cutting it means nothing.\nconst SubgraphContextSelect = observer(function SubgraphContextSelect({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n return model.loadedTrackId ? (\n <div className={classes.section}>\n <FormControl className={classes.formControl}>\n <InputLabel>Graph context</InputLabel>\n <Select\n value={model.subgraphContext}\n label=\"Graph context\"\n data-testid=\"graph-context-select\"\n onChange={e => {\n model.setSubgraphContext(e.target.value)\n void model.reloadSubgraph()\n }}\n >\n {SUBGRAPH_CONTEXTS.map(({ value, label }) => (\n <MenuItem key={value} value={value}>\n {label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n <Typography variant=\"caption\" color=\"text.secondary\">\n How far the cut follows links out of the region, one hop by default. A\n detour that leaves the reference before the window and rejoins after it\n is indexed under its own sequence, so at none its middle is missing and\n the one bubble draws as two unrelated stubs. Each hop costs a query per\n off-reference segment already reached.\n </Typography>\n </div>\n ) : null\n})\n\nexport default SubgraphContextSelect\n", "import { useState } from 'react'\n\nimport { TextField, Typography } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n section: {\n marginBottom: 24,\n },\n})\n\nexport function parseHaplotypeList(text: string) {\n const names = text\n .split(/[\\s,]+/)\n .map(name => name.trim())\n .filter(name => name !== '')\n return names.length === 0 ? undefined : names\n}\n\nconst HaplotypeListField = observer(function HaplotypeListField({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const [draft, setDraft] = useState(model.subgraphHaplotypes?.join(', ') ?? '')\n const apply = () => {\n const parsed = parseHaplotypeList(draft)\n const unchanged =\n parsed?.join('\\n') === model.subgraphHaplotypes?.join('\\n')\n if (!unchanged) {\n model.setSubgraphHaplotypes(parsed)\n void model.reloadSubgraph()\n }\n }\n return (\n <TextField\n fullWidth\n label=\"Haplotypes\"\n placeholder=\"every haplotype\"\n value={draft}\n slotProps={{ htmlInput: { 'data-testid': 'graph-haplotypes-field' } }}\n onChange={e => {\n setDraft(e.target.value)\n }}\n onBlur={() => {\n apply()\n }}\n onKeyDown={e => {\n if (e.key === 'Enter') {\n apply()\n }\n }}\n />\n )\n})\n\n// Only a graph cut from a track has a set to choose; a file holds whatever\n// walks it holds. The inner field is keyed on the model's set so an outside\n// change (a restored session, a launch) resets the draft rather than fighting\n// it.\nconst SubgraphHaplotypesField = observer(function SubgraphHaplotypesField({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n return model.loadedTrackId ? (\n <div className={classes.section}>\n <HaplotypeListField\n key={model.subgraphHaplotypes?.join(',') ?? ''}\n model={model}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n The haplotypes the cut is for, as lane assembly names or PanSN prefixes\n (HG002#1, or HG002 for both), separated by commas. The cut keeps their\n walks and the nodes those walks visit, with the reference. Empty is\n every haplotype. Only a gbz-base track reads this.\n </Typography>\n </div>\n ) : null\n})\n\nexport default SubgraphHaplotypesField\n", "import { FormControl, InputLabel, MenuItem, Select } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n formControl: {\n minWidth: 130,\n maxWidth: 220,\n },\n})\n\n// Which walk to lift out of the drawing: Bandage's path highlight, with the\n// haplotypes a GBZ cut carries. Absent from a graph with no walks.\nconst WalkSelect = observer(function WalkSelect({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n const { walkChoices } = model\n if (walkChoices.length === 0) {\n return null\n }\n const names = new Set(walkChoices.map(c => c.name))\n return (\n <FormControl size=\"small\" className={classes.formControl}>\n <InputLabel>Walk</InputLabel>\n <Select\n value={names.has(model.highlightedPath) ? model.highlightedPath : ''}\n label=\"Walk\"\n data-testid=\"graph-walk-select\"\n onChange={e => {\n model.setHighlightedPath(e.target.value)\n }}\n >\n <MenuItem value=\"\">\n <em>None</em>\n </MenuItem>\n {walkChoices.map(({ name, label }) => (\n <MenuItem key={name} value={name}>\n {label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n )\n})\n\nexport default WalkSelect\n", "import { observer } from 'mobx-react'\n\nimport { LABEL_CHAR_PX } from './overlayLabels'\nimport { ROW_HEIGHT_PX } from '../layout/rowSpacing'\nimport { CALL_TOLERANCE } from '../repeats/walkCalls'\n\nimport type { GraphGenomeViewModel } from '../model'\n\n// The walk-rows layout's bars: one per haplotype walk under the reference line\n// the canvas draws, each on its own bp axis from the window's left edge. Blue\n// is sequence the reference walk also carries, purple is sequence it does not,\n// and the readout at the end of a bar is what it carries against the\n// reference, which for a repeat array is the expansion.\n//\n// A tick is the genotyped allele length paired with THAT walk, and the walk\n// rows pair them \u2014 see repeats/walkCalls.ts, which also holds the threshold\n// this reads a red readout off.\n\nconst svgStyle = {\n position: 'absolute' as const,\n left: 0,\n top: 0,\n pointerEvents: 'none' as const,\n overflow: 'visible' as const,\n zIndex: 3,\n}\n\nconst ON_REFERENCE = '#2f8fd6'\nconst CALL_TICK = '#111'\nconst UNBACKED_TICK = '#9e9e9e'\nconst DISAGREES = '#c62828'\nconst OFF_REFERENCE = '#8e3fbf'\nconst BAR_PX = 12\n\nconst legendBoxStyle = {\n background: 'rgba(255,255,255,0.82)',\n padding: '4px 6px',\n borderRadius: 3,\n fontSize: 11,\n lineHeight: '15px',\n whiteSpace: 'nowrap' as const,\n}\nconst legendRowStyle = { display: 'flex', alignItems: 'center', gap: 5 }\nconst swatchStyle = { width: 18, height: BAR_PX - 4, borderRadius: 2 }\nconst tickSwatchStyle = {\n ...swatchStyle,\n display: 'flex',\n justifyContent: 'center',\n}\n\nfunction TickSwatch({ color }: { color: string }) {\n return (\n <div style={tickSwatchStyle}>\n <div style={{ width: 2, backgroundColor: color }} />\n </div>\n )\n}\n\n// What the bar colours and the ticks mean, in the legend stack with the other\n// keys. The reference row is named, so \"the reference\" here reads as that row.\n// Each tick row appears once the rows hold one, so a catalogue with no\n// genotypes keeps the two-colour key it had.\nexport const WalkRowsLegend = observer(function WalkRowsLegend({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const bars = model.walkRowBars\n if (!bars) {\n return null\n }\n const calls = bars.rows.flatMap(row => row.call ?? [])\n return (\n <div style={legendBoxStyle} data-testid=\"graph-walk-rows-legend\">\n <div style={legendRowStyle}>\n <div style={{ ...swatchStyle, backgroundColor: ON_REFERENCE }} />\n <span>sequence {bars.reference.label} also carries</span>\n </div>\n <div style={legendRowStyle}>\n <div style={{ ...swatchStyle, backgroundColor: OFF_REFERENCE }} />\n <span>sequence it does not</span>\n </div>\n {calls.some(call => call.spanningReads !== 0) ? (\n <div style={legendRowStyle}>\n <TickSwatch color={CALL_TICK} />\n <span>allele length called for this walk</span>\n </div>\n ) : null}\n {calls.some(call => call.spanningReads === 0) ? (\n <div style={legendRowStyle}>\n <TickSwatch color={UNBACKED_TICK} />\n <span>called with no read spanning it</span>\n </div>\n ) : null}\n {calls.some(call => call.agrees === false) ? (\n <div style={legendRowStyle}>\n <div style={swatchStyle} />\n <span style={{ color: DISAGREES }}>\n walk and call over {Math.round(CALL_TOLERANCE * 100)}% apart\n </span>\n </div>\n ) : null}\n </div>\n )\n})\n\nfunction kb(bp: number) {\n return `${(bp / 1000).toFixed(bp < 10_000 ? 1 : 0)} kb`\n}\n\nfunction units(bp: number, unit: number | undefined) {\n return unit ? ` \u2248 ${Math.round(bp / unit)} units` : ''\n}\n\nfunction readout(\n bp: number,\n referenceBp: number,\n complete: boolean,\n unit: number | undefined,\n) {\n const delta = bp - referenceBp\n const against =\n delta === 0 ? '' : ` (${delta > 0 ? '+' : '\u2212'}${kb(Math.abs(delta))})`\n return `${kb(bp)}${units(bp, unit)}${against}${complete ? '' : ' \u00B7 partial walk'}`\n}\n\nfunction calledReadout(call: { bp: number; spanningReads?: number }) {\n const unbacked = call.spanningReads === 0 ? ' \u00B7 no spanning read' : ''\n return ` \u00B7 called ${kb(call.bp)}${unbacked}`\n}\n\n// One separator per unit along a bar, so copies are countable, dropped when a\n// unit is under a few px. The reference bar is the canvas's, so it gets none.\nconst MIN_TILE_PX = 3\n\nfunction tileSeparators(bp: number, unit: number, X: (bp: number) => number) {\n if (X(unit) - X(0) < MIN_TILE_PX) {\n return []\n }\n const xs: number[] = []\n for (let k = unit; k < bp; k += unit) {\n xs.push(k)\n }\n return xs\n}\n\nconst WalkRowsOverlay = observer(function WalkRowsOverlay({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { walkRowBars } = model\n if (!walkRowBars) {\n return null\n }\n const { scaleX, scaleY, translateX, translateY, width, canvasHeight } = model\n const X = (bp: number) => bp * scaleX + translateX\n const Y = (row: number) => row * ROW_HEIGHT_PX * scaleY + translateY\n const { origin, unit, reference, rows } = walkRowBars\n // The backbone the canvas draws spans the cut window, which reaches past a\n // selected array on both sides, so the reference readout sits after it.\n const backboneEnd = Math.max(\n origin + reference.bp,\n model.loadedRegion?.end ?? 0,\n )\n // A readout that would leave the pane is written inside the end of its bar.\n const label = (text: string, endBp: number, y: number, fill = '#333') => {\n const x = X(endBp) + 6\n const fits = x + text.length * LABEL_CHAR_PX < width\n return (\n <text\n x={fits ? x : X(endBp) - 6}\n y={y + 4}\n fontSize={11}\n fontFamily=\"sans-serif\"\n fill={fill}\n stroke={fits ? undefined : 'white'}\n strokeWidth={fits ? undefined : 3}\n paintOrder=\"stroke\"\n textAnchor={fits ? 'start' : 'end'}\n >\n {text}\n </text>\n )\n }\n return (\n <svg\n style={svgStyle}\n width={width}\n height={canvasHeight}\n data-testid=\"graph-walk-rows\"\n >\n {label(\n `${kb(reference.bp)}${units(reference.bp, unit)}`,\n backboneEnd,\n Y(0),\n )}\n {rows.map((row, i) => {\n const y = Y(i + 1)\n if (y < -BAR_PX || y > canvasHeight + BAR_PX) {\n return null\n }\n const { call } = row\n return (\n <g key={row.name} data-testid=\"graph-walk-row\">\n {row.runs.map(run => (\n <rect\n key={run.start}\n x={X(origin + run.start)}\n y={y - BAR_PX / 2}\n width={Math.max(1, run.bp * scaleX)}\n height={BAR_PX}\n fill={run.onReference ? ON_REFERENCE : OFF_REFERENCE}\n />\n ))}\n {unit\n ? tileSeparators(row.bp, unit, bp => bp * scaleX).map(k => (\n <line\n key={k}\n x1={X(origin + k)}\n x2={X(origin + k)}\n y1={y - BAR_PX / 2}\n y2={y + BAR_PX / 2}\n stroke=\"white\"\n strokeWidth={1}\n />\n ))\n : null}\n {call ? (\n <rect\n data-testid=\"graph-walk-call\"\n x={X(origin + call.bp) - 1}\n y={y - BAR_PX / 2 - 3}\n width={2}\n height={BAR_PX + 6}\n fill={call.spanningReads === 0 ? UNBACKED_TICK : CALL_TICK}\n />\n ) : null}\n {label(\n `${readout(row.bp, reference.bp, row.complete, unit)}${call ? calledReadout(call) : ''}`,\n origin + row.bp,\n y,\n call?.agrees === false ? DISAGREES : undefined,\n )}\n </g>\n )\n })}\n </svg>\n )\n})\n\nexport default WalkRowsOverlay\n", "import { deletionArcCurves } from './deletionEdges'\nimport { polylineMidpoint } from './layout/mergeRuns'\nimport { curveBounds, curveMidpoint } from './util/geometry'\n\nimport type { DeletionEdge } from './deletionEdges'\nimport type { AlleleDeletion, NodeSegment } from './types'\nimport type { AxisScale, BezierCurve } from './util/geometry'\n\n// What to write on the drawing, and where. Bandage labels a node with its name,\n// its length or its depth; a segment id means nothing to a reader of a pangenome\n// (\"s462766\"), so the label here is the one fact that carries the story \u2014 how\n// much sequence the alternative is worth. A node says its length, a deletion arc\n// says what it skips.\n//\n// **Every length written here is positive, and a deletion says it is one.**\n// A deletion arc used to be labelled \"\u221294.2 kb\", and a review read that as the\n// node's sequence length being negative, which is the only thing a bare signed\n// number beside a graph can mean. \"skips 94.2 kb\" replaced it and left a reader\n// asking 94.2 kb of what; \"skips 94.2 kb of reference\" answered that and still\n// drew \"it is sort of unclear what 'skips X bp of reference' means. is there any\n// better wording?\" on a third round. So the label now names the event rather\n// than describing the arc's mechanics: \"94.2 kb del\" abbreviates the term a\n// reader already has for reference sequence a haplotype does not carry, and\n// against the reference is what a deletion is measured from by default.\n//\n// **Two rules decide which labels there are, and both are about the drawing\n// rather than about a threshold on the graph.**\n//\n// A label has to fit the node it names, within a factor of two: past that it is\n// a label with a node attached rather than a node with a label. The rule used to\n// be a flat minimum drawn length (46 screen px) regardless of how long the text\n// was, which reads as arbitrary from the outside \u2014 review asked why some nodes\n// state a length and others do not, and nothing in the picture could answer it.\n// Measured against the text, the answer is in the picture: the node is shorter\n// than the words.\n//\n// And a label is dropped when its box would overlap one already placed. Labels\n// go biggest-sequence-first, so what survives a crowd is the allele that\n// matters; this is also what fixes labels printing on top of each other (\"\u221210\n// kb3.2 kb\" in the amylase figure).\n//\n// Between them they hold at both ends of the scale a pangenome graph spans: 63\n// kb-scale nodes at the amylase locus nearly all carry a length, and a 154-node\n// base-level pggb graph of 1-100 bp segments carries almost none \u2014 where every\n// label would have said \"1 bp\" and covered the segment it named.\n//\n// Collision is measured in SCREEN pixels, because that is where the text has its\n// size: the same two labels clear each other zoomed in and collide zoomed out,\n// so a wide view sheds labels on its own. This is why there is no \"show labels\"\n// toggle \u2014 a control whose only job is to undo a bad default is a bad default.\n\n// A deletion is worth saying even when its arc is small, because the arc is the\n// only thing on screen that represents it; but not when it is a dot.\n//\n// Measured on the arc's DRAWN EXTENT (the larger side of its bounds, in screen\n// px), not on its bulge. Bulge is in whatever units the layout works in: under\n// FMMM it is a fraction of the bypassed nodes' drawn length at the engine's own\n// scale, and in an anchored layout it is a fraction of their length in bp. The\n// same deletion therefore cleared this in one layout and not the other, which is\n// how `pangenome/hprc_cfhr_deletion` lost the labels on its 2.2 kb and 9.3 kb\n// arcs by changing nothing but the layout. Extent is what a reader sees either\n// way, and it is the same quantity the node rule below uses.\nconst MIN_DELETION_LABEL_PX = 26\n\n// One node label per this much pane, and never fewer than a handful: at a\n// 1400 by 600 pane that is about twenty.\nconst PX_PER_NODE_LABEL = 40_000\nconst MIN_NODE_LABELS = 8\n\n// A deletion clearing that floor still need not be able to CARRY its label: the\n// text is a fixed 26 characters however small the event's arc is, so at the LPA\n// KIV-2 locus `skips 27.7 kb of reference` came out four times the width of the\n// arc it was centred on, reading as a caption dropped on the bubble beside it\n// rather than as that arc's name (`pangenome/hprc_lpa_kiv2`, reviewed\n// 2026-07-31).\n//\n// A node in the same position simply loses its label, and that is right for a\n// node \u2014 the tube is still drawn, still coloured, still hoverable, and the graph\n// is full of them. It is wrong for a deletion, which is one dashed curve\n// standing for sequence that is not in the picture at all: dropped, nothing\n// tells a reader the arc is an event rather than another link.\n//\n// So the label moves off the arc instead and takes a leader line with it. It is\n// displaced along the direction the arc bows \u2014 which is the side the bulge was\n// drawn INTO, so it is the open side by construction \u2014 far enough that its box\n// clears the curve, and the leader states the association the proximity no\n// longer does.\nconst LEADER_GAP_PX = 8\n\n// The label's own box, for collision. Mirrors GraphCanvas's nodeLabelStyle:\n// 10px text in 3px of horizontal padding, on a 13px line. Character width is an\n// average rather than a measurement \u2014 placement runs on every pan and zoom, and\n// a canvas text metric per candidate costs more than the occasional\n// half-character of slack is worth.\nconst LABEL_CHAR_PX = 5.7\nconst LABEL_PAD_PX = 6\nconst LABEL_HEIGHT_PX = 13\n// Breathing room, so two labels that merely touch still read as two.\nconst LABEL_GAP_PX = 3\n// How much wider than its node a label may be before it is the label that is\n// being drawn rather than the node.\nconst MAX_LABEL_OVERHANG = 2\n\nexport interface Box {\n left: number\n right: number\n top: number\n bottom: number\n}\n\n// Half the box a piece of label text occupies. Split out of `labelBox` because\n// the width is wanted before there is anywhere to put the box: a displaced\n// deletion label is clamped by its own half-width, and the node pass below\n// rejects on size before it has a position at all.\nfunction labelHalfWidth(text: string) {\n return (text.length * LABEL_CHAR_PX + LABEL_PAD_PX + LABEL_GAP_PX) / 2\n}\n\n// Constant whatever the label says, so a candidate's vertical cull costs\n// nothing and can be taken before its text exists.\nconst LABEL_HALF_HEIGHT = (LABEL_HEIGHT_PX + LABEL_GAP_PX) / 2\n\n// The narrowest box `formatBp` can ask for, `1 bp`. A node drawn shorter than\n// this cannot carry ANY label, so the check happens before the length is\n// formatted: on a 7,499-node cut nearly every node fails it, and building a\n// string and a box per node only to discard both was most of this pass\n// (measured 37 ms per frame, and this pass runs on every pan).\nconst MIN_LABEL_HALF_WIDTH = labelHalfWidth('1 bp')\n\n// translate(-50%, -50%) in the style, so the anchor is the box's centre.\nfunction boxAt(halfW: number, x: number, y: number): Box {\n return {\n left: x - halfW,\n right: x + halfW,\n top: y - LABEL_HALF_HEIGHT,\n bottom: y + LABEL_HALF_HEIGHT,\n }\n}\n\n// A box outside the canvas is dropped rather than clipped, and dropped BEFORE\n// the collision test, so a label nobody can see cannot hold space against one\n// inside the frame.\nfunction onScreen(box: Box, width: number, height: number) {\n return box.right > 0 && box.left < width && box.bottom > 0 && box.top < height\n}\n\n// The box a row label occupies, from the same metrics RowLabels renders with\n// (11px text, 4px of horizontal padding, a 16px line, centred on its row). Lives\n// here rather than in the component so the two halves of the collision test\n// cannot drift apart.\nexport function rowLabelBox(text: string, screenY: number): Box {\n return {\n left: 6,\n right: 6 + text.length * 6.2 + 8 + LABEL_GAP_PX,\n top: screenY - 8 - LABEL_GAP_PX / 2,\n bottom: screenY + 8 + LABEL_GAP_PX / 2,\n }\n}\n\n// Slide a value into [lo, hi], leaving it alone when the range is empty \u2014 a box\n// wider than the canvas has nowhere to fit, and pinning it to one edge is worse\n// than the overflow.\nfunction clamp(v: number, lo: number, hi: number) {\n return hi < lo ? v : Math.min(Math.max(v, lo), hi)\n}\n\nfunction overlaps(a: Box, b: Box) {\n return (\n a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom\n )\n}\n\n// A displaced label's tether, in the same screen pixels the label is placed in.\n// Both ends are computed here rather than in the component, because the label's\n// end is the point where its own box meets the line \u2014 geometry the placement\n// already has and the painter would have to rederive. It stops at the box edge\n// rather than at the centre because the label's background is translucent, so a\n// line run under it shows through the text.\nexport interface Leader {\n arcX: number\n arcY: number\n labelX: number\n labelY: number\n}\n\nexport interface GraphLabel {\n key: string\n text: string\n // screen pixels, already through the transform the canvas draws with: the\n // collision test needs them anyway, and returning layout units would have the\n // caller redo the arithmetic that decided which labels there are\n x: number\n y: number\n kind: 'node' | 'deletion'\n // present only on a label that had to move off the thing it names\n leader?: Leader\n}\n\n// bp in the unit that reads. Deliberately coarse: a label is a glance, and\n// \"12,345 bp\" costs twice the width of \"12 kb\" to say the same thing at the\n// scale a pangenome allele lives at.\nexport function formatBp(bp: number) {\n if (bp >= 1_000_000) {\n return `${+(bp / 1_000_000).toFixed(1)} Mb`\n }\n return bp >= 1000 ? `${+(bp / 1000).toFixed(1)} kb` : `${bp} bp`\n}\n\nfunction deletionText(bp: number) {\n return `${formatBp(bp)} del`\n}\n\n// Which way the arc bows, as a unit vector: its chord's midpoint towards its\n// apex. A deletion's bulge is perpendicular to the chord by construction, so\n// this points away from the backbone the arc leaves and rejoins \u2014 the side the\n// curve was opened into, and the side with room in it.\n//\n// Taken on SCREEN, which is where the label it aims has to clear the curve. That\n// used to be free \u2014 with one scale for both axes the transform is a similarity,\n// so a layout direction is a screen direction \u2014 and on a row layout it is not:\n// the two axes are bp and px and the arc leaves at a different angle in each.\nfunction arcOutward(\n curves: BezierCurve[],\n apex: { x: number; y: number },\n scaleX: number,\n scaleY: number,\n) {\n const first = curves[0]!\n const last = curves[curves.length - 1]!\n const dx = (apex.x - (first.x0 + last.x1) / 2) * scaleX\n const dy = (apex.y - (first.y0 + last.y1) / 2) * scaleY\n const len = Math.hypot(dx, dy)\n // A curve with no bow has no outward side; up is as good as any, and this is\n // unreachable for a deletion, whose bulge is what gives it extent at all.\n return len === 0 ? { x: 0, y: -1 } : { x: dx / len, y: dy / len }\n}\n\n// How far the box's own boundary is from its centre in a given direction \u2014 the\n// support function of an axis-aligned rectangle. Displacing a label by this plus\n// a gap puts its near EDGE that gap away from the point, whatever angle the\n// leader leaves at, which a single radius cannot do for a box six times wider\n// than it is tall.\nfunction boxSupport(box: Box, dir: { x: number; y: number }) {\n return (\n (Math.abs(dir.x) * (box.right - box.left)) / 2 +\n (Math.abs(dir.y) * (box.bottom - box.top)) / 2\n )\n}\n\n// Where a ray along `dir` through the box's centre crosses the box, again from\n// the centre \u2014 which is where a leader coming in along that ray has to stop.\n//\n// This is not `boxSupport`, and the difference is the whole leader: support is\n// the distance to the supporting line perpendicular to `dir`, which a box six\n// times wider than it is tall touches at a corner, so a diagonal approach hits\n// the box's real edge four or five times sooner. Stopping the line at the\n// support distance drew an 8px stub at the arc with 50px of white between it and\n// the text it was supposed to tie to.\nfunction boxRayCrossing(box: Box, dir: { x: number; y: number }) {\n const halfW = (box.right - box.left) / 2\n const halfH = (box.bottom - box.top) / 2\n return Math.min(\n dir.x === 0 ? Infinity : halfW / Math.abs(dir.x),\n dir.y === 0 ? Infinity : halfH / Math.abs(dir.y),\n )\n}\n\n// The node's own extent along its polyline, IN SCREEN PX \u2014 what the label has\n// to fit inside. Measured through the transform rather than in layout units and\n// scaled afterwards, because on a row layout the two axes are in different units\n// and there is no single number to scale a mixed length by.\nfunction drawnLength(segments: NodeSegment[], scaleX: number, scaleY: number) {\n let total = 0\n for (let i = 1; i < segments.length; i++) {\n const a = segments[i - 1]!\n const b = segments[i]!\n total += Math.hypot((b.x - a.x) * scaleX, (b.y - a.y) * scaleY)\n }\n return total\n}\n\n// The extent of a run's drawn points, in layout units. One pass rather than\n// four `Math.min(...points.map(...))` spreads over the same array: those\n// allocate a mapped copy per axis, and a run long enough \u2014 a chain of alleles\n// across a whole-file import \u2014 overflows the argument limit and throws\n// RangeError instead of labelling anything. Same reason `backboneSpan` is a\n// fold.\nfunction runBounds(\n nodeIds: string[],\n nodePositions: Record<string, NodeSegment[]>,\n) {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const id of nodeIds) {\n for (const p of nodePositions[id] ?? []) {\n minX = Math.min(minX, p.x)\n maxX = Math.max(maxX, p.x)\n minY = Math.min(minY, p.y)\n maxY = Math.max(maxY, p.y)\n }\n }\n return minX === Infinity ? undefined : { minX, minY, maxX, maxY }\n}\n\n// Both caches below are keyed the way hit detection keys its spatial indexes: a\n// WeakMap on the positions object, so two graph views cannot evict each other\n// and an entry dies with the layout it describes, plus an explicit check of\n// everything the value actually depends on. A node drag mutates the positions\n// in place, so their identity cannot report that they moved \u2014 hence `version`,\n// which the model bumps on exactly that (`positionsVersion`).\n//\n// What makes the caching worth having is that this whole pass reruns on every\n// pan: the labels are placed in screen space, so a translate changes where they\n// go, and neither the order they are considered in nor the arcs they ride\n// depends on it.\n\n// Node label candidates in the order they are considered \u2014 biggest allele\n// first \u2014 which is a function of the sequence lengths alone.\nconst nodeOrderCache = new WeakMap<\n Record<string, NodeSegment[]>,\n {\n nodeLengths: Map<string, number>\n order: [string, NodeSegment[]][]\n }\n>()\n\nfunction labelOrder(\n nodePositions: Record<string, NodeSegment[]>,\n nodeLengths: Map<string, number>,\n) {\n const cached = nodeOrderCache.get(nodePositions)\n if (cached?.nodeLengths === nodeLengths) {\n return cached.order\n }\n const order = Object.entries(nodePositions)\n .filter(([id, segments]) => segments.length > 0 && nodeLengths.has(id))\n .sort(([a], [b]) => nodeLengths.get(b)! - nodeLengths.get(a)!)\n nodeOrderCache.set(nodePositions, { nodeLengths, order })\n return order\n}\n\n// One deletion arc as the renderer draws it, plus the two things placement\n// reads off it: where its apex is, and how big it is on screen.\ninterface ArcPlacement {\n deletion: DeletionEdge\n curves: BezierCurve[]\n apex: { x: number; y: number }\n extent: number\n}\n\nconst arcCache = new WeakMap<\n Record<string, NodeSegment[]>,\n {\n deletions: DeletionEdge[]\n scaleX: number\n scaleY: number\n version: number\n arcs: ArcPlacement[]\n }\n>()\n\n// Biggest deletion first, so an arc that survives a crowd is the one carrying\n// the most sequence.\nfunction arcPlacements(\n nodePositions: Record<string, NodeSegment[]>,\n deletions: DeletionEdge[],\n axis: AxisScale,\n version: number,\n) {\n const { scaleX, scaleY } = axis\n const cached = arcCache.get(nodePositions)\n if (\n cached?.deletions === deletions &&\n cached.scaleX === scaleX &&\n cached.scaleY === scaleY &&\n cached.version === version\n ) {\n return cached.arcs\n }\n const arcs: ArcPlacement[] = []\n for (const deletion of [...deletions].sort((a, b) => b.bp - a.bp)) {\n const curves = deletionArcCurves(nodePositions, deletion, axis)\n const apex = curves ? curveMidpoint(curves) : undefined\n if (curves && apex) {\n const { minX, minY, maxX, maxY } = curveBounds(curves)\n arcs.push({\n deletion,\n curves,\n apex,\n // in screen px, so the two axes' units cannot be added by accident\n extent: Math.max((maxX - minX) * scaleX, (maxY - minY) * scaleY),\n })\n }\n }\n arcCache.set(nodePositions, { deletions, scaleX, scaleY, version, arcs })\n return arcs\n}\n\nexport function graphLabels({\n nodePositions,\n nodeLengths,\n deletions,\n alleleDeletions,\n axis,\n translateX,\n translateY,\n width,\n height,\n reserved,\n version = 0,\n}: {\n nodePositions: Record<string, NodeSegment[]>\n // bp per node id, so this module never has to know what a GraphNode is\n nodeLengths: Map<string, number>\n deletions: DeletionEdge[]\n // Deletions the layout found that carry a segment, so `deletions` above\n // cannot: empty under a layout drawn at sequence scale, where a node's own\n // length IS what its drawn extent means. See AlleleDeletion.\n alleleDeletions?: AlleleDeletion[]\n // Screen px per layout unit, both axes. They are equal on an isotropic layout\n // and differ on a row layout, whose y is already px (scaleY === 1). One value\n // so a caller cannot pass one and default the other \u2014 see AxisScale.\n axis: AxisScale\n translateX: number\n translateY: number\n width: number\n height: number\n // Screen-space boxes already occupied by something this module did not draw \u2014\n // the row labels of a row-structured layout. They sit in the same overlay and\n // are opaque, so a node label under one is not \"behind\" it, it is gone: a\n // label of \"17 bp\" against the left edge of an anchored layout came out as a\n // stray \"bp\" beside `Reference (rank 0)`. Feeding them into the same collision\n // pass moves that label instead of clipping it.\n reserved?: Box[]\n // Bumped whenever the layout's positions are mutated in place, i.e. by a node\n // drag. Everything cached across frames here is keyed by it \u2014 see the caches\n // above \u2014 and reading it is also what makes a dragged node's label follow it.\n version?: number\n}): GraphLabel[] {\n const { scaleX, scaleY } = axis\n // Deletions first, so an arc keeps its label against the nodes around it: the\n // arc is the only thing in the drawing that represents sequence which is not\n // there, and a reader who cannot read it has no other route to it.\n const candidates: { label: GraphLabel; box: Box }[] = []\n for (const { deletion, curves, apex, extent } of arcPlacements(\n nodePositions,\n deletions,\n axis,\n version,\n )) {\n if (extent >= MIN_DELETION_LABEL_PX) {\n const text = deletionText(deletion.bp)\n const halfW = labelHalfWidth(text)\n const box = boxAt(halfW, 0, 0)\n const arcX = apex.x * scaleX + translateX\n const arcY = apex.y * scaleY + translateY\n // The node rule, applied to an arc: a label wider than twice what it\n // names is a label with an arc attached. Under it the label stays\n // centred on the curve, which is what every arc big enough to hold its\n // own name already does.\n const fits = extent >= (halfW * 2) / MAX_LABEL_OVERHANG\n // Displaced by the SUPPORT distance, so the whole box clears the arc\n // whatever angle it leaves at, then the leader is stopped at the box's\n // real edge along the same ray.\n const bow = arcOutward(curves, apex, scaleX, scaleY)\n const offset = boxSupport(box, bow) + LEADER_GAP_PX\n // ...and then slid back into the frame if that put it outside one. A\n // tethered label chooses where it goes, and the cull below keeps any box\n // merely OVERLAPPING the canvas, so displacing blind is how the MHC force\n // layout came out with a clipped `\u2026ips 1.5 kb of reference` against its\n // left edge. Sliding beats dropping and beats picking the arc's other\n // side: the leader is redrawn to wherever the words ended up, so it stays\n // unambiguous however far along the edge they had to move.\n const x = fits ? arcX : clamp(arcX + bow.x * offset, halfW, width - halfW)\n const y = fits\n ? arcY\n : clamp(\n arcY + bow.y * offset,\n LABEL_HALF_HEIGHT,\n height - LABEL_HALF_HEIGHT,\n )\n // From where the label actually sits, not from the bow, or the line\n // points off at the position the label would have had.\n const away = Math.hypot(x - arcX, y - arcY)\n let leader\n if (away > 0) {\n const dir = { x: (x - arcX) / away, y: (y - arcY) / away }\n const crossing = boxRayCrossing(box, dir)\n leader = {\n arcX,\n arcY,\n labelX: x - dir.x * crossing,\n labelY: y - dir.y * crossing,\n }\n }\n candidates.push({\n label: {\n key: `del:${deletion.edgeIndex}`,\n text,\n x,\n y,\n kind: 'deletion',\n leader,\n },\n box: boxAt(halfW, x, y),\n })\n }\n }\n\n // Segment-carrying deletions, next: the arcs above and these are the same\n // event and are said in the same words, so they queue together ahead of the\n // node lengths and biggest-first among themselves.\n //\n // A run's label rides the middle of what the layout DREW for it, which is the\n // reference the allele replaces, and it has to fit that extent by the same\n // factor-of-two rule a node label does \u2014 so the sliver a 2 bp skip occupies in\n // a base-level graph carries nothing, and the 7.1 kb one CFT073 leaves in the\n // E. coli pggb graph carries `7 kb del`.\n //\n // The nodes it covers are then barred from labelling themselves. Their length\n // is a true fact and the wrong one to print here: `93 bp` written across a bar\n // drawn 7.1 kb wide is read as the bar's size, which is the misreading this\n // whole label exists to fix. It stays in the tooltip and the details panel.\n const labelledByDeletion = new Set<string>()\n const alleleRuns = [...(alleleDeletions ?? [])].sort((a, b) => b.bp - a.bp)\n for (const run of alleleRuns) {\n const drawn = runBounds(run.nodeIds, nodePositions)\n if (!drawn) {\n continue\n }\n // ON SCREEN, not in layout units, and that is the whole subtlety here. A\n // run drawn over the reference it replaces can be far wider than the window\n // \u2014 the E. coli one starts 7 kb before the left edge \u2014 so its true midpoint\n // is nowhere, and a label placed there is culled for being off-canvas and\n // the bar goes unnamed. The visible slice is what a reader has, so the\n // label rides the middle of that, and the fit test asks whether the words\n // fit what is SHOWN rather than what is drawn.\n const shownLeft = Math.max(drawn.minX * scaleX + translateX, 0)\n const shownRight = Math.min(drawn.maxX * scaleX + translateX, width)\n const shownTop = Math.max(drawn.minY * scaleY + translateY, 0)\n const shownBottom = Math.min(drawn.maxY * scaleY + translateY, height)\n if (shownRight < shownLeft || shownBottom < shownTop) {\n continue\n }\n const extent = Math.max(shownRight - shownLeft, shownBottom - shownTop)\n const text = deletionText(run.bp)\n const halfW = labelHalfWidth(text)\n if (extent >= (halfW * 2) / MAX_LABEL_OVERHANG) {\n for (const id of run.nodeIds) {\n labelledByDeletion.add(id)\n }\n const x = clamp((shownLeft + shownRight) / 2, halfW, width - halfW)\n const y = clamp(\n (shownTop + shownBottom) / 2,\n LABEL_HALF_HEIGHT,\n height - LABEL_HALF_HEIGHT,\n )\n candidates.push({\n label: {\n key: `alleledel:${run.nodeIds.join(',')}`,\n text,\n x,\n y,\n kind: 'deletion',\n },\n box: boxAt(halfW, x, y),\n })\n }\n }\n\n // Biggest allele first: in a crowd, the label that survives should be the one\n // carrying the most sequence, not whichever node the layout happened to emit\n // first. The order is `labelOrder`'s, cached across frames; the three rejects\n // below are what a frame actually decides, cheapest first \u2014 a node barred by\n // a run's label, then one whose row is off the pane (the box's height is a\n // constant, so this needs no text), then one too short to hold the shortest\n // label there is.\n for (const [id, segments] of labelOrder(nodePositions, nodeLengths)) {\n if (labelledByDeletion.has(id)) {\n continue\n }\n const { x, y } = polylineMidpoint(segments)\n const screenY = y * scaleY + translateY\n if (\n screenY + LABEL_HALF_HEIGHT <= 0 ||\n screenY - LABEL_HALF_HEIGHT >= height\n ) {\n continue\n }\n const drawn = drawnLength(segments, scaleX, scaleY)\n // The same comparison the real fit test makes, against the narrowest box\n // any label can have \u2014 so nothing that would have passed is rejected here.\n if (drawn < (MIN_LABEL_HALF_WIDTH * 2) / MAX_LABEL_OVERHANG) {\n continue\n }\n const text = formatBp(nodeLengths.get(id)!)\n const halfW = labelHalfWidth(text)\n if (drawn >= (halfW * 2) / MAX_LABEL_OVERHANG) {\n const screenX = x * scaleX + translateX\n candidates.push({\n label: {\n key: `node:${id}`,\n text,\n x: screenX,\n y: screenY,\n kind: 'node',\n },\n box: boxAt(halfW, screenX, screenY),\n })\n }\n }\n\n // Node labels are rationed to the pane's area: a base-level cut of hundreds\n // of short nodes fits a length on most of them at a moderate zoom, and a\n // drawing where every node states its length is a table with a graph under\n // it. Biggest first, so the ration goes to the alleles that carry sequence;\n // zooming in gives more area to fewer nodes and the labels come back.\n const nodeBudget = Math.max(\n MIN_NODE_LABELS,\n Math.floor((width * height) / PX_PER_NODE_LABEL),\n )\n const placed: Box[] = [...(reserved ?? [])]\n const labels: GraphLabel[] = []\n let nodeLabels = 0\n for (const { label, box } of candidates) {\n if (label.kind === 'node' && nodeLabels >= nodeBudget) {\n continue\n }\n if (onScreen(box, width, height) && !placed.some(p => overlaps(p, box))) {\n placed.push(box)\n labels.push(label)\n if (label.kind === 'node') {\n nodeLabels++\n }\n }\n }\n return labels\n}\n", "import {\n abgrToCssRgba,\n normalizedRgbToCssRgba,\n} from '@jbrowse/core/util/colorBits'\nimport { syncCanvasSize } from '@jbrowse/render-core/canvas2dUtils'\nimport { Canvas2DRenderingBackendBase } from '@jbrowse/render-core/renderingBackendBase'\n\nimport { brightenAbgr } from './colorBits'\n\nimport type { RenderBatch, Renderer, TransformUniform } from './types'\n\n// Everything is drawn as a handful of paths, one per distinct (colour, weight),\n// rather than one path per thing. A drawing is mostly runs of one colour, and\n// Canvas2D's cost is per path verb and per fill or stroke, not per pixel: the\n// same 15k-node graph took 414 ms a frame as one fill per mesh triangle and 11\n// ms as batched strokes (agent-docs/GRAPH_SCALE_AND_LOD.md). Highlighted things\n// are drawn last, in their own paths, so they sit on top of what they brighten.\n//\n// Extends the shared Canvas2D base rather than standing alone, which is how\n// `setErrorHandler` arrives \u2014 `useRenderingBackend` requires it. The base's\n// no-op implementation is the correct one here: Canvas2D allocates no GPU\n// resources, so there is no OOM channel to forward.\nexport class Canvas2DRenderer\n extends Canvas2DRenderingBackendBase\n implements Renderer\n{\n private transform: TransformUniform | null = null\n private batch: RenderBatch | null = null\n private nodeHighlights: ReadonlyMap<string, number> = new Map()\n private highlightedEdge: number | null = null\n private highlightFactor = 1\n\n // render-core's, not a local `width * devicePixelRatio`: it clamps the\n // backing store at MAX_CANVAS_DIM_PX, reads the ratio through `getDpr()` so\n // this agrees with the transform the model builds, and writes the css size\n // independently of the backing size.\n resize(width: number, height: number) {\n syncCanvasSize(this.ctx.canvas, width, height)\n }\n\n uploadGeometry(batch: RenderBatch) {\n this.batch = batch\n // A rebuild renumbers the strokes, so the old edge no longer addresses the\n // same run; the model re-applies the current hover against the new batch.\n this.highlightedEdge = null\n }\n\n setNodeHighlights(factors: ReadonlyMap<string, number>) {\n this.nodeHighlights = factors\n }\n\n setEdgeHighlight(edgeIndex: number | null, factor: number) {\n this.highlightedEdge = edgeIndex\n this.highlightFactor = factor\n }\n\n updateTransform(transform: TransformUniform) {\n this.transform = transform\n }\n\n render(clearColor: [number, number, number, number]) {\n const t = this.transform\n if (!t) {\n return\n }\n const ctx = this.ctx\n const { width, height } = ctx.canvas\n\n ctx.fillStyle = normalizedRgbToCssRgba(\n [clearColor[0], clearColor[1], clearColor[2]],\n clearColor[3],\n )\n ctx.fillRect(0, 0, width, height)\n\n const batch = this.batch\n if (!batch) {\n return\n }\n ctx.lineCap = 'round'\n ctx.lineJoin = 'round'\n this.renderEdges(batch)\n this.renderNodes(batch)\n this.renderArrows(batch)\n }\n\n private renderEdges(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n const hl =\n this.highlightedEdge === null\n ? undefined\n : batch.edgeCurveRuns.get(this.highlightedEdge)\n for (const { color, items } of groupByPaint(batch.edgeCurves, (e, i) => ({\n color: inRun(hl, i)\n ? brightenAbgr(e.color, this.highlightFactor)\n : e.color,\n weight: e.thickness,\n last: inRun(hl, i),\n }))) {\n ctx.strokeStyle = abgrToCssRgba(color)\n // thickness is the half-width in css px, so the stroke is twice it at\n // the device ratio the transform already carries\n ctx.lineWidth = items[0]!.thickness * 2 * t.dpr\n ctx.beginPath()\n for (const e of items) {\n const first = e.curves[0]!\n ctx.moveTo(\n first.x0 * t.scaleX + t.translateX,\n first.y0 * t.scaleY + t.translateY,\n )\n for (const c of e.curves) {\n ctx.bezierCurveTo(\n c.cx0 * t.scaleX + t.translateX,\n c.cy0 * t.scaleY + t.translateY,\n c.cx1 * t.scaleX + t.translateX,\n c.cy1 * t.scaleY + t.translateY,\n c.x1 * t.scaleX + t.translateX,\n c.y1 * t.scaleY + t.translateY,\n )\n }\n }\n ctx.stroke()\n }\n }\n\n private renderNodes(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n // stroke index -> brighten factor, for the nodes the model lifted\n const lifted = new Map<number, number>()\n for (const [nodeId, factor] of this.nodeHighlights) {\n const run = batch.nodeStrokeRuns.get(nodeId)\n if (run) {\n for (let i = run.start; i < run.start + run.count; i++) {\n lifted.set(i, factor)\n }\n }\n }\n for (const { color, items } of groupByPaint(batch.nodeStrokes, (s, i) => {\n const factor = lifted.get(i)\n return {\n color: factor === undefined ? s.color : brightenAbgr(s.color, factor),\n weight: s.thickness,\n last: factor !== undefined,\n }\n })) {\n ctx.strokeStyle = abgrToCssRgba(color)\n ctx.lineWidth = items[0]!.thickness * 2 * t.dpr\n ctx.beginPath()\n for (const s of items) {\n const p0 = s.points[0]!\n ctx.moveTo(\n p0.x * t.scaleX + t.translateX,\n p0.y * t.scaleY + t.translateY,\n )\n for (let i = 1, l = s.points.length; i < l; i++) {\n const p = s.points[i]!\n ctx.lineTo(\n p.x * t.scaleX + t.translateX,\n p.y * t.scaleY + t.translateY,\n )\n }\n }\n ctx.stroke()\n }\n }\n\n private renderArrows(batch: RenderBatch) {\n const t = this.transform!\n const ctx = this.ctx\n const hl =\n this.highlightedEdge === null\n ? undefined\n : batch.arrowRuns.get(this.highlightedEdge)\n for (const { color, items } of groupByPaint(batch.arrows, (a, i) => ({\n color: inRun(hl, i)\n ? brightenAbgr(a.color, this.highlightFactor)\n : a.color,\n weight: 0,\n last: inRun(hl, i),\n }))) {\n ctx.fillStyle = abgrToCssRgba(color)\n ctx.beginPath()\n for (const a of items) {\n // The tip sits on the edge's end; the two barbs are `size` css px back\n // along either side of the tangent, expanded after the transform so\n // the head is the same size at every zoom.\n const tipX = a.x * t.scaleX + t.translateX\n const tipY = a.y * t.scaleY + t.translateY\n const reach = a.size * t.dpr\n ctx.moveTo(tipX, tipY)\n ctx.lineTo(\n tipX - Math.cos(a.angle - 0.5) * reach,\n tipY - Math.sin(a.angle - 0.5) * reach,\n )\n ctx.lineTo(\n tipX - Math.cos(a.angle + 0.5) * reach,\n tipY - Math.sin(a.angle + 0.5) * reach,\n )\n ctx.closePath()\n }\n ctx.fill()\n }\n }\n\n override dispose() {\n super.dispose()\n this.batch = null\n this.nodeHighlights = new Map()\n this.highlightedEdge = null\n }\n}\n\nfunction inRun(run: { start: number; count: number } | undefined, i: number) {\n return run !== undefined && i >= run.start && i < run.start + run.count\n}\n\ninterface Paint {\n color: number\n weight: number\n // drawn after everything else, so a highlight lands on top of its neighbours\n last: boolean\n}\n\n// Items bucketed by what they are painted with, in first-seen order, with the\n// `last` ones after the rest.\nfunction groupByPaint<T>(\n items: T[],\n paintOf: (item: T, index: number) => Paint,\n) {\n const groups = new Map<string, { color: number; items: T[] }>()\n const deferred = new Map<string, { color: number; items: T[] }>()\n for (let i = 0, l = items.length; i < l; i++) {\n const item = items[i]!\n const { color, weight, last } = paintOf(item, i)\n const into = last ? deferred : groups\n const key = `${color}:${weight}`\n const group = into.get(key)\n if (group) {\n group.items.push(item)\n } else {\n into.set(key, { color, items: [item] })\n }\n }\n return [...groups.values(), ...deferred.values()]\n}\n", "import { Canvas2DRenderer } from './Canvas2DRenderer'\n\n// Canvas2D only, and deliberately not through render-core's GPU ladder. The\n// batch is strokes and arrowheads in layout units, and stroking them batched by\n// colour is inside a frame budget at the node counts this view caps itself at\n// (agent-docs/GRAPH_SCALE_AND_LOD.md), so a GPU backend would buy a few\n// milliseconds for ~30 KB of bundled HAL and a shader toolchain. If one is ever\n// wanted, it should take the same `Renderer` interface and draw the batch as\n// instances \u2014 a capsule per node segment, a bezier ribbon per edge stroke \u2014\n// rather than reviving a triangle mesh; agent-docs/IDEAS.md scopes it.\nexport function createGraphRenderer(canvas: HTMLCanvasElement) {\n return Promise.resolve(new Canvas2DRenderer(canvas))\n}\n", "import { baseEdgeCurves } from './edgeCurves'\nimport { curveBounds, pathRibbonOffsets } from './geometry'\n\nimport type { EdgeCurves } from './edgeCurves'\nimport type { Graph, NodeSegment } from '../types'\nimport type { AxisScale } from './geometry'\n\ninterface CellEntry {\n nodeId: string\n segmentIdx: number\n}\n\n// A uniform grid only performs when its cells are about the size of the things\n// in them: too large and every query scans a crowd, too small and every item is\n// stamped into hundreds of cells. World units here are whatever the layout uses,\n// and the two differ by orders of magnitude \u2014 the force layout's are screen-ish\n// (nodes ~8 units long), while the reference-anchored layouts put x in bp, so a\n// 100 kb window is 100,000 units wide. A fixed 50 was tuned for the first and\n// left the second stamping each edge into hundreds of cells: measured 7.2 ms to\n// index 800 bp-scale edges, against 2.8 ms for 8,000 screen-scale ones.\n//\n// So the size is derived from the mean extent of the items being indexed \u2014\n// **per axis**, for the reason the query radius below is already per axis: on a\n// row layout x is bp and y is screen px, and one size taken from the two summed\n// is the x size. Applied to y it swallowed every row into one cell, so the grid\n// degenerated to a 1-D index on x and each query handed the distance loop every\n// row in the band. On HPRC's rGFA, whose mean segment is ~7 kb against a 20 px\n// row pitch, that is all of them.\nconst FALLBACK_CELL_SIZE = 50\n\nexport interface CellSize {\n x: number\n y: number\n}\n\nfunction meanExtentCellSize(extentTotal: number, count: number) {\n return count > 0 ? Math.max(1, extentTotal / count) : FALLBACK_CELL_SIZE\n}\n\n// The y size for the layouts whose items have no y extent of their own. Every\n// row there is a horizontal line, so the mean per-item extent is exactly zero\n// and says nothing about how the rows are spaced. What separates them is the\n// ROW PITCH, which is their spread over the number of rows they occupy.\nfunction rowPitchOf(boxes: { minY: number }[]) {\n const distinct = new Set<number>()\n let min = Infinity\n let max = -Infinity\n for (const box of boxes) {\n distinct.add(box.minY)\n min = Math.min(min, box.minY)\n max = Math.max(max, box.minY)\n }\n return distinct.size > 1 ? (max - min) / (distinct.size - 1) : undefined\n}\n\n// A row layout with one row has nothing to separate on y, so it takes the x\n// size and lands in one cell rather than being stamped across thousands.\nfunction cellSizeFrom(\n boxes: { minY: number }[],\n extentX: number,\n extentY: number,\n): CellSize {\n const x = meanExtentCellSize(extentX, boxes.length)\n return {\n x,\n y:\n extentY > 0\n ? meanExtentCellSize(extentY, boxes.length)\n : (rowPitchOf(boxes) ?? x),\n }\n}\n\nfunction resolveCellSize(\n given: number | CellSize | undefined,\n boxes: { minY: number }[],\n extentX: number,\n extentY: number,\n): CellSize {\n return given === undefined\n ? cellSizeFrom(boxes, extentX, extentY)\n : typeof given === 'number'\n ? { x: given, y: given }\n : given\n}\n\nfunction getOrCreateCell<T>(\n cells: Map<number, Map<number, T[]>>,\n cx: number,\n cy: number,\n) {\n let row = cells.get(cx)\n if (!row) {\n row = new Map()\n cells.set(cx, row)\n }\n let cell = row.get(cy)\n if (!cell) {\n cell = []\n row.set(cy, cell)\n }\n return cell\n}\n\nfunction addToGrid<T>(\n cells: Map<number, Map<number, T[]>>,\n cellSize: CellSize,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n entry: T,\n) {\n const cx0 = Math.floor(minX / cellSize.x)\n const cx1 = Math.floor(maxX / cellSize.x)\n const cy0 = Math.floor(minY / cellSize.y)\n const cy1 = Math.floor(maxY / cellSize.y)\n for (let cx = cx0; cx <= cx1; cx++) {\n for (let cy = cy0; cy <= cy1; cy++) {\n getOrCreateCell(cells, cx, cy).push(entry)\n }\n }\n}\n\n// The radius is per axis because the two axes need not be in the same units: a\n// row layout's x is bp and its y is screen px, so the same 5 px of slack is\n// hundreds of bp one way and 5 the other. Passing the x radius on both axes\n// would sweep every row in the band into the candidate set.\nfunction queryGrid<T>(\n cells: Map<number, Map<number, T[]>>,\n cellSize: CellSize,\n x: number,\n y: number,\n radiusX: number,\n radiusY: number,\n): T[] {\n const cx0 = Math.floor((x - radiusX) / cellSize.x)\n const cx1 = Math.floor((x + radiusX) / cellSize.x)\n const cy0 = Math.floor((y - radiusY) / cellSize.y)\n const cy1 = Math.floor((y + radiusY) / cellSize.y)\n const results: T[] = []\n const seen = new Set<T>()\n for (let cx = cx0; cx <= cx1; cx++) {\n const row = cells.get(cx)\n if (row) {\n for (let cy = cy0; cy <= cy1; cy++) {\n const cell = row.get(cy)\n if (cell) {\n for (const entry of cell) {\n if (!seen.has(entry)) {\n seen.add(entry)\n results.push(entry)\n }\n }\n }\n }\n }\n }\n return results\n}\n\nexport class SpatialIndex {\n private cellSize: CellSize\n private cells = new Map<number, Map<number, CellEntry[]>>()\n\n constructor(\n nodePositions: Record<string, NodeSegment[]>,\n // one number sets both axes, which is what a test that only cares about\n // bucketing wants\n cellSize?: number | CellSize,\n ) {\n // Segment boxes are collected first so the cell size can come from their\n // mean extent; the grid itself needs the size up front.\n const boxes: {\n entry: CellEntry\n minX: number\n minY: number\n maxX: number\n maxY: number\n }[] = []\n let extentX = 0\n let extentY = 0\n for (const [nodeId, segments] of Object.entries(nodePositions)) {\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!\n const next = segments[i + 1]!\n const minX = Math.min(seg.x, next.x)\n const minY = Math.min(seg.y, next.y)\n const maxX = Math.max(seg.x, next.x)\n const maxY = Math.max(seg.y, next.y)\n extentX += maxX - minX\n extentY += maxY - minY\n boxes.push({ entry: { nodeId, segmentIdx: i }, minX, minY, maxX, maxY })\n }\n }\n\n this.cellSize = resolveCellSize(cellSize, boxes, extentX, extentY)\n for (const b of boxes) {\n addToGrid(\n this.cells,\n this.cellSize,\n b.minX,\n b.minY,\n b.maxX,\n b.maxY,\n b.entry,\n )\n }\n }\n\n query(x: number, y: number, radiusX: number, radiusY = radiusX) {\n return queryGrid(this.cells, this.cellSize, x, y, radiusX, radiusY)\n }\n}\n\nexport class EdgeSpatialIndex {\n private cellSize: CellSize\n private cells = new Map<number, Map<number, number[]>>()\n // Base curves (offset 0) by edge index \u2014 the shared derivation, which the\n // geometry builder draws the same edges from. findHoveredEdge reuses them on\n // every mousemove; path-offset variants translate them (pure translation).\n private edgeCurves: EdgeCurves\n\n constructor(\n nodePositions: Record<string, NodeSegment[]>,\n graph: Graph,\n drawPaths: boolean,\n // The transform the curves indexed here were drawn under, so hit detection\n // tests the shape on screen. See AxisScale.\n axis: AxisScale,\n cellSize?: number | CellSize,\n // Bypassed backbone ids per deletion edge, i.e. what GeometryBuilder is\n // given. Without it a deletion is indexed and hit-tested on the STRAIGHT\n // chord between its endpoints while it is drawn as an arc bowed off that\n // chord, so the shape on screen is not hoverable and the empty space where\n // the chord runs is \u2014 and the tutorial tells the reader to hover it for the\n // interval and the bp. Bowing is part of the drawn geometry, so it has to be\n // part of the geometry hit detection uses.\n deletions?: Map<number, string[]>,\n // Bumped when a drag moves the positions in place; see baseEdgeCurves.\n version = 0,\n ) {\n this.edgeCurves = baseEdgeCurves(\n nodePositions,\n graph,\n axis,\n deletions,\n version,\n )\n const boxes: {\n ei: number\n minX: number\n minY: number\n maxX: number\n maxY: number\n }[] = []\n let extentX = 0\n let extentY = 0\n for (let ei = 0; ei < graph.edges.length; ei++) {\n const edge = graph.edges[ei]!\n const fromSegments = nodePositions[edge.from]\n const toSegments = nodePositions[edge.to]\n const curves = this.edgeCurves.get(ei)\n if (fromSegments?.length && toSegments?.length && curves) {\n // The control polygon's box, which contains the curve \u2014 the same\n // measure the geometry builder culls by, from the one function that\n // takes it.\n let { minX, minY, maxX, maxY } = curveBounds(curves)\n\n // Ribbons sit off the edge itself, so the box has to hold them too. Per\n // axis and off the offsets actually used, rather than one number on\n // both: the fan is perpendicular to the edge, so an edge running along\n // x spends all of it on y and none on x, and on a row layout the two\n // axes are not even in the same units.\n const numPaths = edge.pathIds?.length ?? 0\n if (drawPaths && numPaths > 0) {\n let padX = 0\n let padY = 0\n for (const o of pathRibbonOffsets(\n fromSegments,\n toSegments,\n numPaths,\n axis,\n )) {\n padX = Math.max(padX, Math.abs(o.x))\n padY = Math.max(padY, Math.abs(o.y))\n }\n minX -= padX\n minY -= padY\n maxX += padX\n maxY += padY\n }\n\n extentX += maxX - minX\n extentY += maxY - minY\n boxes.push({ ei, minX, minY, maxX, maxY })\n }\n }\n\n this.cellSize = resolveCellSize(cellSize, boxes, extentX, extentY)\n for (const b of boxes) {\n addToGrid(this.cells, this.cellSize, b.minX, b.minY, b.maxX, b.maxY, b.ei)\n }\n }\n\n getCurves(ei: number) {\n return this.edgeCurves.get(ei)\n }\n\n query(x: number, y: number, radiusX: number, radiusY = radiusX) {\n return queryGrid(this.cells, this.cellSize, x, y, radiusX, radiusY)\n }\n}\n", "import { EdgeSpatialIndex, SpatialIndex } from './SpatialIndex'\nimport { pathRibbonOffsets, translateCurves, yToXOf } from './geometry'\n\nimport type { Graph, NodeSegment } from '../types'\nimport type { AxisScale, BezierCurve } from './geometry'\n\nexport function distanceToSegment(\n px: number,\n py: number,\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n) {\n const dx = x2 - x1\n const dy = y2 - y1\n const lenSq = dx * dx + dy * dy\n if (lenSq === 0) {\n return Math.hypot(px - x1, py - y1)\n }\n const t = Math.max(0, Math.min(1, ((px - x1) * dx + (py - y1) * dy) / lenSq))\n return Math.hypot(px - (x1 + t * dx), py - (y1 + t * dy))\n}\n\nexport function distanceToCubicBezier(\n px: number,\n py: number,\n x1: number,\n y1: number,\n cx1: number,\n cy1: number,\n cx2: number,\n cy2: number,\n x2: number,\n y2: number,\n) {\n // To the chords between samples, not to the samples: on a long curve they\n // are tens of px apart, and a cursor on the curve between two of them\n // measured as far off it as half that gap.\n let minDist = Infinity\n const samples = 20\n let lastX = x1\n let lastY = y1\n for (let i = 1; i <= samples; i++) {\n const t = i / samples\n const u = 1 - t\n const bx =\n u * u * u * x1 +\n 3 * u * u * t * cx1 +\n 3 * u * t * t * cx2 +\n t * t * t * x2\n const by =\n u * u * u * y1 +\n 3 * u * u * t * cy1 +\n 3 * u * t * t * cy2 +\n t * t * t * y2\n minDist = Math.min(minDist, distanceToSegment(px, py, lastX, lastY, bx, by))\n lastX = bx\n lastY = by\n }\n return minDist\n}\n\nfunction distanceToBezierCurve(px: number, py: number, c: BezierCurve) {\n return distanceToCubicBezier(\n px,\n py,\n c.x0,\n c.y0,\n c.cx0,\n c.cy0,\n c.cx1,\n c.cy1,\n c.x1,\n c.y1,\n )\n}\n\nfunction distanceToEdgeCurves(\n px: number,\n py: number,\n curves: BezierCurve[],\n yToX = 1,\n) {\n let minDist = Infinity\n for (const c of curves) {\n const d = distanceToBezierCurve(\n px,\n py,\n yToX === 1\n ? c\n : {\n x0: c.x0,\n y0: c.y0 * yToX,\n cx0: c.cx0,\n cy0: c.cy0 * yToX,\n cx1: c.cx1,\n cy1: c.cy1 * yToX,\n x1: c.x1,\n y1: c.y1 * yToX,\n },\n )\n if (d < minDist) {\n minDist = d\n }\n }\n return minDist\n}\n\n// Indexes are cached against the layout they were built from, in a WeakMap\n// rather than a module-level slot. A single slot meant two graph views in one\n// session evicted each other's index on alternating mousemoves, and held the last\n// graph's positions alive for as long as the tab lived. Keyed this way each view\n// keeps its own, and an entry becomes collectable when its layout is replaced.\n//\n// `version` still has to be compared: a node drag mutates the position objects in\n// place, so the identity of `nodePositions` cannot report that it changed.\nconst nodeCache = new WeakMap<\n Record<string, NodeSegment[]>,\n { version: number; index: SpatialIndex }\n>()\n\nfunction getSpatialIndex(\n nodePositions: Record<string, NodeSegment[]>,\n version: number,\n) {\n const cached = nodeCache.get(nodePositions)\n if (cached?.version === version) {\n return cached.index\n }\n const index = new SpatialIndex(nodePositions)\n nodeCache.set(nodePositions, { version, index })\n return index\n}\n\nconst edgeCache = new WeakMap<\n Record<string, NodeSegment[]>,\n {\n graph: Graph\n drawPaths: boolean\n // compared by VALUE, not by the AxisScale's identity: the model rebuilds\n // that object whenever either scale changes, so identity would miss a cache\n // hit on every mousemove\n scaleX: number\n scaleY: number\n version: number\n index: EdgeSpatialIndex\n deletions?: Map<number, string[]>\n }\n>()\n\nfunction getEdgeSpatialIndex(\n nodePositions: Record<string, NodeSegment[]>,\n graph: Graph,\n drawPaths: boolean,\n axis: AxisScale,\n version: number,\n deletions?: Map<number, string[]>,\n) {\n const cached = edgeCache.get(nodePositions)\n if (\n cached?.graph === graph &&\n cached.drawPaths === drawPaths &&\n cached.scaleX === axis.scaleX &&\n cached.scaleY === axis.scaleY &&\n cached.version === version &&\n cached.deletions === deletions\n ) {\n return cached.index\n }\n const index = new EdgeSpatialIndex(\n nodePositions,\n graph,\n drawPaths,\n axis,\n undefined,\n deletions,\n version,\n )\n edgeCache.set(nodePositions, {\n graph,\n drawPaths,\n scaleX: axis.scaleX,\n scaleY: axis.scaleY,\n version,\n index,\n deletions,\n })\n return index\n}\n\n// Every distance below is measured with y converted into x units by `yToX` (the\n// model's scaleY / scaleX), because on a row layout the two axes are bp and\n// screen px and a raw hypot over them is not a distance at all \u2014 5 bp of x and\n// 5 px of y are nothing alike. The thresholds stay screen px over the x scale,\n// which is what they always were; on an isotropic layout yToX is 1 and this is\n// the arithmetic it has always done.\n//\n// `ink` is how far from its centreline each node is drawn, in screen px, and\n// the most any node is. A node drawn thick by its depth is 18 px across at the\n// defaults, and a fixed tolerance missed the pointer on its outer ink, where\n// the link beside it took the hover instead. MIN_NODE_REACH_PX keeps a thin\n// node catchable.\nconst MIN_NODE_REACH_PX = 5\n\nexport interface NodeInk {\n halfWidthPx: (nodeId: string) => number\n maxHalfWidthPx: number\n}\n\nexport function findHoveredNode(\n nodePositions: Record<string, NodeSegment[]>,\n graphX: number,\n graphY: number,\n axis: AxisScale,\n version = 0,\n ink?: NodeInk,\n) {\n const yToX = yToXOf(axis)\n const index = getSpatialIndex(nodePositions, version)\n const reach = (px: number) => Math.max(MIN_NODE_REACH_PX, px) / axis.scaleX\n // the query has to cover the thickest node there could be\n const queryReach = reach(ink?.maxHalfWidthPx ?? 0)\n const candidates = index.query(graphX, graphY, queryReach, queryReach / yToX)\n\n // The nearest candidate, not the first one inside its reach. The reach is in\n // world units, so when zoomed out it covers several nodes at once and \"first\"\n // meant whichever the grid happened to visit first \u2014 the cursor could sit on\n // one node and highlight its neighbour.\n let hovered: string | null = null\n let best = Infinity\n for (const { nodeId, segmentIdx } of candidates) {\n const segments = nodePositions[nodeId]!\n const dist = distanceToSegment(\n graphX,\n graphY * yToX,\n segments[segmentIdx]!.x,\n segments[segmentIdx]!.y * yToX,\n segments[segmentIdx + 1]!.x,\n segments[segmentIdx + 1]!.y * yToX,\n )\n if (dist < best && dist < reach(ink?.halfWidthPx(nodeId) ?? 0)) {\n best = dist\n hovered = nodeId\n }\n }\n return hovered\n}\n\nexport function findHoveredEdge(\n nodePositions: Record<string, NodeSegment[]>,\n graph: Graph,\n graphX: number,\n graphY: number,\n axis: AxisScale,\n drawPaths: boolean,\n version = 0,\n deletions?: Map<number, string[]>,\n hiddenEdges?: ReadonlySet<number>,\n) {\n const yToX = yToXOf(axis)\n const edgeThreshold = 10 / axis.scaleX\n const edgeIndex = getEdgeSpatialIndex(\n nodePositions,\n graph,\n drawPaths,\n axis,\n version,\n deletions,\n )\n const candidates = edgeIndex.query(\n graphX,\n graphY,\n edgeThreshold,\n edgeThreshold / yToX,\n )\n\n // Nearest, not first \u2014 same reason as findHoveredNode.\n let hovered: number | null = null\n let best = edgeThreshold\n for (const edgeIdx of candidates) {\n const edge = graph.edges[edgeIdx]!\n const fromSegments = nodePositions[edge.from]\n const toSegments = nodePositions[edge.to]\n if (\n !fromSegments?.length ||\n !toSegments?.length ||\n hiddenEdges?.has(edgeIdx)\n ) {\n continue\n }\n\n const numPaths = edge.pathIds?.length ?? 0\n const baseCurves = edgeIndex.getCurves(edgeIdx)\n if (!baseCurves) {\n continue\n }\n let dist: number\n\n if (!drawPaths || numPaths === 0) {\n dist = distanceToEdgeCurves(graphX, graphY * yToX, baseCurves, yToX)\n } else {\n // The same offsets GeometryBuilder drew the ribbons at, from the one\n // function that decides them, so a ribbon is hoverable where it is drawn.\n let minDist = Infinity\n for (const offset of pathRibbonOffsets(\n fromSegments,\n toSegments,\n numPaths,\n axis,\n )) {\n const curves =\n offset.x === 0 && offset.y === 0\n ? baseCurves\n : translateCurves(baseCurves, offset.x, offset.y)\n const d = distanceToEdgeCurves(graphX, graphY * yToX, curves, yToX)\n if (d < minDist) {\n minDist = d\n }\n }\n dist = minDist\n }\n\n if (dist < best) {\n best = dist\n hovered = edgeIdx\n }\n }\n return hovered\n}\n", "const ZOOM_PER_NOTCH = 1.1\nconst NOTCH_PX = 100\nconst LINE_PX = 40\nconst PAGE_PX = 800\nconst PINCH_GAIN = 10\nconst MAX_NOTCHES_PER_EVENT = 2\n\n// Zoom by how far the wheel moved rather than by one step per event. A mouse\n// sends one event of about NOTCH_PX per notch and still zooms ZOOM_PER_NOTCH;\n// a trackpad sends sixty small ones a second, which at a step each compounded\n// to hundreds of times per second. A pinch arrives as ctrl+wheel with deltas a\n// tenth the size. The delta is normalized here rather than with core's\n// normalizeWheelDelta, which a released host may not export.\nexport function wheelZoomFactor(e: {\n deltaY: number\n deltaMode: number\n ctrlKey: boolean\n}) {\n const px =\n e.deltaY * (e.deltaMode === 1 ? LINE_PX : e.deltaMode === 2 ? PAGE_PX : 1)\n const notches = ((e.ctrlKey ? PINCH_GAIN : 1) * px) / NOTCH_PX\n return (\n ZOOM_PER_NOTCH **\n -Math.max(-MAX_NOTCHES_PER_EVENT, Math.min(MAX_NOTCHES_PER_EVENT, notches))\n )\n}\n", "import { ErrorBanner, LoadingEllipses } from '@jbrowse/core/ui'\nimport { Button, LinearProgress, Paper, Typography } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n paper: {\n padding: 16,\n margin: 8,\n maxWidth: 560,\n marginInline: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n },\n row: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n },\n})\n\n// What a view with no graph on screen shows while its load runs, and after a\n// load of a source it can retry was canceled or failed.\nconst GraphLoadStatus = observer(function GraphLoadStatus({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n if (model.isLoading) {\n return (\n <Paper className={classes.paper} data-testid=\"graph-genome-loading\">\n <div className={classes.row}>\n <LoadingEllipses variant=\"h6\" message={model.statusMessage} />\n <Button\n size=\"small\"\n data-testid=\"graph-genome-cancel\"\n onClick={() => {\n model.cancelLoad()\n }}\n >\n Cancel\n </Button>\n </div>\n <LinearProgress variant=\"indeterminate\" />\n </Paper>\n )\n }\n if (model.error) {\n return (\n <Paper className={classes.paper}>\n <ErrorBanner\n error={model.error}\n onReset={() => {\n model.retryLoad()\n }}\n />\n </Paper>\n )\n }\n return (\n <Paper className={classes.paper} data-testid=\"graph-genome-load-canceled\">\n <div className={classes.row}>\n <Typography variant=\"h6\">Loading canceled</Typography>\n <Button\n variant=\"contained\"\n size=\"small\"\n data-testid=\"graph-genome-retry\"\n onClick={() => {\n model.retryLoad()\n }}\n >\n Retry\n </Button>\n </div>\n </Paper>\n )\n})\n\nexport default GraphLoadStatus\n", "import { useState } from 'react'\n\nimport { ErrorBanner } from '@jbrowse/core/ui'\nimport { Button, Paper, TextField, Typography } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport type { GraphGenomeViewModel } from '../model'\n\nconst EXAMPLE_GFA = `H\\tVN:Z:1.0\nS\\t1\\tACGT\nS\\t2\\tGGCC\nS\\t3\\tTTAA\nS\\t4\\tCCGG\nL\\t1\\t+\\t2\\t+\\t0M\nL\\t1\\t+\\t3\\t+\\t0M\nL\\t2\\t+\\t4\\t+\\t0M\nL\\t3\\t+\\t4\\t+\\t0M`\n\nconst useStyles = makeStyles()({\n column: { display: 'flex', flexDirection: 'column', gap: 8 },\n rowEnd: { display: 'flex', gap: 8, alignItems: 'flex-end' },\n rowCenter: { display: 'flex', gap: 8, alignItems: 'center' },\n flex1: { flex: 1 },\n paper: { padding: 16, margin: 8, maxWidth: 560, marginInline: 'auto' },\n header: { marginBottom: 12 },\n footer: { marginTop: 12, display: 'flex', justifyContent: 'flex-end' },\n})\n\nconst ImportForm = observer(function ImportForm({\n model,\n}: {\n model: GraphGenomeViewModel\n}) {\n const { classes } = useStyles()\n const [url, setUrl] = useState('')\n\n function handleUrlLoad() {\n if (url.trim()) {\n void model.loadGFAFromLocation({ uri: url, locationType: 'UriLocation' })\n }\n }\n\n function handleFileUpload(event: React.ChangeEvent<HTMLInputElement>) {\n const file = event.target.files?.[0]\n if (file) {\n file\n .text()\n .then(text => model.loadGFA(text, file.name))\n .catch((err: unknown) => {\n model.setError(err)\n })\n }\n }\n\n return (\n <Paper className={classes.paper}>\n <div className={classes.header}>\n <Typography variant=\"h6\">Load a GFA graph</Typography>\n </div>\n\n <div className={classes.column}>\n <div className={classes.rowCenter}>\n <Button variant=\"outlined\" component=\"label\" size=\"small\">\n Choose file\n <input\n type=\"file\"\n accept=\".gfa,.gfa1,.gfa2\"\n hidden\n onChange={event => {\n handleFileUpload(event)\n }}\n />\n </Button>\n <Typography variant=\"caption\" color=\"text.secondary\">\n Whole-file GFA; best for small/medium graphs.\n </Typography>\n </div>\n\n <div className={classes.rowEnd}>\n <TextField\n size=\"small\"\n label=\"URL\"\n placeholder=\"https://example.com/graph.gfa\"\n value={url}\n onChange={e => {\n setUrl(e.target.value)\n }}\n onKeyDown={e => {\n if (e.key === 'Enter') {\n handleUrlLoad()\n }\n }}\n className={classes.flex1}\n />\n <Button\n variant=\"contained\"\n onClick={() => {\n handleUrlLoad()\n }}\n disabled={!url.trim()}\n >\n Open\n </Button>\n </div>\n </div>\n\n <div className={classes.footer}>\n <Button\n size=\"small\"\n onClick={() => {\n void model.loadGFA(EXAMPLE_GFA, 'Example graph')\n }}\n >\n Load 4-node example\n </Button>\n </div>\n\n {model.error ? <ErrorBanner error={model.error} /> : null}\n </Paper>\n )\n})\n\nexport default ImportForm\n"],
5
+ "mappings": "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",
6
+ "names": ["require_useRenderingBackend", "__commonJSMin", "exports", "module", "require_Button", "__commonJSMin", "exports", "module", "require_renderingBackendBase", "__commonJSMin", "exports", "module", "import_mobx_react", "import_react", "import_ui", "import_useRenderingBackend", "import_jsx_runtime", "Info_default", "_jsx", "import_mobx_react", "import_mobx_react", "labelWidth", "text", "overlaps", "a", "b", "placeLabels", "candidates", "frame", "reserved", "placed", "out", "row", "c", "w", "direction", "tries", "y", "box", "p", "import_jsx_runtime", "LabelChip", "x", "y", "w", "text", "color", "italic", "small", "dimmed", "title", "testId", "onClick", "import_jsx_runtime", "svgStyle", "HALO_FACTOR", "LEGEND_CORNER_PX", "ROUTE_STACK", "BubbleHalos", "model", "bubbleHalos", "walkHighlight", "scaleX", "scaleY", "translateX", "translateY", "width", "canvasHeight", "contigThickness", "halo", "screen", "p", "dimmedBubble", "h", "id", "dimmedRoute", "r", "reserved", "byBubble", "a", "candidates", "x", "y", "labels", "placeLabels", "BUBBLE_KIND_COLORS", "route", "w", "text", "LabelChip", "BubbleHalos_default", "import_material", "import_mobx_react", "import_jsx_runtime", "svgStyle", "backButtonStyle", "LABEL_ROW_PX", "MAX_LABEL_ROWS", "LEGEND_PX", "MIN_GLYPH_PX", "glyphHeight", "bp", "room", "raw", "BubbleOverlay", "model", "bubbleGlyphs", "poppedFrom", "back", "scaleX", "translateX", "translateY", "width", "canvasHeight", "lineY", "X", "maxRows", "glyphs", "a", "b", "rowEnd", "labels", "g", "bx", "half", "cx", "row", "end", "at", "bubble", "kind", "label", "x0", "x1", "w", "h", "color", "BUBBLE_KIND_COLORS", "skipped", "dip", "key", "y", "top", "BubbleOverlay_default", "import_mobx_react", "import_jsx_runtime", "svgStyle", "EXON_COLOR", "GenePins", "model", "genePins", "scaleX", "scaleY", "translateX", "translateY", "width", "canvasHeight", "contigThickness", "labels", "placeLabels", "a", "b", "pin", "y", "w", "text", "LabelChip", "GenePins_default", "import_jsx_runtime", "CropFree_default", "_jsx", "import_jsx_runtime", "ZoomIn_default", "_jsx", "import_jsx_runtime", "ZoomOut_default", "_jsx", "import_material", "import_mobx_react", "import_mui", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "ColorSchemeSelect", "model", "classes", "e", "COLOR_SCHEMES", "value", "label", "ColorSchemeSelect_default", "import_material", "import_mobx_react", "import_jsx_runtime", "FollowControl", "model", "followLinearView", "followState", "note", "state", "FollowControl_default", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "fmtMs", "ms", "GraphPerf", "model", "classes", "lastFetchMs", "lastLayoutMs", "lastGeometryMs", "hasMetrics", "GraphStats", "GraphStats_default", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "LayoutSelect", "model", "classes", "graph", "e", "LAYOUT_MODES", "value", "label", "description", "available", "LayoutSelect_default", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "RepeatSelect", "model", "classes", "repeatChoices", "keys", "r", "e", "key", "name", "unit", "RepeatSelect_default", "import_react", "import_ui", "import_jsx_runtime", "Delete_default", "_jsx", "import_jsx_runtime", "MoreVert_default", "_jsx", "import_jsx_runtime", "Settings_default", "_jsx", "import_mobx_react", "import_ui", "import_material", "import_Button", "import_mobx_react", "import_mui", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "SUBGRAPH_CONTEXTS", "SubgraphContextSelect", "model", "classes", "e", "value", "label", "SubgraphContextSelect_default", "import_react", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "parseHaplotypeList", "text", "names", "name", "HaplotypeListField", "model", "draft", "setDraft", "apply", "parsed", "e", "SubgraphHaplotypesField", "classes", "SubgraphHaplotypesField_default", "import_jsx_runtime", "useStyles", "qualityLabels", "EngineOnly", "model", "GraphSettingsDialog", "props", "open", "onClose", "classes", "e", "quality", "label", "i", "MAX_PATH_COLORS", "trackId", "name", "BUBBLE_SPREADS", "value", "description", "NODE_WIDTHS", "SubgraphContextSelect_default", "SubgraphHaplotypesField_default", "COLOR_SCHEMES", "Button", "GraphSettingsDialog_default", "import_jsx_runtime", "SettingsMenu", "model", "settingsOpen", "setSettingsOpen", "Settings_default", "Delete_default", "MoreVert_default", "GraphSettingsDialog_default", "SettingsMenu_default", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "WalkSelect", "model", "classes", "walkChoices", "names", "c", "e", "name", "label", "WalkSelect_default", "import_jsx_runtime", "useStyles", "ZoomDisplay", "model", "GraphToolbar", "classes", "LayoutSelect_default", "ColorSchemeSelect_default", "WalkSelect_default", "RepeatSelect_default", "ZoomIn_default", "ZoomOut_default", "CropFree_default", "FollowControl_default", "GraphStats_default", "SettingsMenu_default", "GraphToolbar_default", "import_mobx_react", "import_jsx_runtime", "svgStyle", "ON_REFERENCE", "CALL_TICK", "UNBACKED_TICK", "DISAGREES", "OFF_REFERENCE", "BAR_PX", "legendBoxStyle", "legendRowStyle", "swatchStyle", "tickSwatchStyle", "TickSwatch", "color", "WalkRowsLegend", "model", "bars", "calls", "row", "call", "kb", "bp", "units", "unit", "readout", "referenceBp", "complete", "delta", "against", "calledReadout", "unbacked", "MIN_TILE_PX", "tileSeparators", "X", "xs", "k", "WalkRowsOverlay", "walkRowBars", "scaleX", "scaleY", "translateX", "translateY", "width", "canvasHeight", "Y", "origin", "reference", "rows", "backboneEnd", "label", "text", "endBp", "y", "fill", "x", "fits", "i", "run", "WalkRowsOverlay_default", "MIN_DELETION_LABEL_PX", "PX_PER_NODE_LABEL", "MIN_NODE_LABELS", "LEADER_GAP_PX", "LABEL_CHAR_PX", "LABEL_PAD_PX", "LABEL_HEIGHT_PX", "LABEL_GAP_PX", "MAX_LABEL_OVERHANG", "labelHalfWidth", "text", "LABEL_HALF_HEIGHT", "MIN_LABEL_HALF_WIDTH", "boxAt", "halfW", "x", "y", "onScreen", "box", "width", "height", "rowLabelBox", "screenY", "clamp", "v", "lo", "hi", "overlaps", "a", "b", "formatBp", "bp", "deletionText", "arcOutward", "curves", "apex", "scaleX", "scaleY", "first", "last", "dx", "dy", "len", "boxSupport", "dir", "boxRayCrossing", "halfH", "drawnLength", "segments", "total", "i", "runBounds", "nodeIds", "nodePositions", "minX", "minY", "maxX", "maxY", "id", "p", "nodeOrderCache", "labelOrder", "nodeLengths", "cached", "order", "arcCache", "arcPlacements", "deletions", "axis", "version", "arcs", "deletion", "deletionArcCurves", "curveMidpoint", "curveBounds", "graphLabels", "alleleDeletions", "translateX", "translateY", "reserved", "candidates", "extent", "arcX", "arcY", "fits", "bow", "offset", "away", "leader", "crossing", "labelledByDeletion", "alleleRuns", "run", "drawn", "shownLeft", "shownRight", "shownTop", "shownBottom", "polylineMidpoint", "screenX", "nodeBudget", "placed", "labels", "nodeLabels", "label", "import_colorBits", "import_canvas2dUtils", "import_renderingBackendBase", "Canvas2DRenderer", "width", "height", "batch", "factors", "edgeIndex", "factor", "transform", "clearColor", "ctx", "t", "hl", "color", "items", "groupByPaint", "e", "i", "inRun", "brightenAbgr", "first", "c", "lifted", "nodeId", "run", "s", "p0", "l", "p", "a", "tipX", "tipY", "reach", "paintOf", "groups", "deferred", "item", "weight", "last", "into", "key", "group", "createGraphRenderer", "canvas", "Canvas2DRenderer", "FALLBACK_CELL_SIZE", "meanExtentCellSize", "extentTotal", "count", "rowPitchOf", "boxes", "distinct", "min", "max", "box", "cellSizeFrom", "extentX", "extentY", "x", "resolveCellSize", "given", "getOrCreateCell", "cells", "cx", "cy", "row", "cell", "addToGrid", "cellSize", "minX", "minY", "maxX", "maxY", "entry", "cx0", "cx1", "cy0", "cy1", "queryGrid", "y", "radiusX", "radiusY", "results", "seen", "SpatialIndex", "nodePositions", "nodeId", "segments", "seg", "next", "b", "EdgeSpatialIndex", "graph", "drawPaths", "axis", "deletions", "version", "baseEdgeCurves", "ei", "edge", "fromSegments", "toSegments", "curves", "curveBounds", "numPaths", "padX", "padY", "o", "pathRibbonOffsets", "distanceToSegment", "px", "py", "x1", "y1", "x2", "y2", "dx", "dy", "lenSq", "t", "distanceToCubicBezier", "cx1", "cy1", "cx2", "cy2", "minDist", "samples", "lastX", "lastY", "i", "u", "bx", "by", "distanceToBezierCurve", "c", "distanceToEdgeCurves", "curves", "yToX", "d", "nodeCache", "getSpatialIndex", "nodePositions", "version", "cached", "index", "SpatialIndex", "edgeCache", "getEdgeSpatialIndex", "graph", "drawPaths", "axis", "deletions", "EdgeSpatialIndex", "MIN_NODE_REACH_PX", "findHoveredNode", "graphX", "graphY", "ink", "yToXOf", "reach", "queryReach", "candidates", "hovered", "best", "nodeId", "segmentIdx", "segments", "dist", "findHoveredEdge", "hiddenEdges", "edgeThreshold", "edgeIndex", "edgeIdx", "edge", "fromSegments", "toSegments", "numPaths", "baseCurves", "offset", "pathRibbonOffsets", "translateCurves", "wheelZoomFactor", "e", "px", "notches", "import_jsx_runtime", "tooltipStyle", "wrapperStyle", "canvasAreaStyle", "loadingLayerStyle", "rowLabelStyle", "RowLabels", "model", "rowLabels", "label", "y", "screenY", "legendStackStyle", "legendBoxStyle", "pathLegendRowStyle", "pathSwatchStyle", "PathLegend", "pathLegend", "name", "color", "WalkReadout", "h", "c", "delta", "rampStripStyle", "_", "i", "REFERENCE_RAMP_MAX_HUE", "rampEndsStyle", "ReferenceRampLegend", "domain", "formatBp", "nodeLabelStyle", "deletionLabelStyle", "leaderStyle", "GraphSizeLabels", "nodePositions", "labels", "graphLabels", "rowLabelBox", "leaders", "key", "leader", "arcX", "arcY", "labelX", "labelY", "text", "x", "kind", "HoverTooltips", "hoveredNodeData", "hoveredEdgeData", "ownLocation", "nodeOwnLocation", "hoveredDeletion", "d", "locLabel", "NodeContextMenu", "nodeId", "top", "left", "onClose", "own", "reference", "highlight", "callback", "Info_default", "nodeLaunchMenuItems", "target", "GraphCanvas", "canvasRef", "canvas", "createGraphRenderer", "lastMouseRef", "hasMovedRef", "pendingRef", "contextNode", "setContextNode", "handleWheel", "e", "rect", "wheelZoomFactor", "screenToGraph", "screenX", "getMouseCoord", "nodeAt", "findHoveredNode", "handleMouseDown", "node", "applyPending", "pending", "findHoveredEdge", "scheduleFrame", "dropPending", "handleMouseMove", "dx", "dy", "handleMouseUp", "handleMouseLeave", "handleContextMenu", "handleClick", "GraphToolbar_default", "BubbleHalos_default", "GenePins_default", "BubbleOverlay_default", "WalkRowsOverlay_default", "WalkRowsLegend", "GraphCanvas_default", "import_ui", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "useStyles", "GraphLoadStatus", "model", "classes", "GraphLoadStatus_default", "import_react", "import_ui", "import_material", "import_mobx_react", "import_mui", "import_jsx_runtime", "EXAMPLE_GFA", "useStyles", "ImportForm", "model", "classes", "url", "setUrl", "handleUrlLoad", "handleFileUpload", "event", "file", "text", "err", "e", "ImportForm_default", "import_jsx_runtime", "GraphGenomeView", "model", "GraphCanvas_default", "GraphLoadStatus_default", "ImportForm_default", "GraphGenomeView_default"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{a as b,b as p,d as u}from"./chunk-ZNGDWZ6Z.js";import{a as m,b as g}from"./chunk-HOM2Y6XS.js";import{a as N,b as C}from"./chunk-TN6FK5UD.js";import"./chunk-KWAXLUPQ.js";import{a as y}from"./chunk-J4OLMLS5.js";import"./chunk-F4C3BEPV.js";import{b as n}from"./chunk-FY3BTKY4.js";var f=n(N(),1),r=n(y(),1),c=n(C(),1);var o=class extends f.BaseFeatureDataAdapter{static{this.capabilities=["getFeatures","getRefNames"]}constructor(t,a,s){super(t,a,s),this.bubbles=m(this,["bubblesLocation"],["index"]),this.refNames=new g(this.bubbles,this)}async getRefNames(t={}){return this.refNames.assemblyRefNames(t)}async hasDataForRefName(){return!0}getFeatures(t,a={}){let{signal:s,statusCallback:d}=a;return(0,c.ObservableCreate)(async i=>{let l=await this.refNames.resolve(t,a);l!==void 0&&await(0,r.updateStatus)("Downloading bubbles",d,()=>this.bubbles.getLines(l,t.start,t.end,{signal:s,lineCallback:(h,A)=>{let e=b(h);i.next(new r.SimpleFeature({uniqueId:`bubble-${A}`,refName:t.refName,start:e.start,end:e.end,name:p(e),description:u(e),type:"bubble",score:e.segmentCount,segmentCount:e.segmentCount,pathCount:e.pathCount,inversion:e.inversion,shortestAlleleLength:e.shortestAlleleLength,longestAlleleLength:e.longestAlleleLength,segments:e.segments,shortestAllele:e.shortestAllele,longestAllele:e.longestAllele}))}})),i.complete()},s)}};export{o as default};
2
+ //# sourceMappingURL=MinigraphBubbleAdapter-A7SBF2XY.js.map