react-msaview 3.1.11 → 3.2.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 (191) hide show
  1. package/bundle/index.js +32 -31
  2. package/dist/colorSchemes.d.ts +2 -2
  3. package/dist/colorSchemes.js +3 -4
  4. package/dist/colorSchemes.js.map +1 -1
  5. package/dist/components/Loading.d.ts +1 -1
  6. package/dist/components/Loading.js +4 -4
  7. package/dist/components/Loading.js.map +1 -1
  8. package/dist/components/MSAView.d.ts +1 -1
  9. package/dist/components/MSAView.js +13 -9
  10. package/dist/components/MSAView.js.map +1 -1
  11. package/dist/components/ResizeHandles.d.ts +1 -1
  12. package/dist/components/ResizeHandles.js +2 -2
  13. package/dist/components/SequenceTextArea.js +4 -0
  14. package/dist/components/SequenceTextArea.js.map +1 -1
  15. package/dist/components/TextTrack.d.ts +1 -1
  16. package/dist/components/Track.d.ts +1 -1
  17. package/dist/components/VerticalScrollbar.d.ts +6 -0
  18. package/dist/components/VerticalScrollbar.js +65 -0
  19. package/dist/components/VerticalScrollbar.js.map +1 -0
  20. package/dist/components/dialogs/AddTrackDialog.d.ts +1 -1
  21. package/dist/components/dialogs/DomainDialog.d.ts +6 -0
  22. package/dist/components/dialogs/DomainDialog.js +19 -0
  23. package/dist/components/dialogs/DomainDialog.js.map +1 -0
  24. package/dist/components/dialogs/ExportSVGDialog.d.ts +1 -1
  25. package/dist/components/dialogs/FeatureDialog.d.ts +1 -1
  26. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  27. package/dist/components/dialogs/InterProScanDialog.d.ts +4 -4
  28. package/dist/components/dialogs/InterProScanDialog.js +37 -8
  29. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  30. package/dist/components/dialogs/MetadataDialog.d.ts +1 -1
  31. package/dist/components/dialogs/SettingsDialog.d.ts +1 -1
  32. package/dist/components/dialogs/SettingsDialog.js +10 -1
  33. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  34. package/dist/components/dialogs/TabPanel.d.ts +6 -0
  35. package/dist/components/dialogs/TabPanel.js +6 -0
  36. package/dist/components/dialogs/TabPanel.js.map +1 -0
  37. package/dist/components/dialogs/TracklistDialog.d.ts +1 -1
  38. package/dist/components/dialogs/UserProvidedDomainsDialog.d.ts +7 -0
  39. package/dist/components/dialogs/UserProvidedDomainsDialog.js +58 -0
  40. package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +1 -0
  41. package/dist/components/header/Header.d.ts +1 -1
  42. package/dist/components/header/Header.js +8 -5
  43. package/dist/components/header/Header.js.map +1 -1
  44. package/dist/components/header/HeaderInfoArea.d.ts +1 -1
  45. package/dist/components/header/HeaderMenu.d.ts +1 -1
  46. package/dist/components/header/HeaderMenuExtra.d.ts +1 -1
  47. package/dist/components/header/HeaderMenuExtra.js +54 -41
  48. package/dist/components/header/HeaderMenuExtra.js.map +1 -1
  49. package/dist/components/header/HeaderStatusArea.d.ts +1 -1
  50. package/dist/components/header/HeaderStatusArea.js +2 -1
  51. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  52. package/dist/components/header/MultiAlignmentSelector.d.ts +1 -1
  53. package/dist/components/header/ZoomControls.js +31 -1
  54. package/dist/components/header/ZoomControls.js.map +1 -1
  55. package/dist/components/import/ImportForm.d.ts +1 -1
  56. package/dist/components/import/ImportForm.js +1 -1
  57. package/dist/components/import/ImportForm.js.map +1 -1
  58. package/dist/components/import/ImportFormExamples.d.ts +1 -1
  59. package/dist/components/import/ImportFormExamples.js +10 -8
  60. package/dist/components/import/ImportFormExamples.js.map +1 -1
  61. package/dist/components/import/util.d.ts +2 -2
  62. package/dist/components/minimap/Minimap.d.ts +1 -1
  63. package/dist/components/minimap/Minimap.js +14 -15
  64. package/dist/components/minimap/Minimap.js.map +1 -1
  65. package/dist/components/minimap/MinimapSVG.d.ts +1 -1
  66. package/dist/components/minimap/MinimapSVG.js +1 -1
  67. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  68. package/dist/components/msa/MSACanvas.d.ts +1 -1
  69. package/dist/components/msa/MSACanvas.js +3 -3
  70. package/dist/components/msa/MSACanvas.js.map +1 -1
  71. package/dist/components/msa/MSACanvasBlock.d.ts +3 -3
  72. package/dist/components/msa/MSACanvasBlock.js +4 -3
  73. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  74. package/dist/components/msa/MSAMouseoverCanvas.d.ts +2 -2
  75. package/dist/components/msa/MSAMouseoverCanvas.js +1 -1
  76. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  77. package/dist/components/msa/MSAPanel.d.ts +1 -1
  78. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +1 -1
  79. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +2 -3
  80. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  81. package/dist/components/msa/renderMSABlock.d.ts +2 -2
  82. package/dist/components/msa/renderMSABlock.js +12 -12
  83. package/dist/components/msa/renderMSABlock.js.map +1 -1
  84. package/dist/components/msa/renderMSAMouseover.d.ts +1 -1
  85. package/dist/components/tree/TreeBranchMenu.d.ts +1 -1
  86. package/dist/components/tree/TreeCanvas.d.ts +1 -1
  87. package/dist/components/tree/TreeCanvas.js +13 -12
  88. package/dist/components/tree/TreeCanvas.js.map +1 -1
  89. package/dist/components/tree/TreeCanvasBlock.d.ts +1 -1
  90. package/dist/components/tree/TreeCanvasBlock.js +2 -1
  91. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  92. package/dist/components/tree/TreeNodeMenu.d.ts +1 -1
  93. package/dist/components/tree/TreeNodeMenu.js +2 -2
  94. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  95. package/dist/components/tree/TreePanel.d.ts +1 -1
  96. package/dist/components/tree/TreeRuler.d.ts +1 -1
  97. package/dist/components/tree/dialogs/TreeNodeInfoDialog.d.ts +1 -1
  98. package/dist/components/tree/renderTreeCanvas.d.ts +3 -3
  99. package/dist/components/tree/renderTreeCanvas.js +25 -9
  100. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  101. package/dist/components/util.js +1 -1
  102. package/dist/components/util.js.map +1 -1
  103. package/dist/fetchUtils.d.ts +1 -1
  104. package/dist/fetchUtils.js.map +1 -1
  105. package/dist/launchInterProScan.d.ts +9 -3
  106. package/dist/launchInterProScan.js +57 -22
  107. package/dist/launchInterProScan.js.map +1 -1
  108. package/dist/model/DataModel.d.ts +5 -1
  109. package/dist/model/DataModel.js +10 -1
  110. package/dist/model/DataModel.js.map +1 -1
  111. package/dist/model/DialogQueue.d.ts +1 -1
  112. package/dist/model.d.ts +138 -43
  113. package/dist/model.js +235 -110
  114. package/dist/model.js.map +1 -1
  115. package/dist/parseNewick.js +1 -1
  116. package/dist/parseNewick.js.map +1 -1
  117. package/dist/parsers/ClustalMSA.d.ts +1 -1
  118. package/dist/parsers/FastaMSA.d.ts +1 -1
  119. package/dist/parsers/StockholmMSA.d.ts +1 -1
  120. package/dist/parsers/StockholmMSA.js.map +1 -1
  121. package/dist/renderToSvg.d.ts +2 -2
  122. package/dist/renderToSvg.js +3 -28
  123. package/dist/renderToSvg.js.map +1 -1
  124. package/dist/reparseTree.d.ts +1 -1
  125. package/dist/util.d.ts +2 -2
  126. package/dist/util.js +0 -2
  127. package/dist/util.js.map +1 -1
  128. package/dist/version.d.ts +1 -1
  129. package/dist/version.js +1 -1
  130. package/dist/version.js.map +1 -1
  131. package/package.json +5 -2
  132. package/src/colorSchemes.ts +3 -2
  133. package/src/components/Checkbox2.tsx +1 -1
  134. package/src/components/Loading.tsx +11 -5
  135. package/src/components/MSAView.tsx +27 -18
  136. package/src/components/ResizeHandles.tsx +3 -3
  137. package/src/components/SequenceTextArea.tsx +8 -0
  138. package/src/components/TextTrack.tsx +1 -1
  139. package/src/components/Track.tsx +1 -1
  140. package/src/components/VerticalScrollbar.tsx +85 -0
  141. package/src/components/dialogs/AddTrackDialog.tsx +2 -2
  142. package/src/components/dialogs/DomainDialog.tsx +38 -0
  143. package/src/components/dialogs/ExportSVGDialog.tsx +1 -1
  144. package/src/components/dialogs/FeatureDialog.tsx +3 -3
  145. package/src/components/dialogs/InterProScanDialog.tsx +49 -11
  146. package/src/components/dialogs/MetadataDialog.tsx +1 -1
  147. package/src/components/dialogs/SettingsDialog.tsx +38 -3
  148. package/src/components/dialogs/TabPanel.tsx +19 -0
  149. package/src/components/dialogs/TracklistDialog.tsx +1 -1
  150. package/src/components/dialogs/UserProvidedDomainsDialog.tsx +133 -0
  151. package/src/components/header/Header.tsx +9 -6
  152. package/src/components/header/HeaderInfoArea.tsx +1 -1
  153. package/src/components/header/HeaderMenu.tsx +1 -1
  154. package/src/components/header/HeaderMenuExtra.tsx +65 -48
  155. package/src/components/header/HeaderStatusArea.tsx +3 -2
  156. package/src/components/header/MultiAlignmentSelector.tsx +1 -1
  157. package/src/components/header/ZoomControls.tsx +34 -0
  158. package/src/components/import/ImportForm.tsx +3 -3
  159. package/src/components/import/ImportFormExamples.tsx +19 -17
  160. package/src/components/import/util.ts +2 -2
  161. package/src/components/minimap/Minimap.tsx +15 -22
  162. package/src/components/minimap/MinimapSVG.tsx +2 -2
  163. package/src/components/msa/MSACanvas.tsx +11 -4
  164. package/src/components/msa/MSACanvasBlock.tsx +5 -4
  165. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -6
  166. package/src/components/msa/MSAPanel.tsx +1 -1
  167. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +5 -6
  168. package/src/components/msa/renderMSABlock.ts +37 -17
  169. package/src/components/msa/renderMSAMouseover.ts +1 -1
  170. package/src/components/tree/TreeBranchMenu.tsx +1 -1
  171. package/src/components/tree/TreeCanvas.tsx +15 -16
  172. package/src/components/tree/TreeCanvasBlock.tsx +3 -2
  173. package/src/components/tree/TreeNodeMenu.tsx +3 -3
  174. package/src/components/tree/TreePanel.tsx +1 -1
  175. package/src/components/tree/TreeRuler.tsx +1 -1
  176. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +1 -1
  177. package/src/components/tree/renderTreeCanvas.ts +32 -12
  178. package/src/components/util.ts +1 -1
  179. package/src/fetchUtils.ts +2 -2
  180. package/src/launchInterProScan.ts +69 -24
  181. package/src/model/DataModel.ts +10 -0
  182. package/src/model/DialogQueue.ts +1 -1
  183. package/src/model.ts +262 -143
  184. package/src/parseNewick.ts +1 -1
  185. package/src/parsers/ClustalMSA.ts +1 -1
  186. package/src/parsers/FastaMSA.ts +1 -1
  187. package/src/parsers/StockholmMSA.ts +1 -1
  188. package/src/renderToSvg.tsx +6 -30
  189. package/src/reparseTree.ts +1 -1
  190. package/src/util.ts +2 -4
  191. package/src/version.ts +1 -1
