react-msaview 4.8.1 → 5.0.3

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 (240) hide show
  1. package/README.md +9 -2
  2. package/bundle/index.js +87 -187
  3. package/bundle/index.js.LICENSE.txt +0 -83
  4. package/bundle/index.js.map +1 -1
  5. package/dist/colorSchemes.js +1 -1
  6. package/dist/colorSchemes.js.map +1 -1
  7. package/dist/components/ConservationTrack.d.ts +2 -2
  8. package/dist/components/ConservationTrack.js +1 -2
  9. package/dist/components/ConservationTrack.js.map +1 -1
  10. package/dist/components/Loading.d.ts +1 -1
  11. package/dist/components/Loading.js +2 -2
  12. package/dist/components/Loading.js.map +1 -1
  13. package/dist/components/MSAView.d.ts +1 -1
  14. package/dist/components/MSAView.js +8 -8
  15. package/dist/components/MSAView.js.map +1 -1
  16. package/dist/components/ResizeHandles.d.ts +1 -1
  17. package/dist/components/SequenceTextArea.js +2 -2
  18. package/dist/components/SequenceTextArea.js.map +1 -1
  19. package/dist/components/TextTrack.d.ts +2 -2
  20. package/dist/components/TextTrack.js +1 -1
  21. package/dist/components/TextTrack.js.map +1 -1
  22. package/dist/components/Track.d.ts +1 -1
  23. package/dist/components/Track.js +1 -1
  24. package/dist/components/Track.js.map +1 -1
  25. package/dist/components/VerticalScrollbar.d.ts +1 -1
  26. package/dist/components/dialogs/AboutDialog.js +1 -1
  27. package/dist/components/dialogs/AboutDialog.js.map +1 -1
  28. package/dist/components/dialogs/AddTrackDialog.d.ts +1 -1
  29. package/dist/components/dialogs/DomainDialog.d.ts +1 -1
  30. package/dist/components/dialogs/DomainDialog.js +3 -3
  31. package/dist/components/dialogs/DomainDialog.js.map +1 -1
  32. package/dist/components/dialogs/ExportSVGDialog.d.ts +1 -1
  33. package/dist/components/dialogs/ExportSVGDialog.js +1 -1
  34. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  35. package/dist/components/dialogs/FeatureDialog.d.ts +1 -1
  36. package/dist/components/dialogs/FeatureDialog.js +1 -1
  37. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  38. package/dist/components/dialogs/InterProScanDialog.d.ts +1 -1
  39. package/dist/components/dialogs/InterProScanDialog.js +1 -1
  40. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  41. package/dist/components/dialogs/MetadataDialog.d.ts +1 -1
  42. package/dist/components/dialogs/MetadataDialog.js +1 -1
  43. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  44. package/dist/components/dialogs/SettingsDialog.d.ts +1 -1
  45. package/dist/components/dialogs/SettingsDialog.js +2 -2
  46. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  47. package/dist/components/dialogs/TrackInfoDialog.js +1 -1
  48. package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
  49. package/dist/components/dialogs/UserProvidedDomainsDialog.d.ts +1 -1
  50. package/dist/components/dialogs/UserProvidedDomainsDialog.js +1 -1
  51. package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +1 -1
  52. package/dist/components/header/GappynessSlider.d.ts +1 -1
  53. package/dist/components/header/Header.d.ts +1 -1
  54. package/dist/components/header/Header.js +10 -10
  55. package/dist/components/header/Header.js.map +1 -1
  56. package/dist/components/header/HeaderInfoArea.d.ts +1 -1
  57. package/dist/components/header/HeaderMenu.d.ts +1 -1
  58. package/dist/components/header/HeaderMenu.js +6 -6
  59. package/dist/components/header/HeaderMenu.js.map +1 -1
  60. package/dist/components/header/HeaderStatusArea.d.ts +1 -1
  61. package/dist/components/header/MultiAlignmentSelector.d.ts +1 -1
  62. package/dist/components/header/SettingsMenu.d.ts +1 -1
  63. package/dist/components/header/SettingsMenu.js +1 -1
  64. package/dist/components/header/SettingsMenu.js.map +1 -1
  65. package/dist/components/header/ZoomControls.d.ts +1 -1
  66. package/dist/components/header/ZoomMenu.d.ts +1 -1
  67. package/dist/components/header/ZoomStar.d.ts +1 -1
  68. package/dist/components/import/ImportForm.d.ts +1 -1
  69. package/dist/components/import/ImportForm.js +2 -2
  70. package/dist/components/import/ImportForm.js.map +1 -1
  71. package/dist/components/import/ImportFormExamples.d.ts +1 -1
  72. package/dist/components/import/ImportFormExamples.js +2 -2
  73. package/dist/components/import/ImportFormExamples.js.map +1 -1
  74. package/dist/components/import/util.d.ts +1 -1
  75. package/dist/components/minimap/Minimap.d.ts +1 -1
  76. package/dist/components/minimap/MinimapSVG.d.ts +1 -1
  77. package/dist/components/msa/MSACanvas.d.ts +1 -1
  78. package/dist/components/msa/MSACanvas.js +2 -2
  79. package/dist/components/msa/MSACanvas.js.map +1 -1
  80. package/dist/components/msa/MSACanvasBlock.d.ts +1 -1
  81. package/dist/components/msa/MSACanvasBlock.js +3 -3
  82. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  83. package/dist/components/msa/MSAMouseoverCanvas.d.ts +1 -1
  84. package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
  85. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  86. package/dist/components/msa/MSAPanel.d.ts +1 -1
  87. package/dist/components/msa/MSAPanel.js +2 -2
  88. package/dist/components/msa/MSAPanel.js.map +1 -1
  89. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +1 -1
  90. package/dist/components/msa/renderMSABlock.d.ts +1 -1
  91. package/dist/components/msa/renderMSAMouseover.d.ts +1 -1
  92. package/dist/components/tracks/renderTracksSvg.d.ts +2 -2
  93. package/dist/components/tree/TreeBranchMenu.d.ts +1 -1
  94. package/dist/components/tree/TreeCanvas.d.ts +1 -1
  95. package/dist/components/tree/TreeCanvas.js +3 -3
  96. package/dist/components/tree/TreeCanvas.js.map +1 -1
  97. package/dist/components/tree/TreeCanvasBlock.d.ts +1 -1
  98. package/dist/components/tree/TreeCanvasBlock.js +3 -3
  99. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  100. package/dist/components/tree/TreeNodeMenu.d.ts +1 -1
  101. package/dist/components/tree/TreeNodeMenu.js +1 -1
  102. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  103. package/dist/components/tree/TreePanel.d.ts +1 -1
  104. package/dist/components/tree/TreePanel.js +1 -1
  105. package/dist/components/tree/TreePanel.js.map +1 -1
  106. package/dist/components/tree/TreeRuler.d.ts +1 -1
  107. package/dist/components/tree/dialogs/TreeNodeInfoDialog.d.ts +1 -1
  108. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +1 -1
  109. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  110. package/dist/components/tree/renderTreeCanvas.d.ts +1 -1
  111. package/dist/createPaletteMap.d.ts +7 -0
  112. package/dist/createPaletteMap.js +11 -0
  113. package/dist/createPaletteMap.js.map +1 -0
  114. package/dist/createPaletteMap.test.d.ts +1 -0
  115. package/dist/createPaletteMap.test.js +49 -0
  116. package/dist/createPaletteMap.test.js.map +1 -0
  117. package/dist/index.d.ts +3 -2
  118. package/dist/index.js +2 -2
  119. package/dist/index.js.map +1 -1
  120. package/dist/launchInterProScan.d.ts +1 -1
  121. package/dist/launchInterProScan.js +1 -1
  122. package/dist/launchInterProScan.js.map +1 -1
  123. package/dist/model/DataModel.d.ts +5 -5
  124. package/dist/model/DataModel.js +1 -1
  125. package/dist/model/DataModel.js.map +1 -1
  126. package/dist/model/DialogQueue.d.ts +2 -2
  127. package/dist/model/DialogQueue.js +1 -1
  128. package/dist/model/DialogQueue.js.map +1 -1
  129. package/dist/model/msaModel.d.ts +4 -4
  130. package/dist/model/msaModel.js +2 -2
  131. package/dist/model/msaModel.js.map +1 -1
  132. package/dist/model/treeModel.d.ts +10 -10
  133. package/dist/model/treeModel.js +2 -2
  134. package/dist/model/treeModel.js.map +1 -1
  135. package/dist/model.d.ts +524 -420
  136. package/dist/model.js +63 -40
  137. package/dist/model.js.map +1 -1
  138. package/dist/neighborJoining.test.js +1 -1
  139. package/dist/neighborJoining.test.js.map +1 -1
  140. package/dist/parseAsn1.test.js +1 -1
  141. package/dist/parseAsn1.test.js.map +1 -1
  142. package/dist/renderToSvg.d.ts +1 -1
  143. package/dist/renderToSvg.js +10 -10
  144. package/dist/renderToSvg.js.map +1 -1
  145. package/dist/reparseTree.d.ts +1 -1
  146. package/dist/rowCoordinateCalculations.js +1 -1
  147. package/dist/rowCoordinateCalculations.js.map +1 -1
  148. package/dist/rowCoordinateCalculations.test.js +1 -1
  149. package/dist/rowCoordinateCalculations.test.js.map +1 -1
  150. package/dist/seqPosToGlobalCol.js +1 -1
  151. package/dist/seqPosToGlobalCol.js.map +1 -1
  152. package/dist/seqPosToGlobalCol.test.js +1 -1
  153. package/dist/seqPosToGlobalCol.test.js.map +1 -1
  154. package/dist/util.d.ts +1 -1
  155. package/dist/vendor/copyToClipboard.d.ts +10 -0
  156. package/dist/vendor/copyToClipboard.js +113 -0
  157. package/dist/vendor/copyToClipboard.js.map +1 -0
  158. package/dist/vendor/fileSaver.d.ts +11 -0
  159. package/dist/vendor/fileSaver.js +80 -0
  160. package/dist/vendor/fileSaver.js.map +1 -0
  161. package/dist/version.d.ts +1 -1
  162. package/dist/version.js +1 -1
  163. package/dist/webpack.d.ts +2 -2
  164. package/dist/webpack.js +2 -2
  165. package/dist/webpack.js.map +1 -1
  166. package/package.json +26 -23
  167. package/src/colorSchemes.ts +1 -1
  168. package/src/components/ConservationTrack.tsx +3 -4
  169. package/src/components/Loading.tsx +3 -3
  170. package/src/components/MSAView.tsx +12 -9
  171. package/src/components/ResizeHandles.tsx +1 -1
  172. package/src/components/SequenceTextArea.tsx +2 -2
  173. package/src/components/TextTrack.tsx +3 -3
  174. package/src/components/Track.tsx +2 -2
  175. package/src/components/VerticalScrollbar.tsx +1 -1
  176. package/src/components/dialogs/AboutDialog.tsx +1 -1
  177. package/src/components/dialogs/AddTrackDialog.tsx +1 -1
  178. package/src/components/dialogs/DomainDialog.tsx +4 -4
  179. package/src/components/dialogs/ExportSVGDialog.tsx +2 -2
  180. package/src/components/dialogs/FeatureDialog.tsx +2 -2
  181. package/src/components/dialogs/InterProScanDialog.tsx +2 -2
  182. package/src/components/dialogs/MetadataDialog.tsx +2 -2
  183. package/src/components/dialogs/SettingsDialog.tsx +3 -3
  184. package/src/components/dialogs/TrackInfoDialog.tsx +2 -1
  185. package/src/components/dialogs/UserProvidedDomainsDialog.tsx +3 -3
  186. package/src/components/header/GappynessSlider.tsx +1 -1
  187. package/src/components/header/Header.tsx +11 -11
  188. package/src/components/header/HeaderInfoArea.tsx +1 -1
  189. package/src/components/header/HeaderMenu.tsx +9 -7
  190. package/src/components/header/HeaderStatusArea.tsx +1 -1
  191. package/src/components/header/MultiAlignmentSelector.tsx +1 -1
  192. package/src/components/header/SettingsMenu.tsx +2 -2
  193. package/src/components/header/ZoomControls.tsx +1 -1
  194. package/src/components/header/ZoomMenu.tsx +1 -1
  195. package/src/components/header/ZoomStar.tsx +1 -1
  196. package/src/components/import/ImportForm.tsx +3 -3
  197. package/src/components/import/ImportFormExamples.tsx +3 -3
  198. package/src/components/import/util.ts +1 -1
  199. package/src/components/minimap/Minimap.tsx +1 -1
  200. package/src/components/minimap/MinimapSVG.tsx +1 -1
  201. package/src/components/msa/MSACanvas.tsx +3 -3
  202. package/src/components/msa/MSACanvasBlock.tsx +4 -4
  203. package/src/components/msa/MSAMouseoverCanvas.tsx +3 -3
  204. package/src/components/msa/MSAPanel.tsx +3 -3
  205. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +2 -2
  206. package/src/components/msa/renderMSABlock.ts +2 -2
  207. package/src/components/msa/renderMSAMouseover.ts +1 -1
  208. package/src/components/tracks/renderTracksSvg.ts +2 -2
  209. package/src/components/tree/TreeBranchMenu.tsx +1 -1
  210. package/src/components/tree/TreeCanvas.tsx +4 -4
  211. package/src/components/tree/TreeCanvasBlock.tsx +4 -4
  212. package/src/components/tree/TreeNodeMenu.tsx +4 -2
  213. package/src/components/tree/TreePanel.tsx +2 -2
  214. package/src/components/tree/TreeRuler.tsx +1 -1
  215. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
  216. package/src/components/tree/renderTreeCanvas.ts +1 -1
  217. package/src/createPaletteMap.test.ts +57 -0
  218. package/src/createPaletteMap.ts +11 -0
  219. package/src/index.ts +3 -2
  220. package/src/launchInterProScan.ts +2 -2
  221. package/src/model/DataModel.ts +1 -1
  222. package/src/model/DialogQueue.ts +1 -1
  223. package/src/model/msaModel.ts +2 -2
  224. package/src/model/treeModel.ts +2 -2
  225. package/src/model.ts +71 -46
  226. package/src/neighborJoining.test.ts +1 -1
  227. package/src/parseAsn1.test.ts +1 -1
  228. package/src/renderToSvg.tsx +17 -19
  229. package/src/reparseTree.ts +1 -1
  230. package/src/rowCoordinateCalculations.test.ts +1 -1
  231. package/src/rowCoordinateCalculations.ts +1 -1
  232. package/src/seqPosToGlobalCol.test.ts +1 -1
  233. package/src/seqPosToGlobalCol.ts +1 -1
  234. package/src/util.ts +1 -1
  235. package/src/vendor/copyToClipboard.ts +125 -0
  236. package/src/vendor/fileSaver.ts +107 -0
  237. package/src/version.ts +1 -1
  238. package/src/webpack.ts +2 -2
  239. package/dist/__snapshots__/parseAsn1.test.js.snap +0 -2400
  240. package/src/declare.d.ts +0 -1
