jbrowse-plugin-graphgenomeviewer 7.4.3 → 7.5.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 (170) hide show
  1. package/README.md +10 -7
  2. package/dist/chunks/{AddTrackWorkflow-LK25EC3H.js → AddTrackWorkflow-NLSZYK56.js} +2 -2
  3. package/dist/chunks/{GbzBaseSyntenyAdapter-EKLUEECW.js → GbzBaseSyntenyAdapter-4KVWVUN6.js} +2 -2
  4. package/dist/chunks/{GbzBaseSyntenyAdapter-EKLUEECW.js.map → GbzBaseSyntenyAdapter-4KVWVUN6.js.map} +1 -1
  5. package/dist/chunks/{GraphGenomeView-P3XMSKUQ.js → GraphGenomeView-KI4FQ5A7.js} +2 -2
  6. package/dist/chunks/GraphTrackSettingsDialog-UQNMDXTE.js +4 -0
  7. package/dist/chunks/GraphTrackSettingsDialog-UQNMDXTE.js.map +7 -0
  8. package/dist/chunks/{HighlightColorDialog-G6AFRYUD.js → HighlightColorDialog-D4RCUCUF.js} +2 -2
  9. package/dist/chunks/LinearGraphDisplay-RPZUZSM2.js +2 -0
  10. package/dist/chunks/{LinearGraphDisplay-O4SQSXJJ.js.map → LinearGraphDisplay-RPZUZSM2.js.map} +2 -2
  11. package/dist/chunks/MinigraphBubbleAdapter-TJYI7KEN.js +2 -0
  12. package/dist/chunks/{MinigraphBubbleAdapter-3NJ2VLN2.js.map → MinigraphBubbleAdapter-TJYI7KEN.js.map} +1 -1
  13. package/dist/chunks/RgfaTabixAdapter-5ZMJLFZP.js +2 -0
  14. package/dist/chunks/RgfaTabixAdapter-5ZMJLFZP.js.map +7 -0
  15. package/dist/chunks/WalkFormsAdapter-CAJOWOEX.js +2 -0
  16. package/dist/chunks/WalkFormsAdapter-CAJOWOEX.js.map +7 -0
  17. package/dist/chunks/WalkTabixSyntenyAdapter-VI3KA2GX.js +2 -0
  18. package/dist/chunks/{WalkTabixSyntenyAdapter-NHNSZZBX.js.map → WalkTabixSyntenyAdapter-VI3KA2GX.js.map} +2 -2
  19. package/dist/chunks/{chunk-MCE6STZR.js → chunk-22ANI2KL.js} +2 -2
  20. package/dist/chunks/{chunk-MCE6STZR.js.map → chunk-22ANI2KL.js.map} +1 -1
  21. package/dist/chunks/chunk-42KD4AVY.js +2 -0
  22. package/dist/chunks/{chunk-OKRGO7XA.js.map → chunk-42KD4AVY.js.map} +4 -4
  23. package/dist/chunks/chunk-4NIWMRKH.js +2 -0
  24. package/dist/chunks/chunk-4NIWMRKH.js.map +7 -0
  25. package/dist/chunks/chunk-BCOXRUNQ.js +3 -0
  26. package/dist/chunks/chunk-BCOXRUNQ.js.map +7 -0
  27. package/dist/chunks/chunk-DAPI7M4M.js +4 -0
  28. package/dist/chunks/chunk-DAPI7M4M.js.map +7 -0
  29. package/dist/chunks/chunk-DEWZOJSC.js +2 -0
  30. package/dist/chunks/{chunk-6OXXT2YP.js.map → chunk-DEWZOJSC.js.map} +4 -4
  31. package/dist/chunks/{chunk-K4TFAHGC.js → chunk-FCMXEFJA.js} +1 -1
  32. package/dist/chunks/{chunk-K4TFAHGC.js.map → chunk-FCMXEFJA.js.map} +1 -1
  33. package/dist/chunks/{chunk-UOFWYS6T.js → chunk-GYY6LF5L.js} +1 -1
  34. package/dist/chunks/{chunk-UOFWYS6T.js.map → chunk-GYY6LF5L.js.map} +1 -1
  35. package/dist/chunks/{chunk-F2DMEJEK.js → chunk-HHMJNAA3.js} +2 -2
  36. package/dist/chunks/{chunk-NPV7SRJD.js → chunk-HTTONSO4.js} +2 -2
  37. package/dist/chunks/{chunk-NPV7SRJD.js.map → chunk-HTTONSO4.js.map} +1 -1
  38. package/dist/chunks/{chunk-MVVXRZXV.js → chunk-JRJHXLGP.js} +2 -2
  39. package/dist/chunks/{chunk-MVVXRZXV.js.map → chunk-JRJHXLGP.js.map} +2 -2
  40. package/dist/chunks/chunk-K4JL57NR.js +2 -0
  41. package/dist/chunks/chunk-K4JL57NR.js.map +7 -0
  42. package/dist/chunks/chunk-KOC23YNN.js +4 -0
  43. package/dist/chunks/chunk-KOC23YNN.js.map +7 -0
  44. package/dist/chunks/{chunk-MNCPMOHJ.js → chunk-LTNFVL2F.js} +2 -2
  45. package/dist/chunks/chunk-OZEKEYT3.js +2 -0
  46. package/dist/chunks/{chunk-ZAYC5OMQ.js.map → chunk-OZEKEYT3.js.map} +4 -4
  47. package/dist/chunks/{chunk-DUPBXBYE.js → chunk-POUV7OPY.js} +2 -2
  48. package/dist/chunks/{chunk-DUPBXBYE.js.map → chunk-POUV7OPY.js.map} +1 -1
  49. package/dist/chunks/{chunk-CNNZDE6I.js → chunk-SPL3UO2D.js} +2 -2
  50. package/dist/chunks/{chunk-CNNZDE6I.js.map → chunk-SPL3UO2D.js.map} +1 -1
  51. package/dist/chunks/{chunk-QJ43NDYS.js → chunk-TYV6UXJA.js} +2 -2
  52. package/dist/chunks/chunk-UR2MJJUP.js +2 -0
  53. package/dist/chunks/chunk-VTMJQFN5.js +2 -0
  54. package/dist/chunks/chunk-VTMJQFN5.js.map +7 -0
  55. package/dist/chunks/{chunk-CXOMX2I6.js → chunk-XZGYJ3JC.js} +1 -1
  56. package/dist/chunks/{chunk-CXOMX2I6.js.map → chunk-XZGYJ3JC.js.map} +1 -1
  57. package/dist/chunks/{chunk-TGSDXZXI.js → chunk-Z3IKZAYC.js} +2 -2
  58. package/dist/chunks/{chunk-TGSDXZXI.js.map → chunk-Z3IKZAYC.js.map} +1 -1
  59. package/dist/chunks/chunk-ZV5FNOHS.js +3 -0
  60. package/dist/chunks/chunk-ZV5FNOHS.js.map +7 -0
  61. package/dist/chunks/{renderSvg-7G6NWBXF.js → renderSvg-3PPAEMBT.js} +2 -2
  62. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +4 -4
  63. package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +4 -4
  64. package/package.json +3 -3
  65. package/src/GraphComputeLayout.test.ts +24 -0
  66. package/src/GraphComputeLayout.ts +4 -2
  67. package/src/GraphGenomeView/components/GraphSettingsDialog.tsx +16 -0
  68. package/src/GraphGenomeView/components/WalkRowsOverlay.tsx +83 -0
  69. package/src/GraphGenomeView/components/usePaneGestures.test.tsx +82 -5
  70. package/src/GraphGenomeView/components/usePaneGestures.ts +10 -4
  71. package/src/GraphGenomeView/graphLayers.ts +14 -1
  72. package/src/GraphGenomeView/legendSections.ts +32 -0
  73. package/src/GraphGenomeView/model.test.ts +294 -113
  74. package/src/GraphGenomeView/model.ts +3 -10
  75. package/src/GraphGenomeView/nodeSize.test.ts +21 -5
  76. package/src/GraphGenomeView/nodeSize.ts +24 -8
  77. package/src/GraphGenomeView/pane/fitViews.ts +5 -110
  78. package/src/GraphGenomeView/pane/graphViews.ts +96 -20
  79. package/src/GraphGenomeView/pane/launchActions.ts +55 -33
  80. package/src/GraphGenomeView/pane/launchViews.ts +46 -0
  81. package/src/GraphGenomeView/pane/loadActions.ts +85 -25
  82. package/src/GraphGenomeView/pane/morph.test.ts +25 -1
  83. package/src/GraphGenomeView/pane/morph.ts +3 -1
  84. package/src/GraphGenomeView/pane/paneBase.ts +5 -7
  85. package/src/GraphGenomeView/pane/renderingActions.ts +61 -2
  86. package/src/GraphGenomeView/pane/settingActions.ts +62 -4
  87. package/src/GraphGenomeView/pane/walkRowViews.ts +17 -6
  88. package/src/GraphGenomeView/tubeMapPanels.ts +2 -1
  89. package/src/GraphGenomeView/viewModel.ts +4 -9
  90. package/src/GraphGenomeView/walkRowBlocks.ts +50 -0
  91. package/src/LinearGraphDisplay/LinearGraphDisplay.test.ts +35 -5
  92. package/src/LinearGraphDisplay/components/GraphStatusChrome.test.tsx +18 -0
  93. package/src/LinearGraphDisplay/components/GraphStatusChrome.tsx +4 -2
  94. package/src/LinearGraphDisplay/components/SubgraphHaplotypesField.test.tsx +41 -1
  95. package/src/LinearGraphDisplay/components/SubgraphHaplotypesField.tsx +33 -19
  96. package/src/LinearGraphDisplay/configSchema.ts +4 -3
  97. package/src/RgfaTabixAdapter/RgfaTabixAdapter.ts +2 -9
  98. package/src/RgfaTabixAdapter/configSchema.ts +3 -3
  99. package/src/RgfaTabixAdapter/walkFiles.test.ts +6 -6
  100. package/src/WalkFormsAdapter/WalkFormsAdapter.test.ts +204 -0
  101. package/src/WalkFormsAdapter/WalkFormsAdapter.ts +232 -0
  102. package/src/WalkFormsAdapter/configSchema.ts +127 -0
  103. package/src/WalkFormsAdapter/formRows.test.ts +258 -0
  104. package/src/WalkFormsAdapter/formRows.ts +231 -0
  105. package/src/WalkFormsAdapter/formsMenu.test.ts +237 -0
  106. package/src/WalkFormsAdapter/formsMenu.ts +202 -0
  107. package/src/WalkFormsAdapter/index.ts +21 -0
  108. package/src/WalkFormsAdapter/pinned.ts +33 -0
  109. package/src/WalkTabixSyntenyAdapter/WalkTabixSyntenyAdapter.test.ts +5 -5
  110. package/src/WalkTabixSyntenyAdapter/WalkTabixSyntenyAdapter.ts +3 -6
  111. package/src/WalkTabixSyntenyAdapter/configSchema.ts +2 -2
  112. package/src/hoverSync/graphViewHighlights.test.ts +53 -0
  113. package/src/hoverSync/graphViewHighlights.ts +25 -0
  114. package/src/hoverSync/lgvHover.test.ts +100 -1
  115. package/src/hoverSync/lgvHover.ts +77 -6
  116. package/src/index.ts +4 -0
  117. package/src/launchFromGraph/contributors.test.ts +52 -0
  118. package/src/launchFromGraph/contributors.ts +105 -1
  119. package/src/launchFromGraph/launchFromGraph.ts +2 -2
  120. package/src/panSNAliases/panSNAliases.test.ts +2 -2
  121. package/src/panSNTabix.test.ts +28 -0
  122. package/src/panSNTabix.ts +26 -8
  123. package/src/version.ts +1 -1
  124. package/dist/chunks/GraphTrackSettingsDialog-UTYH6HEC.js +0 -6
  125. package/dist/chunks/GraphTrackSettingsDialog-UTYH6HEC.js.map +0 -7
  126. package/dist/chunks/LinearGraphDisplay-O4SQSXJJ.js +0 -2
  127. package/dist/chunks/MinigraphBubbleAdapter-3NJ2VLN2.js +0 -2
  128. package/dist/chunks/RgfaTabixAdapter-55DGDID2.js +0 -2
  129. package/dist/chunks/RgfaTabixAdapter-55DGDID2.js.map +0 -7
  130. package/dist/chunks/WalkTabixSyntenyAdapter-NHNSZZBX.js +0 -2
  131. package/dist/chunks/chunk-4I3K4RHE.js +0 -2
  132. package/dist/chunks/chunk-4I3K4RHE.js.map +0 -7
  133. package/dist/chunks/chunk-6OXXT2YP.js +0 -2
  134. package/dist/chunks/chunk-NFH2U74B.js +0 -2
  135. package/dist/chunks/chunk-OKRGO7XA.js +0 -2
  136. package/dist/chunks/chunk-P7VMOTUM.js +0 -3
  137. package/dist/chunks/chunk-P7VMOTUM.js.map +0 -7
  138. package/dist/chunks/chunk-RU4WSKBO.js +0 -3
  139. package/dist/chunks/chunk-RU4WSKBO.js.map +0 -7
  140. package/dist/chunks/chunk-WGTLNVST.js +0 -2
  141. package/dist/chunks/chunk-WGTLNVST.js.map +0 -7
  142. package/dist/chunks/chunk-XQFBLJU5.js +0 -4
  143. package/dist/chunks/chunk-XQFBLJU5.js.map +0 -7
  144. package/dist/chunks/chunk-Y5DNBFDY.js +0 -3
  145. package/dist/chunks/chunk-Y5DNBFDY.js.map +0 -7
  146. package/dist/chunks/chunk-ZAYC5OMQ.js +0 -2
  147. package/src/launchFromGraph/launchTracks.test.ts +0 -95
  148. package/src/launchFromGraph/launchTracks.ts +0 -49
  149. /package/dist/chunks/{AddTrackWorkflow-LK25EC3H.js.map → AddTrackWorkflow-NLSZYK56.js.map} +0 -0
  150. /package/dist/chunks/{GraphGenomeView-P3XMSKUQ.js.map → GraphGenomeView-KI4FQ5A7.js.map} +0 -0
  151. /package/dist/chunks/{HighlightColorDialog-G6AFRYUD.js.map → HighlightColorDialog-D4RCUCUF.js.map} +0 -0
  152. /package/dist/chunks/{chunk-F2DMEJEK.js.map → chunk-HHMJNAA3.js.map} +0 -0
  153. /package/dist/chunks/{chunk-MNCPMOHJ.js.map → chunk-LTNFVL2F.js.map} +0 -0
  154. /package/dist/chunks/{chunk-QJ43NDYS.js.map → chunk-TYV6UXJA.js.map} +0 -0
  155. /package/dist/chunks/{chunk-NFH2U74B.js.map → chunk-UR2MJJUP.js.map} +0 -0
  156. /package/dist/chunks/{renderSvg-7G6NWBXF.js.map → renderSvg-3PPAEMBT.js.map} +0 -0
  157. /package/src/RgfaTabixAdapter/test_data/{walks4_follow.GRCh38.walks.bed.gz → walks5_follow.GRCh38.walks.bed.gz} +0 -0
  158. /package/src/RgfaTabixAdapter/test_data/{walks4_follow.GRCh38.walks.bed.gz.tbi → walks5_follow.GRCh38.walks.bed.gz.tbi} +0 -0
  159. /package/src/RgfaTabixAdapter/test_data/{walks4_header.GRCh38.walks.bed.gz → walks5_header.GRCh38.walks.bed.gz} +0 -0
  160. /package/src/RgfaTabixAdapter/test_data/{walks4_header.GRCh38.walks.bed.gz.tbi → walks5_header.GRCh38.walks.bed.gz.tbi} +0 -0
  161. /package/src/RgfaTabixAdapter/test_data/{walks4_inversion.GRCh38.walks.bed.gz → walks5_inversion.GRCh38.walks.bed.gz} +0 -0
  162. /package/src/RgfaTabixAdapter/test_data/{walks4_inversion.GRCh38.walks.bed.gz.tbi → walks5_inversion.GRCh38.walks.bed.gz.tbi} +0 -0
  163. /package/src/RgfaTabixAdapter/test_data/{walks4_lanepairs.GRCh38.walks.bed.gz → walks5_lanepairs.GRCh38.walks.bed.gz} +0 -0
  164. /package/src/RgfaTabixAdapter/test_data/{walks4_lanepairs.GRCh38.walks.bed.gz.tbi → walks5_lanepairs.GRCh38.walks.bed.gz.tbi} +0 -0
  165. /package/src/RgfaTabixAdapter/test_data/{walks4_maxnode.GRCh38.walks.bed.gz → walks5_maxnode.GRCh38.walks.bed.gz} +0 -0
  166. /package/src/RgfaTabixAdapter/test_data/{walks4_maxnode.GRCh38.walks.bed.gz.tbi → walks5_maxnode.GRCh38.walks.bed.gz.tbi} +0 -0
  167. /package/src/RgfaTabixAdapter/test_data/{walks4_rejoin.GRCh38.walks.bed.gz → walks5_rejoin.GRCh38.walks.bed.gz} +0 -0
  168. /package/src/RgfaTabixAdapter/test_data/{walks4_rejoin.GRCh38.walks.bed.gz.tbi → walks5_rejoin.GRCh38.walks.bed.gz.tbi} +0 -0
  169. /package/src/RgfaTabixAdapter/test_data/{walks4_twopart.GRCh38.walks.bed.gz → walks5_twopart.GRCh38.walks.bed.gz} +0 -0
  170. /package/src/RgfaTabixAdapter/test_data/{walks4_twopart.GRCh38.walks.bed.gz.tbi → walks5_twopart.GRCh38.walks.bed.gz.tbi} +0 -0
