jbrowse-plugin-mafviewer 1.0.6 → 1.1.1

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 (154) hide show
  1. package/README.md +71 -22
  2. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +20 -0
  3. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +197 -0
  4. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -0
  5. package/dist/BgzipTaffyAdapter/configSchema.d.ts +44 -0
  6. package/dist/BgzipTaffyAdapter/configSchema.js +53 -0
  7. package/dist/BgzipTaffyAdapter/configSchema.js.map +1 -0
  8. package/dist/BgzipTaffyAdapter/index.d.ts +2 -0
  9. package/dist/BgzipTaffyAdapter/index.js +11 -0
  10. package/dist/BgzipTaffyAdapter/index.js.map +1 -0
  11. package/dist/BgzipTaffyAdapter/rowInstructions.d.ts +35 -0
  12. package/dist/BgzipTaffyAdapter/rowInstructions.js +55 -0
  13. package/dist/BgzipTaffyAdapter/rowInstructions.js.map +1 -0
  14. package/dist/BgzipTaffyAdapter/types.d.ts +13 -0
  15. package/dist/BgzipTaffyAdapter/types.js +2 -0
  16. package/dist/BgzipTaffyAdapter/types.js.map +1 -0
  17. package/dist/BgzipTaffyAdapter/virtualOffset.d.ts +8 -0
  18. package/dist/BgzipTaffyAdapter/virtualOffset.js +23 -0
  19. package/dist/BgzipTaffyAdapter/virtualOffset.js.map +1 -0
  20. package/dist/BigMafAdapter/BigMafAdapter.d.ts +17 -0
  21. package/dist/BigMafAdapter/BigMafAdapter.js +92 -0
  22. package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -0
  23. package/dist/BigMafAdapter/configSchema.d.ts +32 -0
  24. package/dist/BigMafAdapter/configSchema.js +39 -0
  25. package/dist/BigMafAdapter/configSchema.js.map +1 -0
  26. package/dist/BigMafAdapter/index.d.ts +2 -0
  27. package/dist/BigMafAdapter/index.js +11 -0
  28. package/dist/BigMafAdapter/index.js.map +1 -0
  29. package/dist/LinearMafDisplay/components/ColorLegend.d.ts +8 -0
  30. package/dist/LinearMafDisplay/components/ColorLegend.js +19 -0
  31. package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -0
  32. package/dist/LinearMafDisplay/components/ReactComponent.d.ts +6 -0
  33. package/dist/LinearMafDisplay/components/ReactComponent.js +50 -0
  34. package/dist/LinearMafDisplay/components/ReactComponent.js.map +1 -0
  35. package/dist/LinearMafDisplay/components/RectBg.d.ts +9 -0
  36. package/dist/LinearMafDisplay/components/RectBg.js +7 -0
  37. package/dist/LinearMafDisplay/components/RectBg.js.map +1 -0
  38. package/dist/LinearMafDisplay/components/SetRowHeight.d.ts +11 -0
  39. package/dist/LinearMafDisplay/components/SetRowHeight.js +36 -0
  40. package/dist/LinearMafDisplay/components/SetRowHeight.js.map +1 -0
  41. package/dist/LinearMafDisplay/components/SvgWrapper.d.ts +8 -0
  42. package/dist/LinearMafDisplay/components/SvgWrapper.js +21 -0
  43. package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -0
  44. package/dist/LinearMafDisplay/components/Tree.d.ts +5 -0
  45. package/dist/LinearMafDisplay/components/Tree.js +22 -0
  46. package/dist/LinearMafDisplay/components/Tree.js.map +1 -0
  47. package/dist/LinearMafDisplay/components/YScaleBars.d.ts +8 -0
  48. package/dist/LinearMafDisplay/components/YScaleBars.js +20 -0
  49. package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -0
  50. package/dist/LinearMafDisplay/components/util.d.ts +1 -0
  51. package/dist/LinearMafDisplay/components/util.js +8 -0
  52. package/dist/LinearMafDisplay/components/util.js.map +1 -0
  53. package/dist/LinearMafDisplay/configSchema.d.ts +34 -0
  54. package/dist/LinearMafDisplay/configSchema.js +15 -0
  55. package/dist/LinearMafDisplay/configSchema.js.map +1 -0
  56. package/dist/LinearMafDisplay/index.d.ts +2 -0
  57. package/dist/LinearMafDisplay/index.js +20 -0
  58. package/dist/LinearMafDisplay/index.js.map +1 -0
  59. package/dist/LinearMafDisplay/renderSvg.d.ts +4 -0
  60. package/dist/LinearMafDisplay/renderSvg.js +18 -0
  61. package/dist/LinearMafDisplay/renderSvg.js.map +1 -0
  62. package/dist/LinearMafDisplay/stateModel.d.ts +426 -0
  63. package/dist/LinearMafDisplay/stateModel.js +276 -0
  64. package/dist/LinearMafDisplay/stateModel.js.map +1 -0
  65. package/dist/LinearMafDisplay/types.d.ts +17 -0
  66. package/dist/LinearMafDisplay/types.js +16 -0
  67. package/dist/LinearMafDisplay/types.js.map +1 -0
  68. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +45 -0
  69. package/dist/LinearMafRenderer/LinearMafRenderer.js +183 -0
  70. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -0
  71. package/dist/LinearMafRenderer/components/ReactComponent.d.ts +6 -0
  72. package/dist/LinearMafRenderer/components/ReactComponent.js +8 -0
  73. package/dist/LinearMafRenderer/components/ReactComponent.js.map +1 -0
  74. package/dist/LinearMafRenderer/configSchema.d.ts +2 -0
  75. package/dist/LinearMafRenderer/configSchema.js +13 -0
  76. package/dist/LinearMafRenderer/configSchema.js.map +1 -0
  77. package/dist/LinearMafRenderer/index.d.ts +2 -0
  78. package/dist/LinearMafRenderer/index.js +12 -0
  79. package/dist/LinearMafRenderer/index.js.map +1 -0
  80. package/dist/LinearMafRenderer/util.d.ts +10 -0
  81. package/dist/LinearMafRenderer/util.js +16 -0
  82. package/dist/LinearMafRenderer/util.js.map +1 -0
  83. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.d.ts +5 -0
  84. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +128 -0
  85. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -0
  86. package/dist/MafAddTrackWorkflow/index.d.ts +2 -0
  87. package/dist/MafAddTrackWorkflow/index.js +12 -0
  88. package/dist/MafAddTrackWorkflow/index.js.map +1 -0
  89. package/dist/MafRPC/index.d.ts +16 -0
  90. package/dist/MafRPC/index.js +19 -0
  91. package/dist/MafRPC/index.js.map +1 -0
  92. package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +25 -0
  93. package/dist/MafTabixAdapter/MafTabixAdapter.js +95 -0
  94. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -0
  95. package/dist/MafTabixAdapter/configSchema.d.ts +50 -0
  96. package/dist/MafTabixAdapter/configSchema.js +56 -0
  97. package/dist/MafTabixAdapter/configSchema.js.map +1 -0
  98. package/dist/MafTabixAdapter/index.d.ts +2 -0
  99. package/dist/MafTabixAdapter/index.js +11 -0
  100. package/dist/MafTabixAdapter/index.js.map +1 -0
  101. package/dist/MafTrack/configSchema.d.ts +79 -0
  102. package/dist/MafTrack/configSchema.js +15 -0
  103. package/dist/MafTrack/configSchema.js.map +1 -0
  104. package/dist/MafTrack/index.d.ts +2 -0
  105. package/dist/MafTrack/index.js +14 -0
  106. package/dist/MafTrack/index.js.map +1 -0
  107. package/dist/index.d.ts +8 -0
  108. package/dist/index.js +26 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +65 -1
  111. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +7 -1
  112. package/dist/parseNewick.d.ts +60 -0
  113. package/dist/parseNewick.js +95 -0
  114. package/dist/parseNewick.js.map +1 -0
  115. package/dist/util.d.ts +9 -0
  116. package/dist/util.js +9 -0
  117. package/dist/util.js.map +1 -0
  118. package/package.json +37 -41
  119. package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +227 -0
  120. package/src/BgzipTaffyAdapter/configSchema.ts +59 -0
  121. package/src/BgzipTaffyAdapter/index.ts +16 -0
  122. package/src/BgzipTaffyAdapter/rowInstructions.ts +91 -0
  123. package/src/BgzipTaffyAdapter/types.ts +16 -0
  124. package/src/BgzipTaffyAdapter/virtualOffset.ts +29 -0
  125. package/src/BigMafAdapter/BigMafAdapter.ts +12 -13
  126. package/src/BigMafAdapter/configSchema.ts +11 -0
  127. package/src/BigMafAdapter/index.ts +2 -1
  128. package/src/LinearMafDisplay/components/ColorLegend.tsx +38 -27
  129. package/src/LinearMafDisplay/components/ReactComponent.tsx +68 -3
  130. package/src/LinearMafDisplay/components/SetRowHeight.tsx +15 -8
  131. package/src/LinearMafDisplay/components/SvgWrapper.tsx +39 -0
  132. package/src/LinearMafDisplay/components/Tree.tsx +33 -0
  133. package/src/LinearMafDisplay/components/YScaleBars.tsx +11 -38
  134. package/src/LinearMafDisplay/components/util.ts +7 -0
  135. package/src/LinearMafDisplay/index.ts +2 -1
  136. package/src/LinearMafDisplay/renderSvg.tsx +2 -1
  137. package/src/LinearMafDisplay/stateModel.ts +169 -18
  138. package/src/LinearMafDisplay/types.ts +41 -0
  139. package/src/LinearMafRenderer/LinearMafRenderer.ts +51 -44
  140. package/src/LinearMafRenderer/components/ReactComponent.tsx +2 -1
  141. package/src/LinearMafRenderer/index.ts +2 -1
  142. package/src/LinearMafRenderer/util.ts +20 -0
  143. package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +133 -51
  144. package/src/MafRPC/index.ts +39 -0
  145. package/src/MafTabixAdapter/MafTabixAdapter.ts +33 -27
  146. package/src/MafTabixAdapter/configSchema.ts +17 -1
  147. package/src/MafTabixAdapter/index.ts +2 -1
  148. package/src/MafTrack/index.ts +1 -0
  149. package/src/index.ts +6 -2
  150. package/src/parseNewick.ts +94 -0
  151. package/src/util.ts +11 -0
  152. package/LICENSE +0 -201
  153. package/dist/jbrowse-plugin-mafviewer.umd.development.js +0 -1439
  154. package/dist/jbrowse-plugin-mafviewer.umd.development.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { Instance, types } from 'mobx-state-tree'