package/src/model.ts CHANGED
@@ -9,6 +9,11 @@ import {
9
9
  } from '@jbrowse/core/util'
10
10
  import { openLocation } from '@jbrowse/core/util/io'
11
11
  import { ElementId, FileLocation } from '@jbrowse/core/util/types/mst'
12
+ import { addDisposer, cast, types } from '@jbrowse/mobx-state-tree'
13
+ import { colord } from 'colord'
14
+ import { ascending } from 'd3-array'
15
+ import { cluster, hierarchy } from 'd3-hierarchy'
16
+ import { autorun, transaction } from 'mobx'
12
17
  import {
13
18
  A3mMSA,
14
19
  ClustalMSA,
@@ -22,17 +27,11 @@ import {
22
27
  parseNewick,
23
28
  stockholmSniff,
24
29
  } from 'msa-parsers'
25
- import { colord } from 'colord'
26
- import { ascending } from 'd3-array'
27
- import { cluster, hierarchy } from 'd3-hierarchy'
28
- import { saveAs } from 'file-saver'
29
- import { autorun, transaction } from 'mobx'
30
- import { addDisposer, cast, types } from 'mobx-state-tree'
31
30
 
32
- import { blocksX, blocksY } from './calculateBlocks'
33
- import colorSchemes from './colorSchemes'
34
- import ConservationTrack from './components/ConservationTrack'
35
- import TextTrack from './components/TextTrack'
31
+ import { blocksX, blocksY } from './calculateBlocks.ts'
32
+ import colorSchemes from './colorSchemes.ts'
33
+ import ConservationTrack from './components/ConservationTrack.tsx'
34
+ import TextTrack from './components/TextTrack.tsx'
36
35
  import {
37
36
  defaultAllowedGappyness,
38
37
  defaultBgColor,
@@ -56,37 +55,38 @@ import {
56
55
  defaultTreeAreaWidth,
57
56
  defaultTreeWidth,
58
57
  defaultTreeWidthMatchesArea,
59
- } from './constants'
60
- import { flatToTree } from './flatToTree'
61
- import palettes from './ggplotPalettes'
62
- import { measureTextCanvas } from './measureTextCanvas'
63
- import { DataModelF } from './model/DataModel'
64
- import { DialogQueueSessionMixin } from './model/DialogQueue'
65
- import { MSAModelF } from './model/msaModel'
66
- import { TreeModelF } from './model/treeModel'
67
- import { calculateNeighborJoiningTree } from './neighborJoining'
68
- import { parseAsn1 } from './parseAsn1'
69
- import { reparseTree } from './reparseTree'
58
+ } from './constants.ts'
59
+ import { createPaletteMap } from './createPaletteMap.ts'
60
+ import { flatToTree } from './flatToTree.ts'
61
+ import { measureTextCanvas } from './measureTextCanvas.ts'
62
+ import { DataModelF } from './model/DataModel.ts'
63
+ import { DialogQueueSessionMixin } from './model/DialogQueue.ts'
64
+ import { MSAModelF } from './model/msaModel.ts'
65
+ import { TreeModelF } from './model/treeModel.ts'
66
+ import { calculateNeighborJoiningTree } from './neighborJoining.ts'
67
+ import { parseAsn1 } from './parseAsn1.ts'
68
+ import { reparseTree } from './reparseTree.ts'
70
69
  import {
71
70
  globalColToVisibleCol,
72
71
  visibleColToGlobalCol,
73
72
  visibleColToSeqPosForRow,
74
- } from './rowCoordinateCalculations'
75
- import { seqPosToGlobalCol } from './seqPosToGlobalCol'
76
- import { collapse, len, maxLength, setBrLength, skipBlanks } from './util'
73
+ } from './rowCoordinateCalculations.ts'
74
+ import { seqPosToGlobalCol } from './seqPosToGlobalCol.ts'
75
+ import { collapse, len, maxLength, setBrLength, skipBlanks } from './util.ts'
76
+ import { saveAs } from './vendor/fileSaver.ts'
77
77
 