@@ -5,7 +5,6 @@ import {
5
5
  facetGrid,
6
6
  } from '@jbrowse/bandage-core/facetGrid'
7
7
  import { figureSvg } from '@jbrowse/bandage-core/figure'
8
- import { figureSpecSettings } from '@jbrowse/bandage-core/figureSettings'
9
8
  import { rowLabelBox } from '@jbrowse/bandage-core/graphLabels'
10
9
  import {
11
10
  LEGEND_INSET_PX,
@@ -34,10 +33,6 @@ import {
34
33
  } from '@jbrowse/bandage-core/tubeMap/genes'
35
34
  import { padded } from '@jbrowse/bandage-core/viewport'
36
35
  import { walkPosition } from '@jbrowse/bandage-core/walkKey'
37
- import {
38
- expandTabixShorthand,
39
- readConfObject,
40
- } from '@jbrowse/core/configuration'
41
36
  import { untracked } from 'mobx'
42
37
 
43
38
  import { SECTION_HEADER_PX, sectionPlacement } from '../walkRowGroups'
@@ -52,7 +47,6 @@ import {
52
47
  VIEWPORT_PANES_BUILT,
53
48
  dependOn,
54
49
  paneViewportOf,
55
- uriOf,
56
50
  } from './paneBase'
57
51
  import { trackedDpr } from '../trackedDpr'
58
52
 
@@ -63,7 +57,6 @@ import type {
63
57
  } from '@jbrowse/bandage-core/layout/walkRowDraw'
64
58
  import type { Renderer } from '@jbrowse/bandage-core/renderer/types'
65
59
  import type { LiftedWalk, WalkLift } from '@jbrowse/bandage-core/walkHighlight'
66
- import type { FileLocation } from '@jbrowse/core/util/types'
67
60
 
68
61
  export const TOOLTIP_INSET_PX = 8
69
62
  export const FACET_KEY_ROW_PX = 19
@@ -527,6 +520,7 @@ export const withFitViews = withHostViews
527
520
  connectorThickness: self.connectorThickness,
528
521
  drawPaths,
529
522
  nodeWidth: self.nodeWidth,
523
+ edgeWidth: self.linkWidth,
530
524
  highlight,
531
525
  // Untracked, so a zoom does not eagerly rebuild geometry — the
532
526
  // debounced viewportDirty bump drives the scale-dependent rebuild
@@ -557,96 +551,6 @@ export const withFitViews = withHostViews
557
551
  ? (walkPosition(walk, node.id, node.length) ?? 'not on this walk')
558
552
  : undefined
559
553
  },