package/src/model.ts CHANGED
@@ -1,23 +1,23 @@
1
1
  import React from 'react'
2
+ import type { Buffer } from 'buffer'
2
3
  import { autorun, transaction } from 'mobx'
3
- import { Instance, cast, types, addDisposer } from 'mobx-state-tree'
4
- import { hierarchy, cluster, HierarchyNode } from 'd3-hierarchy'
4
+ import { type Instance, cast, types, addDisposer } from 'mobx-state-tree'
5
+ import { hierarchy, cluster, type HierarchyNode } from 'd3-hierarchy'
5
6
  import { ascending } from 'd3-array'
6
7
  import Stockholm from 'stockholm-js'
7
8
  import { saveAs } from 'file-saver'
8
- import { Theme } from '@mui/material'
9
+ import type { Theme } from '@mui/material'
10
+ import { ungzip } from 'pako'
9
11
 
10
12
  // jbrowse
11
13
  import { FileLocation, ElementId } from '@jbrowse/core/util/types/mst'
12
- import { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
14
+ import type { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
13
15
  import { openLocation } from '@jbrowse/core/util/io'
14
- import {
15
- groupBy,
16
- localStorageGetItem,
17
- localStorageSetItem,
18
- notEmpty,
19
- sum,
20
- } from '@jbrowse/core/util'
16
+ import { groupBy, notEmpty, sum } from '@jbrowse/core/util'
17
+
18
+ export function isGzip(buf: Buffer) {
19
+ return buf[0] === 31 && buf[1] === 139 && buf[2] === 8
20
+ }
21
21
 
22
22
  // locals
23
23
  import {
@@ -27,8 +27,8 @@ import {
27
27
  maxLength,
28
28
  setBrLength,
29
29
  skipBlanks,
30
- NodeWithIds,
31
- NodeWithIdsAndLength,
30
+ type NodeWithIds,
31
+ type NodeWithIdsAndLength,
32
32
  len,
33
33
  } from './util'
34
34
  import { colord } from 'colord'
@@ -52,11 +52,7 @@ import { DataModelF } from './model/DataModel'
52
52
  import { DialogQueueSessionMixin } from './model/DialogQueue'
53
53
  import { TreeF } from './model/treeModel'
54
54
  import { MSAModelF } from './model/msaModel'
55
- import {
56
- InterProScanResults,
57
- launchInterProScan,
58
- loadInterProScanResults,
59
- } from './launchInterProScan'
55
+ import type { InterProScanResults } from './launchInterProScan'
60
56
 
61
57
  export interface Accession {
62
58
  accession: string
@@ -102,10 +98,20 @@ function stateModelFactory() {
102
98
  * id of view, randomly generated if not provided
103
99
  */
104
100
  id: ElementId,
101
+
102
+ /**
103
+ * #property
104
+ */
105
+ showDomains: false,
106
+ /**
107
+ * #property
108
+ */
109
+ allowedGappyness: 100,
105
110
  /**
106
111
  * #property
107
112
  */
108
- featureMode: false,
113
+ contrastLettering: true,
114
+
109
115
  /**
110
116
  * #property
111
117
  */
@@ -117,6 +123,20 @@ function stateModelFactory() {
117
123
  */
118
124
  type: types.literal('MsaView'),
119
125
 
126
+ /**
127
+ * #property
128
+ */
129
+ drawMsaLetters: true,
130
+ /**
131
+ * #property
132
+ */
133
+ hideGaps: true,
134
+
135
+ /**
136
+ * #property
137
+ */
138
+ drawTreeText: true,
139
+
120
140
  /**
121
141
  * #property
122
142
  * height of the div containing the view, px
@@ -174,15 +194,17 @@ function stateModelFactory() {
174
194
 
175
195
  /**
176
196
  * #property
177
- * array of tree parent nodes that are 'collapsed'
197
+ * array of tree parent nodes that are 'collapsed' (all children are
198
+ * hidden)
178
199
  */
179
200
  collapsed: types.array(types.string),
180
201
 
181
202
  /**
182
203
  * #property
183
- * array of tree leaf nodes that are 'collapsed'
204
+ * array of tree leaf nodes that are 'collapsed' (just that leaf node
205
+ * is hidden)
184
206
  */
185
- collapsed2: types.array(types.string),
207
+ collapsedLeaves: types.array(types.string),
186
208
  /**
187
209
  * #property
188
210
  * focus on particular subtree
@@ -200,6 +222,7 @@ function stateModelFactory() {
200
222
  * autorun
201
223
  */
202
224
  data: types.optional(DataModelF(), { tree: '', msa: '' }),
225
+
203
226
  /**
204
227
  * #property
205
228
  */
@@ -207,6 +230,13 @@ function stateModelFactory() {
207
230
  }),
208
231
  )
209
232
  .volatile(() => ({
233
+ /**
234
+ * #volatile
235
+ */
236
+ headerHeight: 0,
237
+ /**
238
+ * #volatile
239
+ */
210
240
  status: undefined as { msg: string; url?: string } | undefined,
211
241
  /**
212
242
  * #volatile
@@ -225,7 +255,7 @@ function stateModelFactory() {
225
255
  /**
226
256
  * #volatile
227
257
  */
228
- width: 800,
258
+ volatileWidth: undefined as number | undefined,
229
259
  /**
230
260
  * #volatile
231
261
  * resize handle width between tree and msa area, px
@@ -236,7 +266,7 @@ function stateModelFactory() {
236
266
  * #volatile
237
267
  * size of blocks of content to be drawn, px
238
268
  */
239
- blockSize: 1000,
269
+ blockSize: 500,
240
270
 
241
271
  /**
242
272
  * #volatile
@@ -293,17 +323,29 @@ function stateModelFactory() {
293
323
  * #volatile
294
324
  *
295
325
  */
296
- loadedInterProAnnotations: undefined as
326
+ interProAnnotations: undefined as
297
327
  | undefined
298
328
  | Record<string, InterProScanResults>,
299
- /**
300
- * #volatile
301
- */
302
- interProScanJobIds: JSON.parse(
303
- localStorageGetItem('msaview-interproscanqueries') || '[]',
304
- ) as { jobId: string; date: number }[],
305
329
  }))
306
330
  .actions(self => ({
331
+ /**
332
+ * #action
333
+ */
334
+ setHideGaps(arg: boolean) {
335
+ self.hideGaps = arg
336
+ },
337
+ /**
338
+ * #action
339
+ */
340
+ setAllowedGappyness(arg: number) {
341
+ self.allowedGappyness = arg
342
+ },
343
+ /**
344
+ * #action
345
+ */
346
+ setContrastLettering(arg: boolean) {
347
+ self.contrastLettering = arg
348
+ },
307
349
  /**
308
350
  * #action
309
351
  */
@@ -320,7 +362,7 @@ function stateModelFactory() {
320
362
  * #action
321
363
  */
322
364
  setWidth(arg: number) {
323
- self.width = arg
365
+ self.volatileWidth = arg
324
366
  },
325
367
  /**
326
368
  * #action
@@ -349,8 +391,8 @@ function stateModelFactory() {
349
391
  /**
350
392
  * #action
351
393
  */
352
- setFeatureMode(arg: boolean) {
353
- self.featureMode = arg
394
+ setShowDomains(arg: boolean) {
395
+ self.showDomains = arg
354
396
  },
355
397
 
356
398
  /**
@@ -415,10 +457,10 @@ function stateModelFactory() {
415
457
  * #action
416
458
  */
417
459
  toggleCollapsed2(node: string) {
418
- if (self.collapsed2.includes(node)) {
419
- self.collapsed2.remove(node)
460
+ if (self.collapsedLeaves.includes(node)) {
461
+ self.collapsedLeaves.remove(node)
420
462
  } else {
421
- self.collapsed2.push(node)
463
+ self.collapsedLeaves.push(node)
422
464
  }
423
465
  },
424
466
  /**
@@ -471,6 +513,29 @@ function stateModelFactory() {
471
513
  },
472
514
  }))
473
515
 
516
+ .views(self => ({
517
+ /**
518
+ * #getter
519
+ */
520
+ get actuallyShowDomains() {
521
+ return self.showDomains && !!self.interProAnnotations
522
+ },
523
+ /**
524
+ * #getter
525
+ */
526
+ get viewInitialized() {
527
+ return self.volatileWidth !== undefined
528
+ },
529
+ /**
530
+ * #getter
531
+ */
532
+ get width() {
533
+ if (self.volatileWidth === undefined) {
534
+ throw new Error('not initialized')
535
+ }
536
+ return self.volatileWidth
537
+ },
538
+ }))
474
539
  .views(self => ({
475
540
  /**
476
541
  * #method
@@ -512,13 +577,13 @@ function stateModelFactory() {
512
577
  * #getter
513
578
  */
514
579
  get noTree() {
515
- return !!this._tree.noTree
580
+ return !!this.tree.noTree
516
581
  },
517
582
  /**
518
583
  * #getter
519
584
  */
520
- get noAnnotations() {
521
- return !self.loadedInterProAnnotations
585
+ get noDomains() {
586
+ return !self.interProAnnotations
522
587
  },
523
588
  /**
524
589
  * #getter
@@ -540,11 +605,11 @@ function stateModelFactory() {
540
605
  if (text) {
541
606
  if (Stockholm.sniff(text)) {
542
607
  return new StockholmMSA(text, self.currentAlignment)
543
- } else if (text.startsWith('>')) {
608
+ }
609
+ if (text.startsWith('>')) {
544
610
  return new FastaMSA(text)
545
- } else {
546
- return new ClustalMSA(text)
547
611
  }
612
+ return new ClustalMSA(text)
548
613
  }
549
614
  return null
550
615
  },
@@ -558,7 +623,7 @@ function stateModelFactory() {
558
623
  /**
559
624
  * #getter
560
625
  */
561
- get _tree(): NodeWithIds {
626
+ get tree(): NodeWithIds {
562
627
  const ret = self.data.tree
563
628
  ? generateNodeIds(parseNewick(self.data.tree))
564
629
  : this.MSA?.getTree() || {
@@ -573,7 +638,7 @@ function stateModelFactory() {
573
638
  * #getter
574
639
  */
575
640
  get rowNames(): string[] {
576
- return this.hierarchy.leaves().map(n => n.data.name)
641
+ return this.leaves.map(n => n.data.name)
577
642
  },
578
643
  /**
579
644
  * #getter
@@ -587,7 +652,7 @@ function stateModelFactory() {
587
652
  * #getter
588
653
  */
589
654
  get root() {
590
- let hier = hierarchy(this._tree, d => d.branchset)
655
+ let hier = hierarchy(this.tree, d => d.branchset)
591
656
  .sum(d => (d.branchset ? 0 : 1))
592
657
  .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
593
658
 
@@ -598,7 +663,7 @@ function stateModelFactory() {
598
663
  }
599
664
  }
600
665
 
601
- ;[...self.collapsed, ...self.collapsed2]
666
+ ;[...self.collapsed, ...self.collapsedLeaves]
602
667
  .map(collapsedId => hier.find(node => node.data.id === collapsedId))
603
668
  .filter(notEmpty)
604
669
  .map(node => collapse(node))
@@ -624,9 +689,9 @@ function stateModelFactory() {
624
689
  * #getter
625
690
  */
626
691
  get blanks() {
692
+ const { allowedGappyness } = self
627
693
  const blanks = []
628
- const strs = this.hierarchy
629
- .leaves()
694
+ const strs = this.leaves
630
695
  .map(leaf => this.MSA?.getRow(leaf.data.name))
631
696
  .filter((item): item is string => !!item)
632
697
 
@@ -637,7 +702,7 @@ function stateModelFactory() {
637
702
  counter++
638
703
  }
639
704
  }
640
- if (counter === strs.length) {
705
+ if (counter / strs.length >= allowedGappyness / 100) {
641
706
  blanks.push(i)
642
707
  }
643
708
  }
@@ -648,8 +713,7 @@ function stateModelFactory() {
648
713
  */
649
714
  get rows() {
650
715
  const MSA = this.MSA
651
- return this.hierarchy
652
- .leaves()
716
+ return this.leaves
653
717
  .map(leaf => [leaf.data.name, MSA?.getRow(leaf.data.name)] as const)
654
718
  .filter((f): f is [string, string] => !!f[1])
655
719
  },
@@ -693,6 +757,17 @@ function stateModelFactory() {
693
757
  }
694
758
  return r
695
759
  },
760
+
761
+ /**
762
+ * #getter
763
+ */
764
+ get colStatsSums() {
765
+ return Object.fromEntries(
766
+ Object.entries(this.colStats).map(([key, val]) => {
767
+ return [key, sum(Object.values(val))]
768
+ }),
769
+ )
770
+ },
696
771
  /**
697
772
  * #getter
698
773
  * generates a new tree that is clustered with x,y positions
@@ -713,6 +788,13 @@ function stateModelFactory() {
713
788
  get totalHeight() {
714
789
  return this.root.leaves().length * self.rowHeight
715
790
  },
791
+
792
+ /**
793
+ * #getter
794
+ */
795
+ get leaves() {
796
+ return this.hierarchy.leaves()
797
+ },
716
798
  }))
717
799
  .views(self => ({
718
800
  /**
@@ -727,7 +809,7 @@ function stateModelFactory() {
727
809
  /**
728
810
  * #getter
729
811
  */
730
- get initialized() {
812
+ get dataInitialized() {
731
813
  return (self.data.msa || self.data.tree) && !self.error
732
814
  },
733
815
  /**
@@ -779,29 +861,78 @@ function stateModelFactory() {
779
861
  get maxScrollX() {
780
862
  return -self.totalWidth + (self.msaAreaWidth - 100)
781
863
  },
864
+ /**
865
+ * #getter
866
+ */
867
+ get showMsaLetters() {
868
+ return self.drawMsaLetters && self.rowHeight >= 5
869
+ },
870
+ /**
871
+ * #getter
872
+ */
873
+ get showTreeText() {
874
+ return self.drawLabels && self.rowHeight >= 5
875
+ },
782
876
  }))
783
877
  .actions(self => ({
784
878
  /**
785
879
  * #action
786
880
  */
787
- zoomIn() {
881
+ setDrawMsaLetters(arg: boolean) {
882
+ self.drawMsaLetters = arg
883
+ },
884
+
885
+ /**
886
+ * #action
887
+ */
888
+ zoomOutHorizontal() {
889
+ self.colWidth = Math.max(1, Math.floor(self.colWidth * 0.75))
890
+ self.scrollX = clamp(self.maxScrollX, self.scrollX, 0)
891
+ },
892
+ /**
893
+ * #action
894
+ */
895
+ zoomInHorizontal() {
788
896
  self.colWidth = Math.ceil(self.colWidth * 1.5)
789
- self.rowHeight = Math.ceil(self.rowHeight * 1.5)
790
897
  self.scrollX = clamp(self.maxScrollX, self.scrollX, 0)
791
898
  },
792
899
  /**
793
900
  * #action
794
901
  */
795
- zoomOut() {
796
- self.colWidth = Math.max(1, Math.floor(self.colWidth * 0.75))
902
+ zoomInVertical() {
903
+ self.rowHeight = Math.ceil(self.rowHeight * 1.5)
904
+ },
905
+ /**
906
+ * #action
907
+ */
908
+ zoomOutVertical() {
797
909
  self.rowHeight = Math.max(1.5, Math.floor(self.rowHeight * 0.75))
798
- self.scrollX = clamp(self.maxScrollX, self.scrollX, 0)
799
910
  },
800
911
  /**
801
912
  * #action
802
913
  */
803
- setLoadedInterProAnnotations(data: Record<string, InterProScanResults>) {
804
- self.loadedInterProAnnotations = data
914
+ zoomIn() {
915
+ transaction(() => {
916
+ self.colWidth = Math.ceil(self.colWidth * 1.5)
917
+ self.rowHeight = Math.ceil(self.rowHeight * 1.5)
918
+ self.scrollX = clamp(self.maxScrollX, self.scrollX, 0)
919
+ })
920
+ },
921
+ /**
922
+ * #action
923
+ */
924
+ zoomOut() {
925
+ transaction(() => {
926
+ self.colWidth = Math.max(1, Math.floor(self.colWidth * 0.75))
927
+ self.rowHeight = Math.max(1.5, Math.floor(self.rowHeight * 0.75))
928
+ self.scrollX = clamp(self.maxScrollX, self.scrollX, 0)
929
+ })
930
+ },
931
+ /**
932
+ * #action
933
+ */
934
+ setInterProAnnotations(data: Record<string, InterProScanResults>) {
935
+ self.interProAnnotations = data
805
936
  },
806
937
 
807
938
  /**
@@ -835,15 +966,6 @@ function stateModelFactory() {
835
966
  self.turnedOffTracks.set(id, true)
836
967
  }
837
968
  },
838
- /**
839
- * #action
840
- */
841
- addInterProScanJobId(arg: string) {
842
- self.interProScanJobIds = [
843
- ...self.interProScanJobIds,
844
- { jobId: arg, date: +Date.now() },
845
- ]
846
- },
847
969
  /**
848
970
  * #action
849
971
  */
@@ -857,9 +979,9 @@ function stateModelFactory() {
857
979
  */
858
980
  get labelsWidth() {
859
981
  let x = 0
860
- const { rowHeight, hierarchy, treeMetadata, fontSize } = self
982
+ const { rowHeight, leaves, treeMetadata, fontSize } = self
861
983
  if (rowHeight > 5) {
862
- for (const node of hierarchy.leaves()) {
984
+ for (const node of leaves) {
863
985
  x = Math.max(
864
986
  measureTextCanvas(
865
987
  treeMetadata[node.data.name]?.genome || node.data.name,
@@ -931,6 +1053,13 @@ function stateModelFactory() {
931
1053
  return this.tracks.filter(f => !self.turnedOffTracks.has(f.model.id))
932
1054
  },
933
1055
 
1056
+ /**
1057
+ * #getter
1058
+ */
1059
+ get showHorizontalScrollbar() {
1060
+ return self.msaAreaWidth < self.totalWidth
1061
+ },
1062
+
934
1063
  /**
935
1064
  * #method
936
1065
  * return a row-specific sequence coordinate, skipping gaps, given a global
@@ -982,6 +1111,17 @@ function stateModelFactory() {
982
1111
  }))
983
1112
 
984
1113
  .views(self => ({
1114
+ /**
1115
+ * #getter
1116
+ * widget width minus the tree area gives the space for the MSA
1117
+ */
1118
+ get msaAreaHeight() {
1119
+ return (
1120
+ self.height -
1121
+ (self.showHorizontalScrollbar ? self.minimapHeight : 0) -
1122
+ self.headerHeight
1123
+ )
1124
+ },
985
1125
  /**
986
1126
  * #getter
987
1127
  * total height of track area (px)
@@ -1001,11 +1141,14 @@ function stateModelFactory() {
1001
1141
  }
1002
1142
  return types
1003
1143
  },
1144
+ /**
1145
+ * #getter
1146
+ */
1004
1147
  get tidyAnnotations() {
1005
1148
  const ret = []
1006
- const { loadedInterProAnnotations } = self
1007
- if (loadedInterProAnnotations) {
1008
- for (const [id, val] of Object.entries(loadedInterProAnnotations)) {
1149
+ const { interProAnnotations } = self
1150
+ if (interProAnnotations) {
1151
+ for (const [id, val] of Object.entries(interProAnnotations)) {
1009
1152
  for (const { signature, locations } of val.matches) {
1010
1153
  const { entry } = signature
1011
1154
  if (entry) {
@@ -1042,6 +1185,23 @@ function stateModelFactory() {
1042
1185
  },
1043
1186
  }))
1044
1187
  .views(self => ({
1188
+ /**
1189
+ * #getter
1190
+ */
1191
+ get showVerticalScrollbar() {
1192
+ return self.msaAreaHeight < self.totalHeight
1193
+ },
1194
+ }))
1195
+ .views(self => ({
1196
+ /**
1197
+ * #getter
1198
+ */
1199
+ get verticalScrollbarWidth() {
1200
+ return self.showVerticalScrollbar ? 20 : 0
1201
+ },
1202
+ /**
1203
+ * #getter
1204
+ */
1045
1205
  get fillPalette() {
1046
1206
  const arr = [...self.tidyTypes.keys()]
1047
1207
  let i = 0
@@ -1053,6 +1213,9 @@ function stateModelFactory() {
1053
1213
  }
1054
1214
  return map
1055
1215
  },
1216
+ /**
1217
+ * #getter
1218
+ */
1056
1219
  get strokePalette() {
1057
1220
  return Object.fromEntries(
1058
1221
  Object.entries(this.fillPalette).map(([key, val]) => [
@@ -1066,49 +1229,20 @@ function stateModelFactory() {
1066
1229
  /**
1067
1230
  * #action
1068
1231
  */
1069
- async loadInterProScanResults(jobId: string) {
1070
- self.setStatus({ msg: 'Loading ' + jobId })
1071
- const ret = await loadInterProScanResults(jobId)
1072
- self.setStatus()
1073
- self.setLoadedInterProAnnotations(
1074
- Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])),
1075
- )
1076
- },
1077
- /**
1078
- * #action
1079
- */
1080
- async queryInterProScan(programs: string[]) {
1081
- const { rows } = self
1082
- if (rows.length > 140) {
1083
- throw new Error('Too many sequences, please run InterProScan offline')
1084
- }
1085
-
1086
- const ret = await launchInterProScan({
1087
- algorithm: 'interproscan',
1088
- programs: programs,
1089
- seq: rows
1090
- .map(row => `>${row[0]}\n${row[1].replaceAll('-', '')}`)
1091
- .join('\n'),
1092
- onProgress: arg => self.setStatus(arg),
1093
- onJobId: jobId => self.addInterProScanJobId(jobId),
1094
- })
1095
-
1096
- self.setLoadedInterProAnnotations(
1097
- Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])),
1098
- )
1232
+ setHeaderHeight(arg: number) {
1233
+ self.headerHeight = arg
1099
1234
  },
1100
1235
  /**
1101
1236
  * #action
1102
1237
  */
1103
1238
  reset() {
1104
- transaction(() => {
1105
- self.setData({ tree: '', msa: '' })
1106
- self.setScrollY(0)
1107
- self.setScrollX(0)
1108
- self.setCurrentAlignment(0)
1109
- self.setTreeFilehandle(undefined)
1110
- self.setMSAFilehandle(undefined)
1111
- })
1239
+ self.setData({ tree: '', msa: '' })
1240
+ self.setError(undefined)
1241
+ self.setScrollY(0)
1242
+ self.setScrollX(0)
1243
+ self.setCurrentAlignment(0)
1244
+ self.setTreeFilehandle(undefined)
1245
+ self.setMSAFilehandle(undefined)
1112
1246
  },
1113
1247
  /**
1114
1248
  * #action
@@ -1156,25 +1290,7 @@ function stateModelFactory() {
1156
1290
  }
1157
1291
  }),
1158
1292
  )
1159
- addDisposer(
1160
- self,
1161
- autorun(async () => {
1162
- // const res = Object.fromEntries(
1163
- // await Promise.all(
1164
- // self.annotationTracks.map(async f => {
1165
- // const d = await jsonfetch(
1166
- // `https://jbrowse.org/demos/interproscan/json/${encodeURIComponent(f)}`,
1167
- // )
1168
- // return [
1169
- // decodeURIComponent(f).replace('.json', ''),
1170
- // d.result.results[0],
1171
- // ] as const
1172
- // }),
1173
- // ),
1174
- // )
1175
- // self.setLoadedInterProAnnotations(res)
1176
- }),
1177
- )
1293
+
1178
1294
  // autorun opens treeFilehandle
1179
1295
  addDisposer(
1180
1296
  self,
@@ -1225,9 +1341,11 @@ function stateModelFactory() {
1225
1341
  if (msaFilehandle) {
1226
1342
  try {
1227
1343
  self.setLoadingMSA(true)
1228
- const res = await openLocation(msaFilehandle).readFile('utf8')
1344
+ const res = await openLocation(msaFilehandle).readFile()
1345
+ const buf = isGzip(res) ? ungzip(res) : res
1346
+ const txt = new TextDecoder('utf8').decode(buf)
1229
1347
  transaction(() => {
1230
- self.setMSA(res)
1348
+ self.setMSA(txt)
1231
1349
  if (msaFilehandle.locationType === 'BlobLocation') {
1232
1350
  // clear filehandle after loading if from a local file
1233
1351
  self.setMSAFilehandle(undefined)
@@ -1243,6 +1361,17 @@ function stateModelFactory() {
1243
1361
  }),
1244
1362
  )
1245
1363
 
1364
+ addDisposer(
1365
+ self,
1366
+ autorun(() => {
1367
+ // force colStats not to go stale,
1368
+ // xref solution https://github.com/mobxjs/mobx/issues/266#issuecomment-222007278
1369
+ // xref problem https://github.com/GMOD/react-msaview/issues/75
1370
+ self.colStats
1371
+ self.colStatsSums
1372
+ self.columns
1373
+ }),
1374
+ )
1246
1375
  // autorun synchronizes treeWidth with treeAreaWidth
1247
1376
  addDisposer(
1248
1377
  self,
@@ -1257,16 +1386,6 @@ function stateModelFactory() {
1257
1386
  }
1258
1387
  }),
1259
1388
  )
1260
-
1261
- addDisposer(
1262
- self,
1263
- autorun(() => {
1264
- localStorageSetItem(
1265
- 'msaview-interproscanqueries',
1266
- JSON.stringify(self.interProScanJobIds),
1267
- )
1268
- }),
1269
- )
1270
1389
  },
1271
1390
  }))
1272
1391
  .postProcessSnapshot(result => {