78
- import type { InterProScanResults } from './launchInterProScan'
78
+ import type { InterProScanResults } from './launchInterProScan.ts'
79
79
  import type {
80
80
  Accession,
81
81
  BasicTrack,
82
82
  NodeWithIds,
83
83
  NodeWithIdsAndLength,
84
84
  TextTrackModel,
85
- } from './types'
85
+ } from './types.ts'
86
86
  import type { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
87
+ import type { Instance } from '@jbrowse/mobx-state-tree'
87
88
  import type { Theme } from '@mui/material'
88
89
  import type { HierarchyNode } from 'd3-hierarchy'
89
- import type { Instance } from 'mobx-state-tree'
90
90
 
91
91
  const showZoomStarKey = 'msa-showZoomStar'
92
92
 
@@ -460,7 +460,7 @@ function stateModelFactory() {
460
460
  }
461
461
 
462
462
  // Get all descendant leaf names
463
- const descendantNames = node.leaves().map((leaf: any) => leaf.data.name)
463
+ const descendantNames = node.leaves().map(leaf => leaf.data.name)
464
464
 
465
465
  self.hoveredTreeNode = { nodeId, descendantNames }
466
466
  },
@@ -999,6 +999,34 @@ function stateModelFactory() {
999
999
  return this.colStats.map(val => sum(Object.values(val)))
1000
1000
  },