560
- // The spec bandage-figure makes this drawing from with no browser, or
561
- // undefined for a graph it cannot read again: one cut from a gbz-base
562
- // track or read from a GFA url, with the genes of a GFF3 tabix track
563
- figureSpec() {
564
- const region = self.graphRegion
565
- const adapter = self.sourceAdapter
566
- const panSN = adapter?.assemblyNameToPanSN as
567
- Record<string, string> | undefined
568
- const window = region && `${region.refName}:${region.start}-${region.end}`
569
- const gfa = uriOf(self.sourceGfaLocation)
570
- const db =
571
- adapter?.type === 'GbzBaseSyntenyAdapter'
572
- ? uriOf(adapter.gbzDbLocation as FileLocation)
573
- : undefined
574
- const source = gfa
575
- ? { gfa, region: window }
576
- : db && region && window
577
- ? {
578
- gbz: {
579
- db,
580
- index: uriOf(adapter!.haplotypeIndexLocation as FileLocation),
581
- region: window,
582
- // the adapter cuts a lane by its PanSN prefix
583
- haplotypes: self.cutHaplotypes?.map(
584
- lane => panSN?.[lane] ?? lane,
585
- ),
586
- referenceSample:
587
- (adapter!.referenceSample as string | undefined) ||
588
- panSN?.[region.assemblyName] ||
589
- region.assemblyName,
590
- context: adapter!.context as number | undefined,
591
- snarls: self.cutsWholeWalks
592
- ? 'overlapping'
593
- : (adapter!.subgraphSnarls as string | undefined),
594
- },
595
- }
596
- : undefined
597
- if (!source) {
598
- return undefined
599
- }
600
- const geneTrack = self.showGenes ? self.geneTrack : undefined
601
- const geneConf = geneTrack
602
- ? self.sessionTrack(geneTrack.trackId)
603
- : undefined
604
- const geneAdapter = geneConf
605
- ? expandTabixShorthand(
606
- readConfObject(geneConf, 'adapter') as Record<string, unknown>,
607
- 'gffGzLocation',
608
- )
609
- : undefined
610
- const genes =
611
- geneAdapter?.type === 'Gff3TabixAdapter'
612
- ? {
613
- file: uriOf(geneAdapter.gffGzLocation as FileLocation),
614
- index: uriOf(
615
- (geneAdapter.index as { location?: FileLocation } | undefined)
616
- ?.location,
617
- ),
618
- format: 'gff3',
619
- }
620
- : undefined
621
- return JSON.parse(
622
- JSON.stringify({
623
- ...source,
624
- genes: genes?.file && genes.index ? genes : undefined,
625
- referencePath: self.referencePath || undefined,
626
- layout: self.chosenLayoutMode,
627
- ...figureSpecSettings({
628
- engine: self.layoutEngine,
629
- quality: self.layoutQuality,
630
- linearLayout: self.linearLayout,
631
- bubbleSpread: self.bubbleSpread,
632
- showDeletionEdges: self.showDeletionEdges,
633
- colorScheme: self.chosenColorScheme,
634
- nodeWidth: self.nodeWidth,
635
- contigThickness: self.contigThickness,
636
- connectorThickness: self.connectorThickness,
637
- colorDomain: self.statedColorDomain,
638
- }),
639
- walks: self.walkLayers.length
640
- ? self.walkLayers.map(l => (l.color ? l : l.walk))
641
- : undefined,
642
- facet: self.walkLayers.length > 1 ? self.facetSpec : undefined,
643
- walkStrip: self.walkStripShown || undefined,
644
- walkRowSamples: self.walkStripShown ? self.walkRowSamples : undefined,
645
- width: self.paneWidth,
646
- height: self.paneCeiling,
647
- }),
648
- ) as Record<string, unknown>
649
- },
650
554
  // Why the drawing cannot be written as SVG, which draws the canvas's