1
+ import PluginManager from '@jbrowse/core/PluginManager'
2
2
  import {
3
3
  AnyConfigurationModel,
4
4
  AnyConfigurationSchemaType,
@@ -6,12 +6,26 @@ import {
6
6
  getConf,
7
7
  } from '@jbrowse/core/configuration'
8
8
  import { getEnv, getSession } from '@jbrowse/core/util'
9
- import PluginManager from '@jbrowse/core/PluginManager'
9
+ import { getRpcSessionId } from '@jbrowse/core/util/tracks'
10
10
  import { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
11
+ import { ascending } from 'd3-array'
12
+ import { type HierarchyNode, cluster, hierarchy } from 'd3-hierarchy'
13
+ import { autorun } from 'mobx'
14
+ import { Instance, addDisposer, isAlive, types } from 'mobx-state-tree'
15
+
11
16
  import SetRowHeightDialog from './components/SetRowHeight'
17
+ import {
18
+ NodeWithIds,
19
+ NodeWithIdsAndLength,
20
+ maxLength,
21
+ setBrLength,
22
+ } from './types'
23
+ import { normalize } from '../util'
12
24
 
13
- function isStrs(array: unknown[]): array is string[] {
14
- return typeof array[0] === 'string'
25
+ interface Sample {
26
+ id: string
27
+ label: string
28
+ color?: string
15
29
  }
16
30
 
17
31
  /**
@@ -52,10 +66,35 @@ export default function stateModelFactory(
52
66
  * #property
53
67
  */
54
68
  showAllLetters: false,
69
+ /**
70
+ * #property
71
+ */
72
+ mismatchRendering: true,
73
+
74
+ /**
75
+ * #property
76
+ */
77
+ showBranchLen: false,
78
+
79
+ /**
80
+ * #property
81
+ */
82
+ treeAreaWidth: 80,
55
83
  }),
56
84
  )