1001
1001
 
1002
+ /**
1003
+ * #getter
1004
+ * Detects sequence type based on letters present in the alignment.
1005
+ * Returns 'dna', 'rna', or 'amino'.
1006
+ */
1007
+ get sequenceType(): 'dna' | 'rna' | 'amino' {
1008
+ const letters = new Set<string>()
1009
+ for (const stats of this.colStats) {
1010
+ for (const letter of Object.keys(stats)) {
1011
+ const upper = letter.toUpperCase()
1012
+ if (upper !== '-' && upper !== '.') {
1013
+ letters.add(upper)
1014
+ }
1015
+ }
1016
+ }
1017
+ const dna = new Set(['A', 'C', 'G', 'T', 'N'])
1018
+ const rna = new Set(['A', 'C', 'G', 'U', 'N'])
1019
+ const isDna = [...letters].every(l => dna.has(l))
1020
+ const isRna = [...letters].every(l => rna.has(l))
1021
+ if (isDna && !letters.has('U')) {
1022
+ return 'dna'
1023
+ }
1024
+ if (isRna && !letters.has('T')) {
1025
+ return 'rna'
1026
+ }
1027
+ return 'amino'
1028
+ },
1029
+
1002
1030
  /**
1003
1031
  * #getter
1004
1032
  * Pre-computed consensus letter and percent identity color per column.
@@ -1175,8 +1203,8 @@ function stateModelFactory() {
1175
1203
  * Returns values 0-1 where 1 = fully conserved, 0 = no conservation.
1176
1204
  */