651
555
  // nodes, or walk rows' bars
652
556
  get figureUnavailable() {
@@ -658,15 +562,6 @@ export const withFitViews = withHostViews
658
562
  ? undefined
659
563
  : `${layoutModeByValue(self.chosenLayoutMode).label} draws a picture of its own, which the SVG export does not`
660
564
  },
661
- // Why bandage-figure can't make this drawing again: it draws only
662
- // layouts with nodes, from a graph it can read for itself
663
- get figureSpecUnavailable() {
664
- return !self.drawsNodes
665
- ? `bandage-figure draws no ${layoutModeByValue(self.chosenLayoutMode).label} layout`
666
- : this.figureSpec()
667
- ? undefined
668
- : 'bandage-figure reads a graph cut from a gbz-base track or a GFA url'
669
- },
670
565
  // The drawing as a standalone SVG, fitted, with its genes, its lifted
671
566
  // walks' keys, facet panels and the strip of walk rows; see figureSvg
672
567
  figure() {
@@ -679,13 +574,16 @@ export const withFitViews = withHostViews
679
574
  facet: self.facetSpec,
680
575
  colorScheme: self.effectiveColorScheme,
681
576
  nodeWidth: self.nodeWidth,
577
+ edgeWidth: self.linkWidth,
682
578
  showDeletionEdges: self.showDeletionEdges,
683
579
  linearLayout: self.linearLayout,
684
580
  contigThickness: self.contigThickness,
685
581
  connectorThickness: self.connectorThickness,
686
582
  region: self.graphRegion,
687
583
  colorDomain: self.statedColorDomain,
688
- genes: self.showGenes ? self.backboneGenes : undefined,
584
+ genes: self.showGenes
585
+ ? [...(self.backboneGenes ?? []), ...(self.ownGeneFeatures ?? [])]
586
+ : undefined,
689
587
  walkRows: self.walkRowBars,
690
588
  walkRowPitch: self.walkRowPitch,
691
589
  rowGenes: self.walkRowGenes,
@@ -695,9 +593,6 @@ export const withFitViews = withHostViews
695
593
  rowGenes: self.walkRowGenes,
696
594
  rowGeneGaps: self.walkRowGeneGaps,
697
595
  },
