jbrowse-plugin-mafviewer 1.4.3 → 1.4.6

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 (204) hide show
  1. package/README.md +1 -1
  2. package/dist/BigMafAdapter/BigMafAdapter.js +4 -5
  3. package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
  4. package/dist/BigMafAdapter/configSchema.d.ts +2 -2
  5. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js +39 -109
  6. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -1
  7. package/dist/LinearMafDisplay/components/MAFTooltip.d.ts +0 -3
  8. package/dist/LinearMafDisplay/components/MAFTooltip.js.map +1 -1
  9. package/dist/LinearMafDisplay/components/MsaHighlightOverlay.d.ts +9 -0
  10. package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js +34 -0
  11. package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js.map +1 -0
  12. package/dist/LinearMafDisplay/components/Sidebar/ColorLegend.js +2 -2
  13. package/dist/LinearMafDisplay/components/Sidebar/ColorLegend.js.map +1 -1
  14. package/dist/LinearMafDisplay/components/Sidebar/RectBg.d.ts +1 -1
  15. package/dist/LinearMafDisplay/components/Sidebar/RectBg.js +2 -3
  16. package/dist/LinearMafDisplay/components/Sidebar/RectBg.js.map +1 -1
  17. package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js +81 -11
  18. package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js.map +1 -1
  19. package/dist/LinearMafDisplay/components/Sidebar/Tree.js +30 -9
  20. package/dist/LinearMafDisplay/components/Sidebar/Tree.js.map +1 -1
  21. package/dist/LinearMafDisplay/components/Sidebar/YScaleBars.d.ts +0 -1
  22. package/dist/LinearMafDisplay/components/Sidebar/YScaleBars.js.map +1 -1
  23. package/dist/LinearMafDisplay/components/useDragSelection.d.ts +25 -0
  24. package/dist/LinearMafDisplay/components/useDragSelection.js +103 -0
  25. package/dist/LinearMafDisplay/components/useDragSelection.js.map +1 -0
  26. package/dist/LinearMafDisplay/configSchema.d.ts +3 -30
  27. package/dist/LinearMafDisplay/renderSvg.js +1 -1
  28. package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
  29. package/dist/LinearMafDisplay/stateModel.d.ts +1090 -102
  30. package/dist/LinearMafDisplay/stateModel.js +156 -17
  31. package/dist/LinearMafDisplay/stateModel.js.map +1 -1
  32. package/dist/LinearMafDisplay/types.d.ts +2 -2
  33. package/dist/LinearMafDisplay/util.d.ts +6 -0
  34. package/dist/LinearMafDisplay/util.js +28 -6
  35. package/dist/LinearMafDisplay/util.js.map +1 -1
  36. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +43 -10
  37. package/dist/LinearMafRenderer/LinearMafRenderer.js +1 -1
  38. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
  39. package/dist/LinearMafRenderer/components/LinearMafRendering.d.ts +14 -5
  40. package/dist/LinearMafRenderer/components/LinearMafRendering.js +40 -20
  41. package/dist/LinearMafRenderer/components/LinearMafRendering.js.map +1 -1
  42. package/dist/LinearMafRenderer/configSchema.d.ts +1 -6
  43. package/dist/LinearMafRenderer/configSchema.js +1 -6
  44. package/dist/LinearMafRenderer/configSchema.js.map +1 -1
  45. package/dist/LinearMafRenderer/makeImageData.js +6 -7
  46. package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
  47. package/dist/LinearMafRenderer/rendering/features.d.ts +0 -1
  48. package/dist/LinearMafRenderer/rendering/features.js +1 -14
  49. package/dist/LinearMafRenderer/rendering/features.js.map +1 -1
  50. package/dist/LinearMafRenderer/rendering/insertions.d.ts +1 -1
  51. package/dist/LinearMafRenderer/rendering/insertions.js +10 -8
  52. package/dist/LinearMafRenderer/rendering/insertions.js.map +1 -1
  53. package/dist/LinearMafRenderer/rendering/matches.d.ts +1 -1
  54. package/dist/LinearMafRenderer/rendering/matches.js +3 -15
  55. package/dist/LinearMafRenderer/rendering/matches.js.map +1 -1
  56. package/dist/LinearMafRenderer/rendering/mismatches.d.ts +1 -1
  57. package/dist/LinearMafRenderer/rendering/mismatches.js +3 -3
  58. package/dist/LinearMafRenderer/rendering/spatialIndex.js +8 -2
  59. package/dist/LinearMafRenderer/rendering/spatialIndex.js.map +1 -1
  60. package/dist/LinearMafRenderer/rendering/text.js +1 -3
  61. package/dist/LinearMafRenderer/rendering/text.js.map +1 -1
  62. package/dist/LinearMafRenderer/rendering/types.d.ts +6 -5
  63. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +1 -1
  64. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
  65. package/dist/MafAddTrackWorkflow/index.js +1 -1
  66. package/dist/MafAddTrackWorkflow/index.js.map +1 -1
  67. package/dist/MafGetSequences/MafGetSequences.d.ts +1 -0
  68. package/dist/MafGetSequences/MafGetSequences.js +2 -1
  69. package/dist/MafGetSequences/MafGetSequences.js.map +1 -1
  70. package/dist/MafSequenceWidget/LabelsCanvas.d.ts +8 -0
  71. package/dist/MafSequenceWidget/LabelsCanvas.js +37 -0
  72. package/dist/MafSequenceWidget/LabelsCanvas.js.map +1 -0
  73. package/dist/MafSequenceWidget/MafSequenceHoverHighlight.d.ts +6 -0
  74. package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js +52 -0
  75. package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js.map +1 -0
  76. package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.d.ts +2 -0
  77. package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js +12 -0
  78. package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js.map +1 -0
  79. package/dist/MafSequenceWidget/MafSequenceWidget.d.ts +6 -0
  80. package/dist/MafSequenceWidget/MafSequenceWidget.js +189 -0
  81. package/dist/MafSequenceWidget/MafSequenceWidget.js.map +1 -0
  82. package/dist/MafSequenceWidget/SequenceCanvas.d.ts +12 -0
  83. package/dist/MafSequenceWidget/SequenceCanvas.js +86 -0
  84. package/dist/MafSequenceWidget/SequenceCanvas.js.map +1 -0
  85. package/dist/MafSequenceWidget/SequenceDisplay.d.ts +12 -0
  86. package/dist/MafSequenceWidget/SequenceDisplay.js +117 -0
  87. package/dist/MafSequenceWidget/SequenceDisplay.js.map +1 -0
  88. package/dist/MafSequenceWidget/SequenceTooltip.d.ts +11 -0
  89. package/dist/MafSequenceWidget/SequenceTooltip.js +39 -0
  90. package/dist/MafSequenceWidget/SequenceTooltip.js.map +1 -0
  91. package/dist/MafSequenceWidget/baseColors.d.ts +3 -0
  92. package/dist/MafSequenceWidget/baseColors.js +64 -0
  93. package/dist/MafSequenceWidget/baseColors.js.map +1 -0
  94. package/dist/MafSequenceWidget/colToGenomePos.d.ts +13 -0
  95. package/dist/MafSequenceWidget/colToGenomePos.js +32 -0
  96. package/dist/MafSequenceWidget/colToGenomePos.js.map +1 -0
  97. package/dist/MafSequenceWidget/colToGenomePos.test.d.ts +1 -0
  98. package/dist/MafSequenceWidget/colToGenomePos.test.js +136 -0
  99. package/dist/MafSequenceWidget/colToGenomePos.test.js.map +1 -0
  100. package/dist/MafSequenceWidget/configSchema.d.ts +1 -0
  101. package/dist/MafSequenceWidget/configSchema.js +3 -0
  102. package/dist/MafSequenceWidget/configSchema.js.map +1 -0
  103. package/dist/MafSequenceWidget/constants.d.ts +4 -0
  104. package/dist/MafSequenceWidget/constants.js +5 -0
  105. package/dist/MafSequenceWidget/constants.js.map +1 -0
  106. package/dist/MafSequenceWidget/index.d.ts +2 -0
  107. package/dist/MafSequenceWidget/index.js +16 -0
  108. package/dist/MafSequenceWidget/index.js.map +1 -0
  109. package/dist/MafSequenceWidget/stateModelFactory.d.ts +67 -0
  110. package/dist/MafSequenceWidget/stateModelFactory.js +21 -0
  111. package/dist/MafSequenceWidget/stateModelFactory.js.map +1 -0
  112. package/dist/MafTabixAdapter/MafTabixAdapter.js +4 -35
  113. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
  114. package/dist/MafTabixAdapter/configSchema.d.ts +4 -4
  115. package/dist/MafTrack/configSchema.d.ts +16 -11
  116. package/dist/index.js +2 -0
  117. package/dist/index.js.map +1 -1
  118. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +12 -24
  119. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
  120. package/dist/util/clipboard.d.ts +2 -0
  121. package/dist/util/clipboard.js +28 -0
  122. package/dist/util/clipboard.js.map +1 -0
  123. package/dist/util/fastaUtils.d.ts +2 -1
  124. package/dist/util/fastaUtils.js +93 -50
  125. package/dist/util/fastaUtils.js.map +1 -1
  126. package/dist/util/fastaUtils.test.js +190 -0
  127. package/dist/util/fastaUtils.test.js.map +1 -1
  128. package/dist/util/parseAssemblyName.d.ts +32 -0
  129. package/dist/util/parseAssemblyName.js +87 -0
  130. package/dist/util/parseAssemblyName.js.map +1 -0
  131. package/dist/util/parseAssemblyName.test.d.ts +1 -0
  132. package/dist/util/parseAssemblyName.test.js +269 -0
  133. package/dist/util/parseAssemblyName.test.js.map +1 -0
  134. package/package.json +12 -12
  135. package/src/BigMafAdapter/BigMafAdapter.ts +5 -5
  136. package/src/LinearMafDisplay/components/LinearMafDisplayComponent.tsx +63 -145
  137. package/src/LinearMafDisplay/components/MAFTooltip.tsx +0 -3
  138. package/src/LinearMafDisplay/components/MsaHighlightOverlay.tsx +62 -0
  139. package/src/LinearMafDisplay/components/Sidebar/ColorLegend.tsx +2 -6
  140. package/src/LinearMafDisplay/components/Sidebar/RectBg.tsx +8 -3
  141. package/src/LinearMafDisplay/components/Sidebar/SvgWrapper.tsx +117 -15
  142. package/src/LinearMafDisplay/components/Sidebar/Tree.tsx +53 -8
  143. package/src/LinearMafDisplay/components/Sidebar/YScaleBars.tsx +0 -1
  144. package/src/LinearMafDisplay/components/useDragSelection.ts +159 -0
  145. package/src/LinearMafDisplay/renderSvg.tsx +1 -1
  146. package/src/LinearMafDisplay/stateModel.ts +215 -20
  147. package/src/LinearMafDisplay/types.ts +2 -2
  148. package/src/LinearMafDisplay/util.ts +35 -7
  149. package/src/LinearMafRenderer/LinearMafRenderer.ts +3 -5
  150. package/src/LinearMafRenderer/components/LinearMafRendering.tsx +67 -33
  151. package/src/LinearMafRenderer/configSchema.ts +1 -6
  152. package/src/LinearMafRenderer/makeImageData.ts +5 -14
  153. package/src/LinearMafRenderer/rendering/features.ts +2 -36
  154. package/src/LinearMafRenderer/rendering/insertions.ts +13 -8
  155. package/src/LinearMafRenderer/rendering/matches.ts +2 -27
  156. package/src/LinearMafRenderer/rendering/mismatches.ts +3 -3
  157. package/src/LinearMafRenderer/rendering/spatialIndex.ts +9 -2
  158. package/src/LinearMafRenderer/rendering/text.ts +1 -2
  159. package/src/LinearMafRenderer/rendering/types.ts +8 -5
  160. package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +1 -1
  161. package/src/MafAddTrackWorkflow/index.ts +1 -1
  162. package/src/MafGetSequences/MafGetSequences.ts +10 -2
  163. package/src/MafSequenceWidget/LabelsCanvas.tsx +58 -0
  164. package/src/MafSequenceWidget/MafSequenceHoverHighlight.tsx +83 -0
  165. package/src/MafSequenceWidget/MafSequenceHoverHighlightExtension.tsx +24 -0
  166. package/src/MafSequenceWidget/MafSequenceWidget.tsx +294 -0
  167. package/src/MafSequenceWidget/SequenceCanvas.tsx +136 -0
  168. package/src/MafSequenceWidget/SequenceDisplay.tsx +188 -0
  169. package/src/MafSequenceWidget/SequenceTooltip.tsx +70 -0
  170. package/src/MafSequenceWidget/baseColors.ts +76 -0
  171. package/src/MafSequenceWidget/colToGenomePos.test.ts +166 -0
  172. package/src/MafSequenceWidget/colToGenomePos.ts +40 -0
  173. package/src/MafSequenceWidget/configSchema.ts +3 -0
  174. package/src/MafSequenceWidget/constants.ts +4 -0
  175. package/src/MafSequenceWidget/index.ts +24 -0
  176. package/src/MafSequenceWidget/stateModelFactory.ts +43 -0
  177. package/src/MafTabixAdapter/MafTabixAdapter.ts +12 -51
  178. package/src/index.ts +2 -0
  179. package/src/util/__snapshots__/fastaUtils.test.ts.snap +35 -0
  180. package/src/util/clipboard.ts +35 -0
  181. package/src/util/fastaUtils.test.ts +199 -0
  182. package/src/util/fastaUtils.ts +118 -51
  183. package/src/util/parseAssemblyName.test.ts +350 -0
  184. package/src/util/parseAssemblyName.ts +106 -0
  185. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.d.ts +0 -11
  186. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js +0 -97
  187. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js.map +0 -1
  188. package/dist/LinearMafDisplay/components/util.d.ts +0 -1
  189. package/dist/LinearMafDisplay/components/util.js +0 -8
  190. package/dist/LinearMafDisplay/components/util.js.map +0 -1
  191. package/dist/LinearMafRenderer/components/util.d.ts +0 -1
  192. package/dist/LinearMafRenderer/components/util.js +0 -13
  193. package/dist/LinearMafRenderer/components/util.js.map +0 -1
  194. package/dist/util/fetchSequences.d.ts +0 -18
  195. package/dist/util/fetchSequences.js +0 -39
  196. package/dist/util/fetchSequences.js.map +0 -1
  197. package/dist/util/useSequences.d.ts +0 -21
  198. package/dist/util/useSequences.js +0 -64
  199. package/dist/util/useSequences.js.map +0 -1
  200. package/src/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.tsx +0 -175
  201. package/src/LinearMafDisplay/components/util.ts +0 -7
  202. package/src/LinearMafRenderer/components/util.ts +0 -13
  203. package/src/util/fetchSequences.ts +0 -57
  204. package/src/util/useSequences.ts +0 -90