57
85
  .volatile(() => ({
86
+ /**
87
+ * #volatile
88
+ */
58
89
  prefersOffset: true,
90
+ /**
91
+ * #volatile
92
+ */
93
+ volatileSamples: [] as Sample[],
94
+ /**
95
+ * #volatile
96
+ */
97
+ tree: undefined as any,
59
98
  }))
60
99
  .actions(self => ({
61
100
  /**
@@ -76,26 +115,28 @@ export default function stateModelFactory(
76
115
  setShowAllLetters(f: boolean) {
77
116
  self.showAllLetters = f
78
117
  },
79
- }))
80
- .views(self => ({
81
118
  /**
82
- * #getter
119
+ * #action
83
120
  */
84
- get samples() {
85
- const r = self.adapterConfig.samples as
86
- | string[]
87
- | { id: string; label: string; color?: string }[]
88
- return isStrs(r)
89
- ? r.map(elt => ({ id: elt, label: elt, color: undefined }))
90
- : r
121
+ setMismatchRendering(f: boolean) {
122
+ self.mismatchRendering = f
91
123
  },
92
-
124
+ /**
125
+ * #action
126
+ */
127
+ setSamples({ samples, tree }: { samples: Sample[]; tree: unknown }) {
128
+ self.volatileSamples = samples
129
+ self.tree = tree
130
+ },
131
+ }))
132
+ .views(self => ({
93
133
  /**
94
134
  * #getter
95
135
  */
96
136
  get rendererTypeName() {
97
137
  return 'LinearMafRenderer'
98
138
  },
139
+
99
140
  /**
100
141
  * #getter
101
142
  */
@@ -112,12 +153,77 @@ export default function stateModelFactory(
112
153
  )
113
154
  },
114
155
  }))