698
- // the SVG carries the spec only where the spec draws this same
699
- // picture: one of nodes (figureSpecUnavailable)
700
- spec: self.drawsNodes ? this.figureSpec() : undefined,
701
596
  })
702
597
  : undefined
703
598
  },
@@ -12,6 +12,12 @@ import {
12
12
  import { facetSettingOf } from '@jbrowse/bandage-core/facetGrid'
13
13
  import { genePins } from '@jbrowse/bandage-core/genes/genePins'
14
14
  import { formatBp } from '@jbrowse/bandage-core/graphLabels'
15
+ import {
16
+ ribbonJoin,
17
+ sharedRowOrder,
18
+ walkRowBlocks,
19
+ walkRowLinks,
20
+ } from '@jbrowse/bandage-core/layout/walkRowLinks'
15
21
  import {
16
22
  walkRowRuns,
17
23
  walkRowsOf,
@@ -98,6 +104,7 @@ import type {
98
104
  ColorScheme,
99
105
  ResolvedColorScheme,
100
106
  } from '@jbrowse/bandage-core/colorSchemes'
107
+ import type { EdgeWidth } from '@jbrowse/bandage-core/edgeWidths'
101
108
  import type { FacetInput, FacetSetting } from '@jbrowse/bandage-core/facetGrid'
102
109
  import type { LayoutModeValue } from '@jbrowse/bandage-core/layoutModes'
103
110
  import type { NodeWidth } from '@jbrowse/bandage-core/nodeWidths'
@@ -110,12 +117,16 @@ import type { AnyConfigurationModel } from '@jbrowse/core/configuration'
110
117
  import type { FileLocation } from '@jbrowse/core/util/types'
111
118
 
112
119
  // A repeat pick slices the cut's walks again, by the repeat's own array
120
+ // The rows again from the cut's tables: for a repeat pick, by the repeat's
121
+ // own array, or with each row's visits, which ribbons need and the worker's
122
+ // runs leave out
113
123
  function walkRowsFromCut(
114
124
  cut: WalkCut,
115
125
  region: { start: number; end: number },
116
126
  unit?: number,
127
+ visits?: boolean,
117
128
  ) {
118
- const runs = walkRowRuns(cut, region)
129
+ const runs = walkRowRuns(cut, region, { visits })
119
130
  return runs ? walkRowsOf(runs, unit) : undefined
120
131
  }
121
132
 
@@ -162,9 +173,12 @@ export interface GraphGrammar {
162
173
  type TubeColorKind = 'value' | 'tinted' | 'role' | 'path'
163
174
 
164
175
  // The rows walk rows draw: `kept` names the samples whose walks show, by the
165
- // name before the haplotype number; unset shows every walk the cut holds
176
+ // name before the haplotype number; unset shows every walk the cut holds.
177
+ // `order: 'shared'` stacks each row under the one it shares the most sequence
178
+ // with, so ribbons stay short and shared blocks line up.
166
179
  export interface GraphRows {
167
180
  kept?: string[]
181
+ order?: 'shared'
168
182
  }
169
183
 
170
184
  export const withGraphViews = paneBase
@@ -227,6 +241,10 @@ export const withGraphViews = paneBase
227
241
  get contigThickness() {
228
242
  return nodeSizeOf(self.grammar.size).px
229
243
  },
244
+ // Link thickness by the walks taking it; see EDGE_WIDTHS
245
+ get linkWidth(): EdgeWidth {
246
+ return nodeSizeOf(self.grammar.size).links
247
+ },
230
248
  get layerSet() {
231
249
  return layersOf(self.grammar.layers)
232
250
  },
@@ -248,6 +266,9 @@ export const withGraphViews = paneBase
248
266
  get walkStrip() {
249
267
  return this.layerSet.has('walkStrip')
250
268
  },
269
+ get walkRibbons() {
270
+ return this.layerSet.has('walkRibbons')
271
+ },
251
272
  // One section per value of a field, as a grammar of graphics splits a
252
273
  // plot. A node layout draws a panel per `walk` (each lifted walk alone,
253
274
  // on the same layout) or per `sample` (a sample's haplotypes in a row);
@@ -265,6 +286,9 @@ export const withGraphViews = paneBase
265
286
  const kept = self.grammar.rows?.kept
266
287
  return Array.isArray(kept) ? kept : undefined
267
288
  },
289
+ get walkRowOrder() {
290
+ return self.grammar.rows?.order === 'shared' ? 'shared' : undefined
291
+ },
268
292
  get hoverLightsEverything() {
269
293
  return this.chosenHover === 'everything'
270
294
  },
@@ -666,10 +690,6 @@ export const withGraphViews = paneBase
666
690
  get cutHaplotypes(): string[] | undefined {
667
691
  return undefined
668
692
  },
669
- // the GFA the graph on screen was read from, where a host says
670
- get sourceGfaLocation(): FileLocation | undefined {
671
- return undefined
672
- },
673
693
  get sourceAdapter() {
674
694
  const track = self.sourceTrack
675
695
  return track ? trackAdapterConfig(self, track) : undefined
@@ -744,12 +764,26 @@ export const withGraphViews = paneBase
744
764
  // Each walk sliced to the window, for walk rows and the strip; a repeat
745
765
  // pick slices again by its own array
746
766
  get cutWalkRows() {
747
- const runs = self.walkCut?.walkRowRuns
748
- return runs
749
- ? walkRowsOf(runs)
750
- : self.graph
751
- ? walkRows(self.graph, self.graphRegion)
752
- : undefined
767
+ const cut = self.walkCut
768
+ const runs = cut?.walkRowRuns
769
+ const visits = this.rowVisitsNeeded
770
+ return cut && runs && visits && self.graphRegion
771
+ ? walkRowsFromCut(cut, self.graphRegion, undefined, true)
772
+ : runs
773
+ ? walkRowsOf(runs)
774
+ : self.graph
775
+ ? walkRows(self.graph, self.graphRegion, undefined, { visits })
776
+ : undefined
777
+ },
778
+ // Ribbons, shared blocks and the shared order compare the rows node by
779
+ // node, so the rows carry their visits
780
+ get rowVisitsNeeded() {
781
+ return (
782
+ self.chosenLayoutMode === 'walkrows' &&
783
+ (self.walkRibbons ||
784
+ self.layerSet.has('walkBlocks') ||
785
+ self.walkRowOrder === 'shared')
786
+ )
753
787
  },
754
788
  // An assembly by each name a backbone may spell it with: the session's
755
789
  // name and aliases, and the PanSN prefix the source track maps each of
@@ -856,10 +890,11 @@ export const withGraphViews = paneBase
856
890
  }
857
891
  const repeat = self.selectedRepeat
858
892
  const cut = self.walkCut
893
+ const visits = self.rowVisitsNeeded
859
894
  const bars = repeat
860
895
  ? cut
861
- ? walkRowsFromCut(cut, repeat, repeat.unit)
862
- : walkRows(self.graph, repeat, repeat.unit)
896
+ ? walkRowsFromCut(cut, repeat, repeat.unit, visits)
897
+ : walkRows(self.graph, repeat, repeat.unit, { visits })
863
898
  : self.cutWalkRows
864
899
  if (!bars) {
865
900
  return undefined
@@ -933,10 +968,18 @@ export const withGraphViews = paneBase
933
968
  ? { by: 'column', ...grouped.groupBy, table: grouped.table }
934
969
  : undefined
935
970
  },
936
- // the cohort's rows the sample filter keeps
971
+ // The cohort's rows the sample filter keeps, in the shared order where
972
+ // asked; a grouping then keeps that order within each section
937
973
  get keptCohortRows() {
938
974
  const bars = this.cohortWalkRows
939
- return bars && filterSamples(bars.rows, self.walkRowSamples)
975
+ const rows = bars && filterSamples(bars.rows, self.walkRowSamples)
976
+ if (!bars || !rows || self.walkRowOrder !== 'shared') {
977
+ return rows
978
+ }
979
+ const all = [bars.reference, ...rows]
980
+ return sharedRowOrder(all)
981
+ .filter(i => i > 0)
982
+ .map(i => all[i]!)
940
983
  },
941
984
  // The kept rows in a section per value of the grouped column, or
942
985
  // undefined while ungrouped or the table lacks that column
@@ -956,6 +999,27 @@ export const withGraphViews = paneBase
956
999
  ? { ...bars, rows: this.walkRowGroups?.rows ?? rows }
957
1000
  : undefined
958
1001
  },
1002
+ // Where each row walks the same nodes as the row below it, as bar
1003
+ // offsets on both, the reference row first
1004
+ get walkRowRibbons() {
1005
+ const bars = this.walkRowBars
1006
+ if (!self.walkRibbons || !self.rowVisitsNeeded || !bars) {
1007
+ return undefined
1008
+ }
1009
+ const rows = [bars.reference, ...bars.rows]
1010
+ return walkRowLinks(rows, ribbonJoin(rows))
1011
+ },
1012
+ // The stretches of each row through a block of nodes off the
1013
+ // reference's path that two or more rows walk, block 0 the one with the
1014
+ // most bp, which the bars letter A, B, C...
1015
+ get sharedBlocks() {
1016
+ const bars = this.walkRowBars
1017
+ if (!self.layerSet.has('walkBlocks') || !self.rowVisitsNeeded || !bars) {
1018
+ return undefined
1019
+ }
1020
+ const rows = [bars.reference, ...bars.rows]
1021
+ return walkRowBlocks(rows, ribbonJoin(rows))
1022
+ },
959
1023
  // Whether the canvas draws the graph's nodes. A tube map and walk rows
960
1024
  // draw a picture of their own over it, so what sits on nodes (genes,
961
1025
  // bubble halos, deletion arcs, the reference strip) has nowhere to go.
@@ -971,12 +1035,17 @@ export const withGraphViews = paneBase
971
1035
  get genePins() {
972
1036
  dependOn(self.positionsVersion)
973
1037
  const positions = self.layoutResult?.nodePositions
1038
+ // each haplotype's own genes ride its nodes off the reference
1039
+ const genes = [
1040
+ ...(self.backboneGenes ?? []),
1041
+ ...(self.ownGeneFeatures ?? []),
1042
+ ]
974
1043
  return self.showGenes &&
975
1044
  this.drawsNodes &&
976
1045
  self.graph &&
977
- self.backboneGenes &&
1046
+ genes.length > 0 &&
978
1047
  positions
979
- ? genePins(self.graph, self.backboneGenes, positions)
1048
+ ? genePins(self.graph, genes, positions)
980
1049
  : []
981
1050
  },
982
1051
  // The bubbles as halos along their nodes.
@@ -984,10 +1053,17 @@ export const withGraphViews = paneBase
984
1053
  get bubbleHalos() {
985
1054
  dependOn(self.positionsVersion)
986
1055
  const positions = self.layoutResult?.nodePositions
987
- if (!self.showBubbles || !this.drawsNodes || !self.graph || !positions) {
1056
+ // with the layer off, a bubble hovered from a linear view's bubble or
1057
+ // variant track still rings
1058
+ const shown = self.showBubbles
1059
+ ? self.bubbles
1060
+ : self.hoveredBubble
1061
+ ? [self.hoveredBubble]
1062
+ : []
1063
+ if (shown.length === 0 || !this.drawsNodes || !self.graph || !positions) {
988
1064
  return []
989
1065
  }
990
- return bubbleHalos(self.graph, self.bubbles, positions, self.repeatArrays)
1066
+ return bubbleHalos(self.graph, shown, positions, self.repeatArrays)
991
1067
  },
992
1068
  // What `reference-position` paints from, which is also what `auto`
993
1069
  // resolves to on any anchored graph
@@ -5,7 +5,6 @@ import {
5
5
  layoutModeByValue,
6
6
  } from '@jbrowse/bandage-core/layoutModes'
7
7
  import { getSession } from '@jbrowse/core/util'
8
- import ContentCopyIcon from '@mui/icons-material/ContentCopy'
9
8
  import HubIcon from '@mui/icons-material/Hub'
10
9
  import PaletteIcon from '@mui/icons-material/Palette'
11
10
  import PhotoCameraIcon from '@mui/icons-material/PhotoCamera'
@@ -23,14 +22,15 @@ import {
23
22
  import { withRenderingActions } from './renderingActions'
24
23
  import { graphLaunchMenuItems } from '../../launchFromGraph/graphMenuItems'
25
24
  import {
25
+ canonicalAssemblyName,
26
26
  highlightInLinearView,
27
27
  launchSyntenyView,
28
28
  showInLinearView,
29
29
  } from '../../launchFromGraph/launchFromGraph'
30
- import { launchTracks } from '../../launchFromGraph/launchTracks'
31
30
  import { pushLaunchItem } from '../../launchFromGraph/pushLaunchItem'
32
31
  import { launchableSyntenyTracks } from '../../launchFromGraph/syntenyTracks'
33
32
  import { downloadText } from '../download'
33
+ import { GENE_ADAPTER_TYPES } from '../genes/geneFeatures'
34
34
  import { HOVER_HIGHLIGHT_VALUES } from '../hoverHighlight'
35
35
  import { TUBE_MAP_PANELS, tubeMapPanelGroups } from '../tubeMapPanels'
36
36
 
@@ -68,10 +68,10 @@ function radioItems<T>(
68
68
  export const withLaunchActions = withRenderingActions
69
69
  .actions(self => ({
70
70
  // Move the linear view already on screen, or open one if there is none,
71
- // and pair with whichever it was. A view being created gets the session's
72
- // annotation for the assembly it opens on (see launchTracks), led by the
73
- // graph's own segments track when the launch is on the reference — the one
74
- // assembly that track is configured for.
71
+ // and pair with whichever it was. A view being created gets the graph's
72
+ // own track when the launch is on the reference, plus the one gene track
73
+ // the graph reads for that assembly: a hub config can hold hundreds of
74
+ // FeatureTracks per assembly, and a launch is a jump to a locus.
75
75
  //
76
76
  // Only a view on the reference is paired with. A node's hover band is on
77
77
  // that assembly, so paired with a view on another the band fails that
@@ -80,16 +80,31 @@ export const withLaunchActions = withRenderingActions
80
80
  showInLinearView(target: { location: GraphLocation; assembly: string }) {
81
81
  const session = getSession(self)
82
82
  const onReference = target.assembly === self.graphRegion?.assemblyName
83
+ const assembly = canonicalAssemblyName(session, target.assembly)
84
+ // an assembly whose annotation nothing names as genes, a strain's BED,
85
+ // still opens on its first feature track rather than on none
86
+ const geneTrack =
87
+ (onReference
88
+ ? self.geneTrack
89
+ : self.geneTracksByAssembly.get(assembly)) ??
90
+ self.sessionTracks.find(
91
+ t =>
92
+ t.assemblies.includes(assembly) &&
93
+ GENE_ADAPTER_TYPES.has(t.adapterType),
94
+ )
83
95
  const viewId = showInLinearView({
84
96
  session,
85
97
  location: target.location,
86
98
  assembly: target.assembly,
87
99
  connectedViewId: self.pairedViewId,
88
- tracks: launchTracks({
89
- session,
90
- assemblyName: target.assembly,
91
- first: onReference ? self.sourceTrackId : undefined,
92
- }),
100
+ tracks: [
101
+ ...new Set(
102
+ [
103
+ onReference ? self.sourceTrackId : undefined,
104
+ geneTrack?.trackId,
105
+ ].filter(id => id !== undefined),
106
+ ),
107
+ ],
93
108
  })
94
109
  if (onReference) {
95
110
  self.pairWithLinearView(viewId)
@@ -283,7 +298,35 @@ export const withLaunchActions = withRenderingActions
283
298
  ]
284
299
  : []),
285
300
  ...(self.chosenLayoutMode === 'walkrows' && self.walkRowBars
286
- ? [{ label: 'Samples', subMenu: this.walkRowSampleMenuItems() }]
301
+ ? [
302
+ { label: 'Samples', subMenu: this.walkRowSampleMenuItems() },
303
+ {
304
+ type: 'checkbox' as const,
305
+ label: 'Ribbons between rows',
306
+ checked: self.walkRibbons,
307
+ onClick: () => {
308
+ self.setLayer('walkRibbons', !self.walkRibbons)
309
+ },
310
+ },
311
+ {
312
+ type: 'checkbox' as const,
313
+ label: 'Letter shared blocks',
314
+ checked: self.layerSet.has('walkBlocks'),
315
+ onClick: () => {
316
+ self.setLayer('walkBlocks', !self.layerSet.has('walkBlocks'))
317
+ },
318
+ },
319
+ {
320
+ type: 'checkbox' as const,
321
+ label: 'Order rows by shared sequence',
322
+ checked: self.walkRowOrder === 'shared',
323
+ onClick: () => {
324
+ self.setWalkRowOrder(
325
+ self.walkRowOrder === 'shared' ? undefined : 'shared',
326
+ )
327
+ },
328
+ },
329
+ ]
287
330
  : []),
288
331
  ...(self.chosenLayoutMode === 'walkrows' &&
289
332
  self.walkRowBars &&
@@ -338,27 +381,6 @@ export const withLaunchActions = withRenderingActions
338
381
  }
339
382
  },
340
383
  },
341
- {
342
- label: 'Copy figure spec',
343
- icon: ContentCopyIcon,
344
- disabled: self.figureSpecUnavailable !== undefined,
345
- disabledHelpText: self.figureSpecUnavailable,
346
- onClick: () => {
347
- const spec = self.figureSpec()
348
- const session = getSession(self)
349
- navigator.clipboard
350
- .writeText(`${JSON.stringify(spec, null, 2)}\n`)
351
- .then(() => {
352
- session.notify(
353
- 'Figure spec copied. Save it as spec.json and run: npx -p @jbrowse/bandage-core bandage-figure spec.json -o figure.svg',
354
- 'info',
355
- )
356
- })
357
- .catch((e: unknown) => {
358
- session.notify(`Could not copy the figure spec: ${String(e)}`)
359
- })
360
- },
361
- },
362
384
  ]
363
385
  },
364
386
  // `picks` false where a toolbar already picks the walks, leaving what is
@@ -5,9 +5,12 @@ import {
5
5
  import { getSession } from '@jbrowse/core/util'
6
6
 
7
7
  import { withGraphViews } from './graphViews'
8
+ import { OWN_GENE_CONTIGS } from './paneBase'
8
9
  import {
9
10
  contributingAssemblies,
11
+ nodeAssemblySpans,
10
12
  nodeOwnLocation,
13
+ offReferenceContigs,
11
14
  resolveContributors,
12
15
  resolveLocationAssembly,
13
16
  } from '../../launchFromGraph/contributors'
@@ -170,4 +173,47 @@ export const withLaunchViews = withGraphViews
170
173
  : undefined,
171
174
  }
172
175
  },
176
+ // The gene reads that put each loaded haplotype's own genes on its nodes
177
+ // off the reference: its assembly's gene track over the span those nodes
178
+ // cover, the first OWN_GENE_CONTIGS contigs of them
179
+ get ownGeneReads() {
180
+ const { graph } = self
181
+ if (!graph || !self.showGenes || !self.modeDrawsNodes) {
182
+ return []
183
+ }
184
+ return offReferenceContigs(
185
+ graph,
186
+ self.assemblyResolver,
187
+ self.graphRegion?.assemblyName,
188
+ )
189
+ .flatMap(({ stableRefName, assemblyName, refName, start, end }) => {
190
+ const track = self.geneTracksByAssembly.get(assemblyName)
191
+ return track
192
+ ? [
193
+ {
194
+ stableRefName,
195
+ trackId: track.trackId,
196
+ region: { assemblyName, refName, start, end },
197
+ },
198
+ ]
199
+ : []
200
+ })
201
+ .slice(0, OWN_GENE_CONTIGS)
202
+ },
203
+ // The hovered node's own sequence on every loaded assembly but the
204
+ // reference, for linear views of those assemblies to band: an allele off
205
+ // the reference is a few hundred bp "instead of" on GRCh38, and here it
206
+ // is where it actually lives on each haplotype
207
+ get assemblyHoverHighlights() {
208
+ const id = self.hoveredNode
209
+ const node = id === null ? undefined : self.nodeById?.get(id)
210
+ return node && self.graph && self.chosenHover !== 'off'
211
+ ? nodeAssemblySpans(
212
+ self.graph,
213
+ node,
214
+ self.assemblyResolver,
215
+ self.graphRegion?.assemblyName,
216
+ )
217
+ : []
218
+ },
173
219
  }))