1177
1205
  get conservation() {
1178
- const { colStats, colStatsSums } = this
1179
- const alphabetSize = 20
1206
+ const { colStats, colStatsSums, sequenceType } = this
1207
+ const alphabetSize = sequenceType === 'amino' ? 20 : 4
1180
1208
  const maxEntropy = Math.log2(alphabetSize)
1181
1209
 
1182
1210
  return colStats.map((stats, i) => {
@@ -1191,13 +1219,18 @@ function stateModelFactory() {
1191
1219
  return 0
1192
1220
  }
1193
1221
 
1194
- let entropy = 0
1222
+ const merged: Record<string, number> = {}
1195
1223
  for (const letter of Object.keys(stats)) {
1196
1224
  if (letter === '-' || letter === '.') {
1197
1225
  continue
1198
1226
  }
1199
- const count = stats[letter]!
1200
- const freq = count / nonGapTotal
1227
+ const upper = letter.toUpperCase()
1228
+ merged[upper] = (merged[upper] || 0) + stats[letter]!
1229
+ }
1230
+
1231
+ let entropy = 0
1232
+ for (const key of Object.keys(merged)) {
1233
+ const freq = merged[key]! / nonGapTotal
1201
1234
  if (freq > 0) {
1202
1235
  entropy -= freq * Math.log2(freq)
1203
1236
  }
@@ -1612,9 +1645,9 @@ function stateModelFactory() {
1612
1645
  * @returns The global column index in the full MSA
1613
1646
  */
1614
1647
  seqPosToGlobalCol(rowName: string, seqPos: number) {
1615
- const { rowNames, rows } = self
1616
- const index = rowNames.indexOf(rowName)
1617
- return index !== -1 && rows[index]
1648
+ const { rows } = self
1649
+ const index = this.rowNamesSet.get(rowName)
1650
+ return index !== undefined && rows[index]
1618
1651
  ? seqPosToGlobalCol({
1619
1652
  row: rows[index][1],
1620
1653
  seqPos,
@@ -1728,15 +1761,7 @@ function stateModelFactory() {
1728
1761
  * #getter
1729
1762
  */
1730
1763
  get fillPalette() {
1731
- const arr = [...self.tidyInterProAnnotationTypes.keys()]
1732
- let i = 0
1733
- const map = {} as Record<string, string>
1734
- for (const key of arr) {
1735
- const k = Math.min(arr.length - 1, palettes.length - 1)
1736
- map[key] = palettes[k]![i]!
1737
- i++
1738
- }
1739
- return map
1764
+ return createPaletteMap([...self.tidyInterProAnnotationTypes.keys()])
1740
1765
  },
1741
1766
  /**
1742
1767
  * #getter
@@ -1792,7 +1817,7 @@ function stateModelFactory() {
1792
1817
  includeTracks?: boolean
1793
1818
  exportType: string
1794
1819
  }) {
1795
- const { renderToSvg } = await import('./renderToSvg')
1820
+ const { renderToSvg } = await import('./renderToSvg.tsx')
1796
1821
  const html = await renderToSvg(self as MsaViewModel, opts)
1797
1822
  const blob = new Blob([html], { type: 'image/svg+xml' })
1798
1823
  saveAs(blob, 'image.svg')
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from 'vitest'
2
2
 
3
- import { calculateNeighborJoiningTree } from './neighborJoining'
3
+ import { calculateNeighborJoiningTree } from './neighborJoining.ts'
4
4
 
5
5
  describe('calculateNeighborJoiningTree', () => {
6
6
  test('generates valid Newick tree for 2 sequences', () => {
@@ -2,7 +2,7 @@ import fs from 'fs'
2
2
 
3
3
  import { expect, test } from 'vitest'
4
4
 
5
- import { parseAsn1 } from './parseAsn1'
5
+ import { parseAsn1 } from './parseAsn1.ts'
6
6
 
7
7
  const r = fs.readFileSync(require.resolve('../test/data/tree.asn'), 'utf8')
8
8
 
@@ -4,15 +4,16 @@ import React from 'react'
4
4
  import { renderToStaticMarkup } from '@jbrowse/core/util'
5
5
  import { when } from 'mobx'
6
6
 
7
- import MinimapSVG from './components/minimap/MinimapSVG'
8
- import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock'
9
- import { renderMSABlock } from './components/msa/renderMSABlock'
10
- import { renderAllTracks } from './components/tracks/renderTracksSvg'
11
- import { renderTreeCanvas } from './components/tree/renderTreeCanvas'
12
- import { colorContrast } from './util'
7
+ import MinimapSVG from './components/minimap/MinimapSVG.tsx'
8
+ import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock.ts'
9
+ import { renderMSABlock } from './components/msa/renderMSABlock.ts'
10
+ import { renderAllTracks } from './components/tracks/renderTracksSvg.ts'
11
+ import { renderTreeCanvas } from './components/tree/renderTreeCanvas.ts'
12
+ import { colorContrast } from './util.ts'
13
13
 
14
- import type { MsaViewModel } from './model'
14
+ import type { MsaViewModel } from './model.ts'
15
15
  import type { Theme } from '@mui/material'
16
+ import type { Context as ContextType } from '@jbrowse/svgcanvas'
16
17
 
17
18
  export interface ExportSvgOptions {
18
19
  theme: Theme
@@ -80,7 +81,7 @@ async function render({
80
81
  includeMinimap?: boolean
81
82
  includeTracks?: boolean
82
83
  }) {
83
- const { Context } = await import('svgcanvas')
84
+ const { Context } = await import('@jbrowse/svgcanvas')
84
85
  const Wrapper = includeMinimap ? MinimapWrapper : NullWrapper
85
86
 
86
87
  return renderToStaticMarkup(
@@ -131,17 +132,16 @@ function CoreRendering({
131
132
  contentHeight: number
132
133
  offsetX: number
133
134
  offsetY: number
134
- Context: (
135
- width: number,
136
- height: number,
137
- ) => CanvasRenderingContext2D & { getSvg: () => { innerHTML: string } }
135
+ Context: typeof ContextType
138
136
  }) {
139
137
  const { treeAreaWidth, colorScheme, id } = model
140
138
  const clipId1 = `tree-${id}`
141
139
  const clipId2 = `msa-${id}`
142
140
  const contrastScheme = colorContrast(colorScheme, theme)
143
- const ctx1 = Context(width, contentHeight)
144
- const ctx2 = Context(width, contentHeight)
141
+
142
+ const ctx1 = new Context(width, contentHeight) as any
143
+
144
+ const ctx2 = new Context(width, contentHeight) as any
145
145
  renderBoxFeatureCanvasBlock({
146
146
  ctx: ctx2,
147
147
  offsetX,
@@ -209,16 +209,14 @@ function TrackRendering({
209
209
  width: number
210
210
  trackHeight: number
211
211
  offsetX: number
212
- Context: (
213
- width: number,
214
- height: number,
215
- ) => CanvasRenderingContext2D & { getSvg: () => { innerHTML: string } }
212
+ Context: typeof ContextType
216
213
  }) {
217
214
  const { treeAreaWidth, colorScheme, id } = model
218
215
  const clipId = `tracks-${id}`
219
216
  const contrastScheme = colorContrast(colorScheme, theme)
220
217
  const msaAreaWidth = width - treeAreaWidth
221
- const ctx = Context(msaAreaWidth, trackHeight)
218
+
219
+ const ctx = new Context(msaAreaWidth, trackHeight) as any
222
220
 
223
221
  renderAllTracks({
224
222
  model,
@@ -1,4 +1,4 @@
1
- import type { NodeWithIds } from './types'
1
+ import type { NodeWithIds } from './types.ts'
2
2
 
3
3
  // this reparse routine helps to allow the app to hide/collapse a single
4
4
  // leafnode
@@ -5,7 +5,7 @@ import {
5
5
  globalColToVisibleCol,
6
6
  visibleColToGlobalCol,
7
7
  visibleColToSeqPos,
8
- } from './rowCoordinateCalculations'
8
+ } from './rowCoordinateCalculations.ts'
9
9
 
10
10
  // Tests for visibleColToGlobalCol (visible → global)
11
11
  test('visibleColToGlobalCol with blanks at positions [2, 5, 8]', () => {
@@ -1,4 +1,4 @@
1
- import { isBlank } from './util'
1
+ import { isBlank } from './util.ts'
2
2
 
3
3
  /**
4
4
  * MSA Coordinate Systems:
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from 'vitest'
2
2
 
3
- import { seqPosToGlobalCol } from './seqPosToGlobalCol'
3
+ import { seqPosToGlobalCol } from './seqPosToGlobalCol.ts'
4
4
 
5
5
  describe('seqPosToGlobalCol', () => {
6
6
  test('converts sequence position to global column with no gaps', () => {
@@ -1,4 +1,4 @@
1
- import { isBlank } from './util'
1
+ import { isBlank } from './util.ts'
2
2
 
3
3
  /**
4
4
  * Convert a sequence position (ungapped, 0-based) to a global column index.
package/src/util.ts CHANGED
@@ -2,7 +2,7 @@ import { colord, extend } from 'colord'
2
2
  import namesPlugin from 'colord/plugins/names'
3
3
  import { max } from 'd3-array'
4
4
 
5
- import type { NodeWithIds } from './types'
5
+ import type { NodeWithIds } from './types.ts'
6
6
  import type { Theme } from '@mui/material'
7
7
  import type { HierarchyNode } from 'd3-hierarchy'
8
8
 
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Copy text to clipboard utility.
3
+ * Uses modern Clipboard API with fallback to execCommand.
4
+ */
5
+
6
+ function deselectCurrent() {
7
+ const selection = document.getSelection()
8
+ if (!selection?.rangeCount) {
9
+ return () => {}
10
+ }
11
+ const active = document.activeElement as HTMLElement | null
12
+
13
+ const ranges: Range[] = []
14
+ for (let i = 0; i < selection.rangeCount; i++) {
15
+ ranges.push(selection.getRangeAt(i))
16
+ }
17
+
18
+ const tagName = active?.tagName.toUpperCase()
19
+ if (tagName === 'INPUT' || tagName === 'TEXTAREA') {
20
+ active?.blur()
21
+ }
22
+
23
+ selection.removeAllRanges()
24
+ return () => {
25
+ if (selection.type === 'Caret') {
26
+ selection.removeAllRanges()
27
+ }
28
+ if (!selection.rangeCount) {
29
+ for (const range of ranges) {
30
+ selection.addRange(range)
31
+ }
32
+ }
33
+ active?.focus()
34
+ }
35
+ }
36
+
37
+ export interface CopyOptions {
38
+ debug?: boolean
39
+ format?: string
40
+ onCopy?: (clipboardData: DataTransfer) => void
41
+ }
42
+
43
+ export default function copy(text: string, options?: CopyOptions): boolean {
44
+ const debug = options?.debug || false
45
+ let success = false
46
+ let reselectPrevious: (() => void) | undefined
47
+ let range: Range | undefined
48
+ let selection: Selection | null = null
49
+ let mark: HTMLSpanElement | undefined
50
+
51
+ try {
52
+ reselectPrevious = deselectCurrent()
53
+ range = document.createRange()
54
+ selection = document.getSelection()
55
+
56
+ mark = document.createElement('span')
57
+ mark.textContent = text
58
+ mark.ariaHidden = 'true'
59
+ mark.style.all = 'unset'
60
+ mark.style.position = 'fixed'
61
+ mark.style.top = '0'
62
+ mark.style.clip = 'rect(0, 0, 0, 0)'
63
+ mark.style.whiteSpace = 'pre'
64
+ mark.style.webkitUserSelect = 'text'
65
+ ;(mark.style as unknown as Record<string, string>).MozUserSelect = 'text'
66
+ ;(mark.style as unknown as Record<string, string>).msUserSelect = 'text'
67
+ mark.style.userSelect = 'text'
68
+
69
+ mark.addEventListener('copy', e => {
70
+ e.stopPropagation()
71
+ if (options?.format) {
72
+ e.preventDefault()
73
+ if (e.clipboardData) {
74
+ e.clipboardData.clearData()
75
+ e.clipboardData.setData(options.format, text)
76
+ }
77
+ }
78
+ if (options?.onCopy && e.clipboardData) {
79
+ e.preventDefault()
80
+ options.onCopy(e.clipboardData)
81
+ }
82
+ })
83
+
84
+ document.body.append(mark)
85
+ range.selectNodeContents(mark)
86
+ selection?.addRange(range)
87
+
88
+ const successful = document.execCommand('copy')
89
+ if (!successful) {
90
+ throw new Error('copy command was unsuccessful')
91
+ }
92
+ success = true
93
+ } catch (err) {
94
+ if (debug) {
95
+ console.error('unable to copy using execCommand:', err)
96
+ }
97
+ try {
98
+ const clipboardData = (window as unknown as Record<string, DataTransfer>)
99
+ .clipboardData
100
+ if (clipboardData) {
101
+ clipboardData.setData(options?.format || 'text', text)
102
+ options?.onCopy?.(clipboardData)
103
+ success = true
104
+ }
105
+ } catch (err2) {
106
+ if (debug) {
107
+ console.error('unable to copy using clipboardData:', err2)
108
+ }
109
+ }
110
+ } finally {
111
+ if (selection) {
112
+ if (typeof selection.removeRange === 'function' && range) {
113
+ selection.removeRange(range)
114
+ } else {
115
+ selection.removeAllRanges()
116
+ }
117
+ }
118
+ if (mark) {
119
+ mark.remove()
120
+ }
121
+ reselectPrevious?.()
122
+ }
123
+
124
+ return success
125
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * FileSaver.js
3
+ * A saveAs() FileSaver implementation.
4
+ *
5
+ * By Eli Grey, http://eligrey.com
6
+ * License: MIT
7
+ * source: http://purl.eligrey.com/github/FileSaver.js
8
+ *
9
+ * Vendored and converted to ESM/TypeScript for pure ESM compatibility.
10
+ */
11
+
12
+ function click(node: HTMLAnchorElement) {
13
+ try {
14
+ node.dispatchEvent(new MouseEvent('click'))
15
+ } catch {
16
+ const evt = document.createEvent('MouseEvents')
17
+ evt.initMouseEvent(
18
+ 'click',
19
+ true,
20
+ true,
21
+ window,
22
+ 0,
23
+ 0,
24
+ 0,
25
+ 80,
26
+ 20,
27
+ false,
28
+ false,
29
+ false,
30
+ false,
31
+ 0,
32
+ null,
33
+ )
34
+ node.dispatchEvent(evt)
35
+ }
36
+ }
37
+
38
+ // Detect WebView inside a native macOS app by ruling out all browsers
39
+ const isMacOSWebView =
40
+ typeof navigator !== 'undefined' &&
41
+ navigator.userAgent.includes('Macintosh') &&
42
+ navigator.userAgent.includes('AppleWebKit') &&
43
+ !navigator.userAgent.includes('Safari')
44
+
45
+ export function saveAs(blob: Blob | string, name?: string) {
46
+ if (typeof window === 'undefined') {
47
+ return
48
+ }
49
+
50
+ const URL = window.URL
51
+ const a = document.createElement('a')
52
+ name = name || (blob instanceof Blob ? 'download' : 'download')
53
+
54
+ // Use download attribute if available and not macOS WebView
55
+ if ('download' in HTMLAnchorElement.prototype && !isMacOSWebView) {
56
+ a.download = name
57
+ a.rel = 'noopener'
58
+
59
+ if (typeof blob === 'string') {
60
+ a.href = blob
61
+ click(a)
62
+ } else {
63
+ a.href = URL.createObjectURL(blob)
64
+ setTimeout(() => {
65
+ URL.revokeObjectURL(a.href)
66
+ }, 40000)
67
+ setTimeout(() => {
68
+ click(a)
69
+ }, 0)
70
+ }
71
+ return
72
+ }
73
+
74
+ // Fallback for browsers without download attribute support
75
+ if (typeof blob === 'string') {
76
+ window.open(blob, '_blank')
77
+ return
78
+ }
79
+
80
+ const isSafari =
81
+ /constructor/i.test(
82
+ (window as unknown as { HTMLElement: unknown }).HTMLElement?.toString() ??
83
+ '',
84
+ ) || !!(window as unknown as { safari?: unknown }).safari
85
+ const isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)
86
+
87
+ if (
88
+ (isChromeIOS || isSafari || isMacOSWebView) &&
89
+ typeof FileReader !== 'undefined'
90
+ ) {
91
+ const reader = new FileReader()
92
+ reader.onloadend = () => {
93
+ let url = reader.result as string
94
+ url = isChromeIOS
95
+ ? url
96
+ : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
97
+ window.open(url, '_blank')
98
+ }
99
+ reader.readAsDataURL(blob)
100
+ } else {
101
+ const url = URL.createObjectURL(blob)
102
+ window.open(url, '_blank')
103
+ setTimeout(() => {
104
+ URL.revokeObjectURL(url)
105
+ }, 40000)
106
+ }
107
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '4.8.0'
1
+ export const version = '5.0.3'
package/src/webpack.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react'
2
2
 
3
- export { default as MSAView } from './components/Loading'
4
- export { type MsaViewModel, default as MSAModelF } from './model'
3
+ export { default as MSAView } from './components/Loading.tsx'
4
+ export { type MsaViewModel, default as MSAModelF } from './model.ts'
5
5
 
6
6
  export * from 'react-dom/client'
7
7