@@ -20,7 +20,7 @@ export function processFeatureAlignment(
20
20
  string,
21
21
  AlignmentRecord
22
22
  >
23
- const referenceSeq = feature.get('seq').toLowerCase()
23
+ const referenceSeq = (feature.get('seq') as string).toLowerCase()
24
24
 
25
25
  for (const [sampleId, alignmentData] of Object.entries(alignments)) {
26
26
  const row = sampleToRowMap.get(sampleId)
@@ -33,16 +33,7 @@ export function processFeatureAlignment(
33
33
  const rowTop = renderingContext.offset + renderingContext.rowHeight * row
34
34
 
35
35
  renderGaps(renderingContext, alignment, referenceSeq, leftPx, rowTop)
36
- renderMatches(
37
- renderingContext,
38
- alignment,
39
- referenceSeq,
40
- leftPx,
41
- rowTop,
42
- row,
43
- alignmentData.start,
44
- alignmentData.chr,
45
- )
36
+ renderMatches(renderingContext, alignment, referenceSeq, leftPx, rowTop)
46
37
  renderMismatches(
47
38
  renderingContext,
48
39
  alignment,
@@ -61,31 +52,6 @@ export function processFeatureAlignment(
61
52
  leftPx,
62
53
  rowTop,
63
54
  )
64
- }
65
- }
66
-
67
- export function processFeatureInsertions(
68
- feature: Feature,
69
- region: GenomicRegion,
70
- bpPerPx: number,
71
- sampleToRowMap: Map<string, number>,
72
- renderingContext: RenderingContext,
73
- ) {
74
- const [leftPx] = featureSpanPx(feature, region, bpPerPx)
75
- const alignments = feature.get('alignments') as Record<
76
- string,
77
- AlignmentRecord
78
- >
79
- const referenceSeq = feature.get('seq').toLowerCase()
80
- for (const [sampleId, alignmentData] of Object.entries(alignments)) {
81
- const row = sampleToRowMap.get(sampleId)
82
- if (row === undefined) {
83
- continue
84
- }
85
-
86
- const alignment = alignmentData.seq.toLowerCase()
87
- const rowTop = renderingContext.offset + renderingContext.rowHeight * row
88
-
89
55
  renderInsertions(
90
56
  renderingContext,
91
57
  alignment,
@@ -1,6 +1,6 @@
1
1
  import { measureText } from '@jbrowse/core/util'
2
2
 
3
- import { fillRect, getCharWidthHeight } from '../util'
3
+ import { fillRect } from '../util'
4
4
  import { addToSpatialIndex, shouldAddToSpatialIndex } from './spatialIndex'
5
5
  import {
6
6
  CHAR_SIZE_WIDTH,
@@ -23,12 +23,11 @@ export function renderInsertions(
23
23
  leftPx: number,
24
24
  rowTop: number,
25
25
  bpPerPx: number,
26
- sampleId: number,
26
+ rowIndex: number,
27
27
  alignmentStart: number,
28
28
  chr: string,
29
29
  ) {
30
- const { ctx, scale, h, canvasWidth, rowHeight } = context
31
- const { charHeight } = getCharWidthHeight()
30
+ const { ctx, scale, h, canvasWidth, rowHeight, charHeight } = context
32
31
 
33
32
  for (
34
33
  let i = 0, genomicOffset = 0, seqLength = alignment.length;
@@ -102,14 +101,17 @@ export function renderInsertions(
102
101
  actualXPos = xPos
103
102
  actualWidth = INSERTION_LINE_WIDTH
104
103
  fillRect(ctx, actualXPos, rowTop, actualWidth, h, canvasWidth, 'purple')
104
+
105
+ // Always use a wider hit box for spatial index, even if visual is 1px
106
+ const hitBoxPadding = 2
107
+ actualXPos = xPos - hitBoxPadding
108
+ actualWidth = INSERTION_LINE_WIDTH + 2 * hitBoxPadding
109
+
105
110
  if (
106
111
  bpPerPx < HIGH_ZOOM_THRESHOLD &&
107
112
  rowHeight > MIN_ROW_HEIGHT_FOR_BORDERS
108
113
  ) {
109
114
  // Add horizontal borders for visibility at high zoom
110
- // Note: borders extend the effective clickable area
111
- actualXPos = xPos - INSERTION_BORDER_WIDTH
112
- actualWidth = INSERTION_BORDER_HEIGHT
113
115
  fillRect(
114
116
  ctx,
115
117
  xPos - INSERTION_BORDER_WIDTH,
@@ -131,6 +133,8 @@ export function renderInsertions(
131
133
 
132
134
  // Add insertion to spatial index with actual rendered dimensions
133
135
  // Insertions always bypass distance filter
136
+ const isLargeInsertion =
137
+ insertionSequence.length > LARGE_INSERTION_THRESHOLD
134
138
  if (shouldAddToSpatialIndex(actualXPos, context, true)) {
135
139
  addToSpatialIndex(
136
140
  context,
@@ -142,8 +146,9 @@ export function renderInsertions(
142
146
  pos: genomicOffset + alignmentStart,
143
147
  chr,
144
148
  base: insertionSequence,
145
- sampleId,
149
+ rowIndex,
146
150
  isInsertion: true,
151
+ isLargeInsertion,
147
152
  },
148
153
  )
149
154
  }
@@ -1,5 +1,4 @@
1
1
  import { fillRect } from '../util'
2
- import { addToSpatialIndex, shouldAddToSpatialIndex } from './spatialIndex'
3
2
  import { GAP_STROKE_OFFSET } from './types'
4
3
 
5
4
  import type { RenderingContext } from './types'
@@ -10,9 +9,6 @@ export function renderMatches(
10
9
  seq: string,
11
10
  leftPx: number,
12
11
  rowTop: number,
13
- sampleId: number,
14
- alignmentStart: number,
15
- chr: string,
16
12
  ) {
17
13
  if (context.showAllLetters) {
18
14
  return
@@ -30,30 +26,9 @@ export function renderMatches(
30
26
  if (seq[i] !== '-') {
31
27
  // Only process non-gap positions in reference
32
28
  const currentChar = alignment[i]
33
- const xPos = leftPx + scale * genomicOffset
34
- if (
35
- seq[i] === currentChar &&
36
- currentChar !== '-' &&
37
- currentChar !== ' '
38
- ) {
29
+ if (seq[i] === currentChar && currentChar !== ' ') {
30
+ const xPos = leftPx + scale * genomicOffset
39
31
  fillRect(ctx, xPos, rowTop, scale + GAP_STROKE_OFFSET, h, canvasWidth)
40
-
41
- // Add to spatial index if distance filter allows
42
- if (shouldAddToSpatialIndex(xPos, context)) {
43
- addToSpatialIndex(
44
- context,
45
- xPos,
46
- rowTop,
47
- xPos + context.scale + GAP_STROKE_OFFSET,
48
- rowTop + context.h,
49
- {
50
- pos: genomicOffset + alignmentStart,
51
- chr,
52
- base: currentChar || '',
53
- sampleId,
54
- },
55
- )
56
- }
57
32
  }
58
33
  genomicOffset++
59
34
  }
@@ -21,7 +21,7 @@ export function renderMismatches(
21
21
  seq: string,
22
22
  leftPx: number,
23
23
  rowTop: number,
24
- sampleId: number,
24
+ rowIndex: number,
25
25
  alignmentStart: number,
26
26
  chr: string,
27
27
  ) {
@@ -70,7 +70,7 @@ export function renderMismatches(
70
70
  pos: genomicOffset + alignmentStart,
71
71
  chr,
72
72
  base: currentChar!,
73
- sampleId,
73
+ rowIndex,
74
74
  },
75
75
  )
76
76
  }
@@ -100,7 +100,7 @@ export function renderMismatches(
100
100
  pos: genomicOffset + alignmentStart,
101
101
  chr,
102
102
  base: currentChar!,
103
- sampleId,
103
+ rowIndex,
104
104
  },
105
105
  )
106
106
  }
@@ -20,9 +20,16 @@ export function shouldAddToSpatialIndex(
20
20
  context: RenderingContext,
21
21
  bypassDistanceFilter = false,
22
22
  ): boolean {
23
+ if (bypassDistanceFilter) {
24
+ return true
25
+ }
26
+
27
+ // Zoom-aware distance threshold: scale threshold based on zoom level
28
+ // At high zoom (small bpPerPx), use smaller threshold for more precision
29
+ // At low zoom (large bpPerPx), use larger threshold to reduce index size
23
30
  return (
24
- bypassDistanceFilter ||
25
- Math.abs(xPos - context.lastInsertedX) > MIN_X_DISTANCE
31
+ Math.abs(xPos - context.lastInsertedX) >
32
+ MIN_X_DISTANCE * Math.max(1, context.bpPerPx)
26
33
  )
27
34
  }
28
35
 
@@ -1,4 +1,3 @@
1
- import { getCharWidthHeight } from '../util'
2
1
  import { CHAR_SIZE_WIDTH, VERTICAL_TEXT_OFFSET } from './types'
3
2
 
4
3
  import type { RenderingContext } from './types'
@@ -34,8 +33,8 @@ export function renderText(
34
33
  mismatchRendering,
35
34
  contrastForBase,
36
35
  showAsUpperCase,
36
+ charHeight,
37
37
  } = context
38
- const { charHeight } = getCharWidthHeight()
39
38
 
40
39
  // Render text labels when zoomed in enough and row is tall enough
41
40
  if (scale >= CHAR_SIZE_WIDTH) {
@@ -13,10 +13,7 @@ export const HIGH_BP_PER_PX_THRESHOLD = 10
13
13
  export const INSERTION_BORDER_HEIGHT = 5
14
14
  export const MIN_X_DISTANCE = 0.5
15
15
 
16
- export interface Sample {
17
- id: string
18
- color?: string
19
- }
16
+ export type { Sample } from '../../LinearMafDisplay/types'
20
17
 
21
18
  export interface GenomicRegion {
22
19
  start: number
@@ -28,8 +25,9 @@ export interface RenderedBase {
28
25
  pos: number
29
26
  chr: string
30
27
  base: string
31
- sampleId: number
28
+ rowIndex: number
32
29
  isInsertion?: boolean
30
+ isLargeInsertion?: boolean
33
31
  }
34
32
 
35
33
  /**
@@ -38,6 +36,7 @@ export interface RenderedBase {
38
36
  export interface RenderingContext {
39
37
  ctx: CanvasRenderingContext2D
40
38
  scale: number
39
+ bpPerPx: number
41
40
  canvasWidth: number
42
41
  rowHeight: number
43
42
  h: number
@@ -49,6 +48,10 @@ export interface RenderingContext {
49
48
  mismatchRendering: boolean
50
49
  showAsUpperCase: boolean
51
50
 
51
+ // Cached char dimensions
52
+ charWidth: number
53
+ charHeight: number
54
+
52
55
  // RBush spatial index for efficient spatial queries
53
56
  spatialIndex: RenderedBase[]
54
57
  spatialIndexCoords: number[]
@@ -7,6 +7,7 @@ import {
7
7
  isSessionModelWithWidgets,
8
8
  isSessionWithAddTracks,
9
9
  } from '@jbrowse/core/util'
10
+ import { getRoot } from '@jbrowse/mobx-state-tree'
10
11
  import {
11
12
  Button,
12
13
  FormControl,
@@ -17,7 +18,6 @@ import {
17
18
  RadioGroup,
18
19
  TextField,
19
20
  } from '@mui/material'
20
- import { getRoot } from 'mobx-state-tree'
21
21
  import { makeStyles } from 'tss-react/mui'
22
22
 
23
23
  import type { AddTrackModel } from '@jbrowse/plugin-data-management'
@@ -1,6 +1,6 @@
1
1
  import PluginManager from '@jbrowse/core/PluginManager'
2
2
  import { AddTrackWorkflowType } from '@jbrowse/core/pluggableElementTypes'
3
- import { types } from 'mobx-state-tree'
3
+ import { types } from '@jbrowse/mobx-state-tree'
4
4
 
5
5
  import MultiMAFWidget from './AddTrackWorkflow'
6
6
 
@@ -21,6 +21,7 @@ export default class MafGetSequences extends RpcMethodTypeWithFiltersAndRenameRe
21
21
  headers?: Record<string, string>
22
22
  regions: Region[]
23
23
  showAllLetters: boolean
24
+ includeInsertions?: boolean
24
25
  },
25
26
  rpcDriverClassName: string,
26
27
  ) {
@@ -28,8 +29,14 @@ export default class MafGetSequences extends RpcMethodTypeWithFiltersAndRenameRe
28
29
  args,
29
30
  rpcDriverClassName,
30
31
  )
31
- const { samples, regions, adapterConfig, sessionId, showAllLetters } =
32
- deserializedArgs
32
+ const {
33
+ samples,
34
+ regions,
35
+ adapterConfig,
36
+ sessionId,
37
+ showAllLetters,
38
+ includeInsertions,
39
+ } = deserializedArgs
33
40
  const dataAdapter = (
34
41
  await getAdapter(this.pluginManager, sessionId, adapterConfig)
35
42
  ).dataAdapter as BaseFeatureDataAdapter
@@ -42,6 +49,7 @@ export default class MafGetSequences extends RpcMethodTypeWithFiltersAndRenameRe
42
49
  samples,
43
50
  regions,
44
51
  showAllLetters,
52
+ includeInsertions,
45
53
  })
46
54
  }
47
55
  }
@@ -0,0 +1,58 @@
1
+ import React, { useEffect, useRef } from 'react'
2
+
3
+ import { useTheme } from '@mui/material'
4
+
5
+ import { CHAR_WIDTH, FONT, LABEL_PADDING, ROW_HEIGHT } from './constants'
6
+
7
+ import type { Sample } from '../LinearMafDisplay/types'
8
+
9
+ interface LabelsCanvasProps {
10
+ samples: Sample[]
11
+ maxLabelLength: number
12
+ }
13
+
14
+ export default function LabelsCanvas({
15
+ samples,
16
+ maxLabelLength,
17
+ }: LabelsCanvasProps) {
18
+ const theme = useTheme()
19
+ const canvasRef = useRef<HTMLCanvasElement>(null)
20
+
21
+ const labelWidth = maxLabelLength * CHAR_WIDTH + LABEL_PADDING
22
+ const canvasHeight = samples.length * ROW_HEIGHT
23
+
24
+ useEffect(() => {
25
+ const canvas = canvasRef.current
26
+ if (!canvas) {
27
+ return
28
+ }
29
+
30
+ const ctx = canvas.getContext('2d')
31
+ if (!ctx) {
32
+ return
33
+ }
34
+
35
+ const dpr = window.devicePixelRatio || 1
36
+ canvas.width = labelWidth * dpr
37
+ canvas.height = canvasHeight * dpr
38
+ canvas.style.width = `${labelWidth}px`
39
+ canvas.style.height = `${canvasHeight}px`
40
+ ctx.scale(dpr, dpr)
41
+
42
+ ctx.fillStyle = theme.palette.background.paper
43
+ ctx.fillRect(0, 0, labelWidth, canvasHeight)
44
+
45
+ ctx.font = FONT
46
+ ctx.textBaseline = 'top'
47
+
48
+ for (let rowIdx = 0; rowIdx < samples.length; rowIdx++) {
49
+ const sample = samples[rowIdx]!
50
+ const y = rowIdx * ROW_HEIGHT
51
+
52
+ ctx.fillStyle = theme.palette.text.secondary
53
+ ctx.fillText(sample.label ?? sample.id, 2, y + 2)
54
+ }
55
+ }, [samples, labelWidth, canvasHeight, theme])
56
+
57
+ return <canvas ref={canvasRef} style={{ display: 'block' }} />
58
+ }
@@ -0,0 +1,83 @@
1
+ import React from 'react'
2
+
3
+ import { getSession } from '@jbrowse/core/util'
4
+ import { colord } from '@jbrowse/core/util/colord'
5
+ import { observer } from 'mobx-react'
6
+ import { makeStyles } from 'tss-react/mui'
7
+
8
+ import type { MafSequenceWidgetModel } from './stateModelFactory'
9
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
10
+
11
+ const useStyles = makeStyles()(theme => ({
12
+ highlight: {
13
+ height: '100%',
14
+ position: 'absolute',
15
+ background: colord(theme.palette.primary.main).alpha(0.4).toRgbString(),
16
+ borderLeft: `2px solid ${theme.palette.primary.main}`,
17
+ borderRight: `2px solid ${theme.palette.primary.main}`,
18
+ pointerEvents: 'none',
19
+ zIndex: 10,
20
+ },
21
+ }))
22
+
23
+ const MafSequenceHoverHighlight = observer(function MafSequenceHoverHighlight({
24
+ model,
25
+ }: {
26
+ model: LinearGenomeViewModel
27
+ }) {
28
+ const { classes } = useStyles()
29
+ const session = getSession(model)
30
+ const { assemblyManager } = session
31
+
32
+ // Find MafSequenceWidget instances that are connected to this view
33
+ const widgets =
34
+ 'widgets' in session ? (session.widgets as Map<string, unknown>) : undefined
35
+ if (!widgets) {
36
+ return null
37
+ }
38
+
39
+ const highlights: React.ReactNode[] = []
40
+
41
+ for (const [, widget] of widgets) {
42
+ const w = widget as {
43
+ type?: string
44
+ connectedViewId?: string
45
+ hoverHighlight?: unknown
46
+ }
47
+ if (w.type === 'MafSequenceWidget' && w.connectedViewId === model.id) {
48
+ const mafWidget = widget as MafSequenceWidgetModel
49
+ const { hoverHighlight } = mafWidget
50
+
51
+ if (hoverHighlight) {
52
+ const { refName, start, end, assemblyName } = hoverHighlight
53
+ const assembly = assemblyManager.get(assemblyName)
54
+ const canonicalRefName =
55
+ assembly?.getCanonicalRefName(refName) ?? refName
56
+
57
+ const startPx = model.bpToPx({
58
+ refName: canonicalRefName,
59
+ coord: start,
60
+ })
61
+ const endPx = model.bpToPx({ refName: canonicalRefName, coord: end })
62
+
63
+ if (startPx && endPx) {
64
+ const left =
65
+ Math.min(startPx.offsetPx, endPx.offsetPx) - model.offsetPx
66
+ const width = Math.max(Math.abs(endPx.offsetPx - startPx.offsetPx), 3)
67
+
68
+ highlights.push(
69
+ <div
70
+ key={`maf-hover-${mafWidget.id}`}
71
+ className={classes.highlight}
72
+ style={{ left, width }}
73
+ />,
74
+ )
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ return <>{highlights}</>
81
+ })
82
+
83
+ export default MafSequenceHoverHighlight
@@ -0,0 +1,24 @@
1
+ import React from 'react'
2
+
3
+ import MafSequenceHoverHighlight from './MafSequenceHoverHighlight'
4
+
5
+ import type PluginManager from '@jbrowse/core/PluginManager'
6
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
7
+
8
+ export default function MafSequenceHoverHighlightExtensionF(
9
+ pluginManager: PluginManager,
10
+ ) {
11
+ pluginManager.addToExtensionPoint(
12
+ 'LinearGenomeView-TracksContainerComponent',
13
+ (rest: React.ReactNode[], props: Record<string, unknown>) => {
14
+ const model = props.model as LinearGenomeViewModel
15
+ return [
16
+ ...rest,
17
+ <MafSequenceHoverHighlight
18
+ key="maf-sequence-hover-highlight"
19
+ model={model}
20
+ />,
21
+ ]
22
+ },
23
+ )
24
+ }