156
+
157
+ .views(self => ({
158
+ /**
159
+ * #getter
160
+ */
161
+ get root() {
162
+ return self.tree
163
+ ? hierarchy(self.tree, d => d.children)
164
+ // todo: investigate whether needed, typescript says children always true
165
+ .sum(d => (d.children ? 0 : 1))
166
+ .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
167
+ : undefined
168
+ },
169
+ }))
170
+ .views(self => ({
171
+ /**
172
+ * #getter
173
+ * generates a new tree that is clustered with x,y positions
174
+ */
175
+ get hierarchy(): HierarchyNode<NodeWithIdsAndLength> | undefined {
176
+ const r = self.root
177
+ if (r) {
178
+ const width = self.treeAreaWidth
179
+ const clust = cluster<NodeWithIds>()
180
+ .size([this.totalHeight, width])
181
+ .separation(() => 1)
182
+ clust(r)
183
+ setBrLength(r, (r.data.length = 0), width / maxLength(r))
184
+ return r as HierarchyNode<NodeWithIdsAndLength>
185
+ } else {
186
+ return undefined
187
+ }
188
+ },
189
+ /**
190
+ * #getter
191
+ */
192
+ get samples() {
193
+ return this.rowNames ? normalize(this.rowNames) : self.volatileSamples
194
+ },
195
+ /**
196
+ * #getter
197
+ */
198
+ get totalHeight() {
199
+ return this.samples.length * self.rowHeight
200
+ },
201
+ /**
202
+ * #getter
203
+ */
204
+ get leaves() {
205
+ return self.root?.leaves()
206
+ },
207
+ /**
208
+ * #getter
209
+ */
210
+ get rowNames(): string[] | undefined {
211
+ return this.leaves?.map(n => n.data.name)
212
+ },
213
+ }))
115
214
  .views(self => {
116
215
  const {
117
216
  trackMenuItems: superTrackMenuItems,
217
+
118
218
  renderProps: superRenderProps,
119
219
  } = self
120
220
  return {
221
+ /**
222
+ * #getter
223
+ */
224
+ get treeWidth() {
225
+ return self.hierarchy ? self.treeAreaWidth : 0
226
+ },
121
227
  /**
122
228
  * #method
123
229
  */
@@ -128,6 +234,7 @@ export default function stateModelFactory(
128
234
  samples,
129
235
  rowHeight,
130
236
  rowProportion,
237
+ mismatchRendering,
131
238
  } = self
132
239
  return {
133
240
  ...superRenderProps(),
@@ -136,6 +243,7 @@ export default function stateModelFactory(
136
243
  rowHeight,
137
244
  rowProportion,
138
245
  showAllLetters,
246
+ mismatchRendering,
139
247
  }
140
248
  },
141
249
  /**
@@ -149,7 +257,10 @@ export default function stateModelFactory(
149
257
  onClick: () => {
150
258
  getSession(self).queueDialog(handleClose => [
151
259
  SetRowHeightDialog,
152
- { model: self, handleClose },
260
+ {
261
+ model: self,
262
+ handleClose,
263
+ },
153
264
  ])
154
265
  },
155
266
  },
@@ -157,14 +268,54 @@ export default function stateModelFactory(
157
268
  label: 'Show all letters',
158
269
  type: 'checkbox',
159
270
  checked: self.showAllLetters,
160
- onClick: () => self.setShowAllLetters(!self.showAllLetters),
271
+ onClick: () => {
272
+ self.setShowAllLetters(!self.showAllLetters)
273
+ },
274
+ },
275
+ {
276
+ label: 'Draw mismatches as single color',
277
+ type: 'checkbox',
278
+ checked: !self.mismatchRendering,
279
+ onClick: () => {
280
+ self.setMismatchRendering(!self.mismatchRendering)
281
+ },
161
282
  },
162
283
  ]
163
284
  },
164
285
  }
165
286
  })
287
+ .actions(self => ({
288
+ afterCreate() {
289
+ addDisposer(
290
+ self,
291
+ autorun(async () => {
292
+ try {
293
+ const { rpcManager } = getSession(self)
294
+ const sessionId = getRpcSessionId(self)
295
+
296
+ const results = (await rpcManager.call(
297
+ sessionId,
298
+ 'MafGetSamples',
299
+ {
300
+ sessionId,
301
+ adapterConfig: self.adapterConfig,
302
+ statusCallback: (message: string) => {
303
+ if (isAlive(self)) {
304
+ self.setMessage(message)
305
+ }
306
+ },
307
+ },
308
+ )) as any
309
+ self.setSamples(results)
310
+ } catch (e) {
311
+ console.error(e)
312
+ getSession(self).notifyError(`${e}`, e)
313
+ }
314
+ }),
315
+ )
316
+ },
317
+ }))
166
318
  .actions(self => {
167
- // eslint-disable-next-line @typescript-eslint/unbound-method
168
319
  const { renderSvg: superRenderSvg } = self
169
320
  return {
170
321
  /**
@@ -0,0 +1,41 @@
1
+ import { max } from 'd3-array'
2
+
3
+ import type { HierarchyNode } from 'd3-hierarchy'
4
+
5
+ export interface NodeWithIds {
6
+ id: string
7
+ name: string
8
+ children: NodeWithIds[]
9
+ length?: number
10
+ noTree?: boolean
11
+ }
12
+
13
+ export interface NodeWithIdsAndLength {
14
+ id: string
15
+ name: string
16
+ children: NodeWithIdsAndLength[]
17
+ noTree?: boolean
18
+ length: number
19
+ }
20
+
21
+ // basically same as maxLength from https://observablehq.com/@d3/tree-of-life
22
+ export function maxLength(d: HierarchyNode<NodeWithIds>): number {
23
+ return (
24
+ (d.data.length || 0) + (d.children ? max(d.children, maxLength) || 0 : 0)
25
+ )
26
+ }
27
+ // basically same as setRadius from https://observablehq.com/@d3/tree-of-life
28
+ export function setBrLength(
29
+ d: HierarchyNode<NodeWithIds>,
30
+ y0: number,
31
+ k: number,
32
+ ) {
33
+ // @ts-expect-error
34
+ d.len = (y0 += Math.max(d.data.length || 0, 0)) * k
35
+
36
+ if (d.children) {
37
+ d.children.forEach(d => {
38
+ setBrLength(d, y0, k)
39
+ })
40
+ }
41
+ }
@@ -7,33 +7,21 @@ import {
7
7
  featureSpanPx,
8
8
  renderToAbstractCanvas,
9
9
  } from '@jbrowse/core/util'
10
- import { Theme } from '@mui/material'
11
-
12
- export function getContrastBaseMap(theme: Theme) {
13
- return Object.fromEntries(
14
- Object.entries(getColorBaseMap(theme)).map(([key, value]) => [
15
- key,
16
- theme.palette.getContrastText(value),
17
- ]),
18
- )
19
- }
20
10
 
11
+ import { getColorBaseMap, getContrastBaseMap } from './util'
12
+
13
+ interface Sample {
14
+ id: string
15
+ color?: string
16
+ }
21
17
  interface RenderArgs extends RenderArgsDeserialized {
22
- samples: { id: string; color?: string }[]
18
+ samples: Sample[]
23
19
  rowHeight: number
24
20
  rowProportion: number
25
21
  showAllLetters: boolean
22
+ mismatchRendering: boolean
26
23
  }
27
24
 
28
- export function getColorBaseMap(theme: Theme) {
29
- const { bases } = theme.palette
30
- return {
31
- a: bases.A.main,
32
- c: bases.C.main,
33
- g: bases.G.main,
34
- t: bases.T.main,
35
- }
36
- }
37
25
  function makeImageData({
38
26
  ctx,
39
27
  renderArgs,
@@ -47,11 +35,12 @@ function makeImageData({
47
35
  rowHeight,
48
36
  showAllLetters,
49
37
  theme: configTheme,
38
+ mismatchRendering,
50
39
  samples,
51
40
  rowProportion,
52
41
  features,
53
42
  } = renderArgs
54
- const [region] = regions
43
+ const region = regions[0]!
55
44
  const h = rowHeight * rowProportion
56
45
  const theme = createJBrowseTheme(configTheme)
57
46
  const colorForBase = getColorBaseMap(theme)
@@ -76,7 +65,7 @@ function makeImageData({
76
65
 
77
66
  const row = sampleToRowMap.get(sample)
78
67
  if (row === undefined) {
79
- throw new Error(`unknown sample encountered: ${sample}`)
68
+ continue
80
69
  }
81
70
 
82
71
  const t = rowHeight * row
@@ -117,27 +106,39 @@ function makeImageData({
117
106
  for (let i = 0, o = 0; i < alignment.length; i++) {
118
107
  const c = alignment[i]
119
108
  if (seq[i] !== '-') {
120
- if ((showAllLetters || seq[i] !== c) && c !== '-') {
109
+ if (c !== '-') {
121
110
  const l = leftPx + scale * o
122
- ctx.fillStyle =
123
- colorForBase[c as keyof typeof colorForBase] ?? 'black'
124
- ctx.fillRect(l, offset + t, scale + f, h)
111
+ if (seq[i] !== c && c !== ' ') {
112
+ ctx.fillStyle = mismatchRendering
113
+ ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
114
+ (colorForBase[c as keyof typeof colorForBase] ?? 'black')
115
+ : 'orange'
116
+ ctx.fillRect(l, offset + t, scale + f, h)
117
+ } else if (showAllLetters) {
118
+ ctx.fillStyle = mismatchRendering
119
+ ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
120
+ (colorForBase[c as keyof typeof colorForBase] ?? 'black')
121
+ : 'lightblue'
122
+ ctx.fillRect(l, offset + t, scale + f, h)
123
+ }
125
124
  }
126
125
  o++
127
126
  }
128
127
  }
129
128
 
130
129
  // font
131
- const charSize = { w: 10 }
132
- if (scale >= charSize.w) {
130
+ const charSizeW = 10
131
+ if (scale >= charSizeW) {
133
132
  for (let i = 0, o = 0; i < alignment.length; i++) {
134
133
  if (seq[i] !== '-') {
135
134
  const l = leftPx + scale * o
136
- const offset = (scale - charSize.w) / 2 + 1
137
- const c = alignment[i]
135
+ const offset = (scale - charSizeW) / 2 + 1
136
+ const c = alignment[i]!
138
137
  if ((showAllLetters || seq[i] !== c) && c !== '-') {
139
- ctx.fillStyle = contrastForBase[c] ?? 'white'
140
- ctx.fillText(origAlignment[i], l + offset, hp2 + t + 3)
138
+ ctx.fillStyle = mismatchRendering
139
+ ? (contrastForBase[c] ?? 'white')
140
+ : 'black'
141
+ ctx.fillText(origAlignment[i] || '', l + offset, hp2 + t + 3)
141
142
  }
142
143
  o++
143
144
  }
@@ -158,7 +159,7 @@ function makeImageData({
158
159
  const alignment = origAlignment.toLowerCase()
159
160
  const row = sampleToRowMap.get(sample)
160
161
  if (row === undefined) {
161
- throw new Error(`unknown sample encountered: ${sample}`)
162
+ continue
162
163
  }
163
164
 
164
165
  const t = rowHeight * row
@@ -168,7 +169,7 @@ function makeImageData({
168
169
  for (let i = 0, o = 0; i < alignment.length; i++) {
169
170
  let ins = ''
170
171
  while (seq[i] === '-') {
171
- if (alignment[i] !== '-') {
172
+ if (alignment[i] !== '-' && alignment[i] !== ' ') {
172
173
  ins += alignment[i]
173
174
  }
174
175
  i++
@@ -199,18 +200,24 @@ export default class LinearMafRenderer extends FeatureRendererType {
199
200
  }
200
201
  async render(renderProps: RenderArgs) {
201
202
  const { regions, bpPerPx, samples, rowHeight } = renderProps
202
- const [region] = regions
203
- const height = samples.length * rowHeight + 100
203
+ const region = regions[0]!
204
+ const height = samples.length * (rowHeight + 1) + 100
204
205
  const width = (region.end - region.start) / bpPerPx
205
206
  const features = await this.getFeatures(renderProps)
206
- const res = await renderToAbstractCanvas(width, height, renderProps, ctx =>
207
- makeImageData({
208
- ctx,
209
- renderArgs: {
210
- ...renderProps,
211
- features,
212
- },
213
- }),
207
+ const res = await renderToAbstractCanvas(
208
+ width,
209
+ height,
210
+ renderProps,
211
+ ctx => {
212
+ makeImageData({
213
+ ctx,
214
+ renderArgs: {
215
+ ...renderProps,
216
+ features,
217
+ },
218
+ })
219
+ return undefined
220
+ },
214
221
  )
215
222
  const results = await super.render({
216
223
  ...renderProps,
@@ -1,6 +1,7 @@
1
+ import React from 'react'
2
+
1
3
  import { PrerenderedCanvas } from '@jbrowse/core/ui'
2
4
  import { observer } from 'mobx-react'
3
- import React from 'react'
4
5
 
5
6
  const LinearMafRendering = observer(function (props: {
6
7
  width: number
@@ -1,7 +1,8 @@
1
1
  import PluginManager from '@jbrowse/core/PluginManager'
2
- import configSchema from './configSchema'
2
+
3
3
  import LinearMafRenderer from './LinearMafRenderer'
4
4
  import ReactComponent from './components/ReactComponent'
5
+ import configSchema from './configSchema'
5
6
 
6
7
  export default function LinearMafRendererF(pluginManager: PluginManager) {
7
8
  pluginManager.addRendererType(
@@ -0,0 +1,20 @@
1
+ import { Theme } from '@mui/material'
2
+
3
+ export function getContrastBaseMap(theme: Theme) {
4
+ return Object.fromEntries(
5
+ Object.entries(getColorBaseMap(theme)).map(([key, value]) => [
6
+ key,
7
+ theme.palette.getContrastText(value),
8
+ ]),
9
+ )
10
+ }
11
+
12
+ export function getColorBaseMap(theme: Theme) {
13
+ const { bases } = theme.palette
14
+ return {
15
+ a: bases.A.main,
16
+ c: bases.C.main,
17
+ g: bases.G.main,
18
+ t: bases.T.main,
19
+ }
20
+ }