react-msaview 9.0.0 → 9.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 (352) hide show
  1. package/README.md +19 -10
  2. package/bundle/index.js +111 -115
  3. package/bundle/index.js.map +4 -4
  4. package/dist/blosum62.d.ts +3 -0
  5. package/dist/blosum62.js +43 -0
  6. package/dist/blosum62.js.map +1 -0
  7. package/dist/colorSchemes.js +2 -3
  8. package/dist/colorSchemes.js.map +1 -1
  9. package/dist/columnCounts.d.ts +0 -1
  10. package/dist/columnCounts.js +1 -1
  11. package/dist/columnCounts.js.map +1 -1
  12. package/dist/components/Loading.d.ts +3 -2
  13. package/dist/components/Loading.js +7 -7
  14. package/dist/components/Loading.js.map +1 -1
  15. package/dist/components/MSAView.d.ts +3 -2
  16. package/dist/components/MSAView.js +7 -4
  17. package/dist/components/MSAView.js.map +1 -1
  18. package/dist/components/MSAViewer.d.ts +1941 -4
  19. package/dist/components/MSAViewer.js +144 -201
  20. package/dist/components/MSAViewer.js.map +1 -1
  21. package/dist/components/MetadataTable.d.ts +4 -1
  22. package/dist/components/MetadataTable.js +55 -16
  23. package/dist/components/MetadataTable.js.map +1 -1
  24. package/dist/components/ResizeHandles.js +3 -2
  25. package/dist/components/ResizeHandles.js.map +1 -1
  26. package/dist/components/Track.js +8 -1
  27. package/dist/components/Track.js.map +1 -1
  28. package/dist/components/controls.d.ts +11 -0
  29. package/dist/components/controls.js +49 -0
  30. package/dist/components/controls.js.map +1 -0
  31. package/dist/components/dialogs/AnnotationFileDialog.js +12 -18
  32. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
  33. package/dist/components/dialogs/ExportSVGDialog.js +5 -7
  34. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  35. package/dist/components/dialogs/FeatureDialog.js +2 -2
  36. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  37. package/dist/components/{header/TreeSettingsMenu.d.ts → dialogs/GapThresholdDialog.d.ts} +3 -2
  38. package/dist/components/dialogs/GapThresholdDialog.js +15 -0
  39. package/dist/components/dialogs/GapThresholdDialog.js.map +1 -0
  40. package/dist/components/{header → dialogs}/GappynessSlider.d.ts +1 -1
  41. package/dist/components/{header → dialogs}/GappynessSlider.js +3 -10
  42. package/dist/components/dialogs/GappynessSlider.js.map +1 -0
  43. package/dist/components/{header → dialogs}/GoToBox.d.ts +4 -2
  44. package/dist/components/dialogs/GoToBox.js +48 -0
  45. package/dist/components/dialogs/GoToBox.js.map +1 -0
  46. package/dist/components/dialogs/GoToDialog.d.ts +6 -0
  47. package/dist/components/dialogs/GoToDialog.js +10 -0
  48. package/dist/components/dialogs/GoToDialog.js.map +1 -0
  49. package/dist/components/dialogs/HelpDialog.d.ts +6 -0
  50. package/dist/components/dialogs/HelpDialog.js +30 -0
  51. package/dist/components/dialogs/HelpDialog.js.map +1 -0
  52. package/dist/components/dialogs/MetadataDialog.js +11 -3
  53. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  54. package/dist/components/dialogs/goTo.d.ts +5 -0
  55. package/dist/components/{header → dialogs}/goTo.js +1 -1
  56. package/dist/components/dialogs/goTo.js.map +1 -0
  57. package/dist/components/dialogs/metadataLabels.d.ts +4 -0
  58. package/dist/components/dialogs/metadataLabels.js +52 -0
  59. package/dist/components/dialogs/metadataLabels.js.map +1 -0
  60. package/dist/components/dialogs/metadataRecord.d.ts +1 -0
  61. package/dist/components/dialogs/metadataRecord.js +38 -0
  62. package/dist/components/dialogs/metadataRecord.js.map +1 -0
  63. package/dist/components/header/Header.d.ts +6 -2
  64. package/dist/components/header/Header.js +30 -34
  65. package/dist/components/header/Header.js.map +1 -1
  66. package/dist/components/header/HiddenColumnsChip.d.ts +6 -0
  67. package/dist/components/header/HiddenColumnsChip.js +15 -0
  68. package/dist/components/header/HiddenColumnsChip.js.map +1 -0
  69. package/dist/components/header/{FileMenu.d.ts → MenuBar.d.ts} +3 -3
  70. package/dist/components/header/MenuBar.js +33 -0
  71. package/dist/components/header/MenuBar.js.map +1 -0
  72. package/dist/components/header/ScrollZoomCheckbox.d.ts +7 -0
  73. package/dist/components/header/ScrollZoomCheckbox.js +19 -0
  74. package/dist/components/header/ScrollZoomCheckbox.js.map +1 -0
  75. package/dist/components/header/ZoomControls.js +12 -48
  76. package/dist/components/header/ZoomControls.js.map +1 -1
  77. package/dist/components/header/colorSchemeMenuItems.d.ts +4 -0
  78. package/dist/components/header/colorSchemeMenuItems.js +106 -0
  79. package/dist/components/header/colorSchemeMenuItems.js.map +1 -0
  80. package/dist/components/header/getAnnotationsMenu.d.ts +3 -30
  81. package/dist/components/header/getAnnotationsMenu.js +48 -54
  82. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  83. package/dist/components/header/menuBarItems.d.ts +11 -0
  84. package/dist/components/header/menuBarItems.js +277 -0
  85. package/dist/components/header/menuBarItems.js.map +1 -0
  86. package/dist/components/import/ImportForm.js +12 -6
  87. package/dist/components/import/ImportForm.js.map +1 -1
  88. package/dist/components/import/data/seq2.d.ts +0 -1
  89. package/dist/components/import/data/seq2.js +0 -17
  90. package/dist/components/import/data/seq2.js.map +1 -1
  91. package/dist/components/minimap/Minimap.js +7 -3
  92. package/dist/components/minimap/Minimap.js.map +1 -1
  93. package/dist/components/minimap/MinimapSVG.js +2 -1
  94. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  95. package/dist/components/minimap/minimapLayout.d.ts +12 -0
  96. package/dist/components/minimap/minimapLayout.js +20 -0
  97. package/dist/components/minimap/minimapLayout.js.map +1 -1
  98. package/dist/components/msa/MSACanvas.js +28 -10
  99. package/dist/components/msa/MSACanvas.js.map +1 -1
  100. package/dist/components/msa/MSACanvasBlock.js +17 -16
  101. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  102. package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
  103. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  104. package/dist/components/msa/WheelZoomHint.d.ts +7 -0
  105. package/dist/components/msa/WheelZoomHint.js +38 -0
  106. package/dist/components/msa/WheelZoomHint.js.map +1 -0
  107. package/dist/components/msa/drawFeatureSpans.d.ts +50 -11
  108. package/dist/components/msa/drawFeatureSpans.js +99 -56
  109. package/dist/components/msa/drawFeatureSpans.js.map +1 -1
  110. package/dist/components/msa/msaKeys.js +3 -0
  111. package/dist/components/msa/msaKeys.js.map +1 -1
  112. package/dist/components/msa/msaRaster.d.ts +14 -2
  113. package/dist/components/msa/msaRaster.js +20 -10
  114. package/dist/components/msa/msaRaster.js.map +1 -1
  115. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +9 -0
  116. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +39 -8
  117. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  118. package/dist/components/msa/renderHighlights.d.ts +2 -5
  119. package/dist/components/msa/renderHighlights.js +5 -6
  120. package/dist/components/msa/renderHighlights.js.map +1 -1
  121. package/dist/components/msa/renderMSABlock.d.ts +6 -1
  122. package/dist/components/msa/renderMSABlock.js +44 -17
  123. package/dist/components/msa/renderMSABlock.js.map +1 -1
  124. package/dist/components/msa/renderMSAMouseover.js +2 -1
  125. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  126. package/dist/components/msa/tileColor.js +7 -0
  127. package/dist/components/msa/tileColor.js.map +1 -1
  128. package/dist/components/renderCtx.d.ts +1 -1
  129. package/dist/components/rowpanels/RowPanelBlock.js +2 -2
  130. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
  131. package/dist/components/rowpanels/renderFeaturePanel.d.ts +5 -1
  132. package/dist/components/rowpanels/renderFeaturePanel.js +12 -5
  133. package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -1
  134. package/dist/components/settings/MenuItemsForm.d.ts +10 -0
  135. package/dist/components/settings/MenuItemsForm.js +80 -0
  136. package/dist/components/settings/MenuItemsForm.js.map +1 -0
  137. package/dist/components/settings/SettingsPanel.d.ts +12 -0
  138. package/dist/components/settings/SettingsPanel.js +92 -0
  139. package/dist/components/settings/SettingsPanel.js.map +1 -0
  140. package/dist/components/{header/ZoomMenu.d.ts → settings/TrackList.d.ts} +2 -2
  141. package/dist/components/settings/TrackList.js +55 -0
  142. package/dist/components/settings/TrackList.js.map +1 -0
  143. package/dist/components/tracks/TrackBlocks.js +2 -2
  144. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  145. package/dist/components/tracks/drawTracks.d.ts +1 -53
  146. package/dist/components/tracks/drawTracks.js +5 -5
  147. package/dist/components/tracks/drawTracks.js.map +1 -1
  148. package/dist/components/tree/CladeLabels.d.ts +1 -1
  149. package/dist/components/tree/CladeLabels.js +22 -34
  150. package/dist/components/tree/CladeLabels.js.map +1 -1
  151. package/dist/components/tree/TreeCanvasBlock.js +2 -2
  152. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  153. package/dist/components/tree/TreeMenu.js +4 -6
  154. package/dist/components/tree/TreeMenu.js.map +1 -1
  155. package/dist/components/tree/TreeOverview.js +2 -2
  156. package/dist/components/tree/TreeOverview.js.map +1 -1
  157. package/dist/components/tree/cladeBrackets.d.ts +17 -10
  158. package/dist/components/tree/cladeBrackets.js +33 -3
  159. package/dist/components/tree/cladeBrackets.js.map +1 -1
  160. package/dist/components/tree/renderTreeCanvas.js +2 -8
  161. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  162. package/dist/components/tree/renderTreeOverview.js +4 -11
  163. package/dist/components/tree/renderTreeOverview.js.map +1 -1
  164. package/dist/constants.d.ts +4 -1
  165. package/dist/constants.js +7 -1
  166. package/dist/constants.js.map +1 -1
  167. package/dist/darkReader.d.ts +7 -0
  168. package/dist/darkReader.js +42 -0
  169. package/dist/darkReader.js.map +1 -0
  170. package/dist/element.js +25 -53
  171. package/dist/element.js.map +1 -1
  172. package/dist/exportLayout.d.ts +39 -0
  173. package/dist/exportLayout.js +78 -0
  174. package/dist/exportLayout.js.map +1 -0
  175. package/dist/featurePanels.d.ts +37 -0
  176. package/dist/featurePanels.js +187 -0
  177. package/dist/featurePanels.js.map +1 -0
  178. package/dist/filehandleLoader.d.ts +41 -0
  179. package/dist/filehandleLoader.js +99 -0
  180. package/dist/filehandleLoader.js.map +1 -0
  181. package/dist/headlessRenderEnv.d.ts +12 -39
  182. package/dist/headlessRenderEnv.js +18 -12
  183. package/dist/headlessRenderEnv.js.map +1 -1
  184. package/dist/hierarchy.d.ts +5 -8
  185. package/dist/hierarchy.js +5 -79
  186. package/dist/hierarchy.js.map +1 -1
  187. package/dist/index.d.ts +6 -1
  188. package/dist/index.js +6 -1
  189. package/dist/index.js.map +1 -1
  190. package/dist/measureTextCanvas.d.ts +7 -0
  191. package/dist/measureTextCanvas.js +8 -6
  192. package/dist/measureTextCanvas.js.map +1 -1
  193. package/dist/model/treeModel.d.ts +6 -2
  194. package/dist/model/treeModel.js +8 -4
  195. package/dist/model/treeModel.js.map +1 -1
  196. package/dist/model.d.ts +180 -76
  197. package/dist/model.js +358 -618
  198. package/dist/model.js.map +1 -1
  199. package/dist/neighborJoining.js +1 -40
  200. package/dist/neighborJoining.js.map +1 -1
  201. package/dist/renderToSvg.d.ts +2 -7
  202. package/dist/renderToSvg.js +33 -133
  203. package/dist/renderToSvg.js.map +1 -1
  204. package/dist/rerootTree.d.ts +1 -0
  205. package/dist/rerootTree.js +7 -6
  206. package/dist/rerootTree.js.map +1 -1
  207. package/dist/schemeResidueColor.d.ts +12 -0
  208. package/dist/schemeResidueColor.js +33 -0
  209. package/dist/schemeResidueColor.js.map +1 -0
  210. package/dist/similarityColors.d.ts +11 -0
  211. package/dist/similarityColors.js +90 -0
  212. package/dist/similarityColors.js.map +1 -0
  213. package/dist/svgTestUtil.d.ts +74 -32
  214. package/dist/treeDisplay.d.ts +37 -0
  215. package/dist/treeDisplay.js +209 -0
  216. package/dist/treeDisplay.js.map +1 -0
  217. package/dist/types.d.ts +6 -3
  218. package/dist/useWheelScroll.d.ts +3 -1
  219. package/dist/useWheelScroll.js +36 -17
  220. package/dist/useWheelScroll.js.map +1 -1
  221. package/dist/util.d.ts +2 -0
  222. package/dist/util.js +2 -0
  223. package/dist/util.js.map +1 -1
  224. package/dist/version.d.ts +1 -1
  225. package/dist/version.js +1 -1
  226. package/dist/viewerSnapshot.d.ts +169 -0
  227. package/dist/viewerSnapshot.js +100 -0
  228. package/dist/viewerSnapshot.js.map +1 -0
  229. package/dist/wheelZoom.d.ts +8 -3
  230. package/dist/wheelZoom.js +44 -32
  231. package/dist/wheelZoom.js.map +1 -1
  232. package/dist/zoomSnapshot.d.ts +29 -0
  233. package/dist/zoomSnapshot.js +47 -0
  234. package/dist/zoomSnapshot.js.map +1 -0
  235. package/package.json +11 -11
  236. package/src/blosum62.ts +45 -0
  237. package/src/colorSchemes.ts +2 -4
  238. package/src/columnCounts.ts +1 -1
  239. package/src/components/Loading.tsx +25 -14
  240. package/src/components/MSAView.tsx +22 -5
  241. package/src/components/MSAViewer.tsx +275 -251
  242. package/src/components/MetadataTable.tsx +83 -26
  243. package/src/components/ResizeHandles.tsx +9 -2
  244. package/src/components/Track.tsx +15 -1
  245. package/src/components/controls.ts +62 -0
  246. package/src/components/dialogs/AnnotationFileDialog.tsx +16 -21
  247. package/src/components/dialogs/ExportSVGDialog.tsx +8 -19
  248. package/src/components/dialogs/FeatureDialog.tsx +2 -5
  249. package/src/components/dialogs/GapThresholdDialog.tsx +36 -0
  250. package/src/components/{header → dialogs}/GappynessSlider.tsx +3 -10
  251. package/src/components/{header → dialogs}/GoToBox.tsx +36 -10
  252. package/src/components/dialogs/GoToDialog.tsx +30 -0
  253. package/src/components/dialogs/HelpDialog.tsx +79 -0
  254. package/src/components/dialogs/MetadataDialog.tsx +32 -4
  255. package/src/components/{header → dialogs}/goTo.ts +1 -1
  256. package/src/components/dialogs/metadataLabels.ts +54 -0
  257. package/src/components/dialogs/metadataRecord.tsx +61 -0
  258. package/src/components/header/Header.tsx +75 -44
  259. package/src/components/header/HiddenColumnsChip.tsx +39 -0
  260. package/src/components/header/MenuBar.tsx +64 -0
  261. package/src/components/header/ScrollZoomCheckbox.tsx +46 -0
  262. package/src/components/header/ZoomControls.tsx +23 -74
  263. package/src/components/header/colorSchemeMenuItems.ts +121 -0
  264. package/src/components/header/getAnnotationsMenu.ts +52 -56
  265. package/src/components/header/menuBarItems.ts +357 -0
  266. package/src/components/import/ImportForm.tsx +19 -7
  267. package/src/components/import/data/seq2.ts +0 -18
  268. package/src/components/minimap/Minimap.tsx +11 -3
  269. package/src/components/minimap/MinimapSVG.tsx +17 -1
  270. package/src/components/minimap/minimapLayout.ts +27 -0
  271. package/src/components/msa/MSACanvas.tsx +24 -4
  272. package/src/components/msa/MSACanvasBlock.tsx +20 -17
  273. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -2
  274. package/src/components/msa/WheelZoomHint.tsx +56 -0
  275. package/src/components/msa/drawFeatureSpans.ts +160 -79
  276. package/src/components/msa/msaKeys.ts +3 -0
  277. package/src/components/msa/msaRaster.ts +26 -10
  278. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +62 -24
  279. package/src/components/msa/renderHighlights.ts +5 -6
  280. package/src/components/msa/renderMSABlock.ts +51 -15
  281. package/src/components/msa/renderMSAMouseover.ts +5 -1
  282. package/src/components/msa/tileColor.ts +7 -0
  283. package/src/components/renderCtx.ts +0 -1
  284. package/src/components/rowpanels/RowPanelBlock.tsx +2 -2
  285. package/src/components/rowpanels/renderFeaturePanel.ts +26 -7
  286. package/src/components/settings/MenuItemsForm.tsx +225 -0
  287. package/src/components/settings/SettingsPanel.tsx +156 -0
  288. package/src/components/settings/TrackList.tsx +119 -0
  289. package/src/components/tracks/TrackBlocks.tsx +2 -2
  290. package/src/components/tracks/drawTracks.ts +5 -5
  291. package/src/components/tree/CladeLabels.tsx +30 -54
  292. package/src/components/tree/TreeCanvasBlock.tsx +2 -2
  293. package/src/components/tree/TreeMenu.tsx +4 -6
  294. package/src/components/tree/TreeOverview.tsx +2 -2
  295. package/src/components/tree/cladeBrackets.ts +50 -3
  296. package/src/components/tree/renderTreeCanvas.ts +2 -13
  297. package/src/components/tree/renderTreeOverview.ts +7 -14
  298. package/src/constants.ts +9 -1
  299. package/src/darkReader.ts +57 -0
  300. package/src/element.ts +37 -64
  301. package/src/exportLayout.ts +134 -0
  302. package/src/featurePanels.ts +269 -0
  303. package/src/filehandleLoader.ts +136 -0
  304. package/src/headlessRenderEnv.ts +24 -11
  305. package/src/hierarchy.ts +5 -88
  306. package/src/index.ts +9 -5
  307. package/src/measureTextCanvas.ts +8 -6
  308. package/src/model/treeModel.ts +9 -3
  309. package/src/model.ts +452 -807
  310. package/src/neighborJoining.ts +1 -42
  311. package/src/renderToSvg.tsx +56 -207
  312. package/src/rerootTree.ts +7 -6
  313. package/src/schemeResidueColor.ts +37 -0
  314. package/src/similarityColors.ts +112 -0
  315. package/src/treeDisplay.ts +278 -0
  316. package/src/types.ts +6 -4
  317. package/src/useWheelScroll.ts +43 -22
  318. package/src/util.ts +3 -0
  319. package/src/version.ts +1 -1
  320. package/src/viewerSnapshot.ts +147 -0
  321. package/src/wheelZoom.ts +55 -33
  322. package/src/zoomSnapshot.ts +80 -0
  323. package/dist/components/header/ColorSchemeMenu.d.ts +0 -12
  324. package/dist/components/header/ColorSchemeMenu.js +0 -37
  325. package/dist/components/header/ColorSchemeMenu.js.map +0 -1
  326. package/dist/components/header/FileMenu.js +0 -66
  327. package/dist/components/header/FileMenu.js.map +0 -1
  328. package/dist/components/header/GappynessSlider.js.map +0 -1
  329. package/dist/components/header/GoToBox.js +0 -31
  330. package/dist/components/header/GoToBox.js.map +0 -1
  331. package/dist/components/header/MSASettingsMenu.d.ts +0 -6
  332. package/dist/components/header/MSASettingsMenu.js +0 -11
  333. package/dist/components/header/MSASettingsMenu.js.map +0 -1
  334. package/dist/components/header/ScrollZoomIcons.d.ts +0 -6
  335. package/dist/components/header/ScrollZoomIcons.js +0 -35
  336. package/dist/components/header/ScrollZoomIcons.js.map +0 -1
  337. package/dist/components/header/TreeSettingsMenu.js +0 -42
  338. package/dist/components/header/TreeSettingsMenu.js.map +0 -1
  339. package/dist/components/header/ZoomMenu.js +0 -37
  340. package/dist/components/header/ZoomMenu.js.map +0 -1
  341. package/dist/components/header/goTo.d.ts +0 -20
  342. package/dist/components/header/goTo.js.map +0 -1
  343. package/dist/components/header/settingsMenuItems.d.ts +0 -4
  344. package/dist/components/header/settingsMenuItems.js +0 -127
  345. package/dist/components/header/settingsMenuItems.js.map +0 -1
  346. package/src/components/header/ColorSchemeMenu.tsx +0 -56
  347. package/src/components/header/FileMenu.tsx +0 -81
  348. package/src/components/header/MSASettingsMenu.tsx +0 -26
  349. package/src/components/header/ScrollZoomIcons.tsx +0 -52
  350. package/src/components/header/TreeSettingsMenu.tsx +0 -60
  351. package/src/components/header/ZoomMenu.tsx +0 -48
  352. package/src/components/header/settingsMenuItems.ts +0 -157
@@ -30,7 +30,7 @@ const TreeMenu = observer(function ({
30
30
  const { name, id, leaf } = node
31
31
  const isCollapsed = collapsed.includes(id)
32
32
  const isRoot = id === model.tree.id
33
- const noun = leaf ? 'subtree' : 'this node'
33
+ const noun = leaf ? 'subtree' : 'clade'
34
34
  return (
35
35
  <Menu
36
36
  anchorReference="anchorPosition"
@@ -73,9 +73,7 @@ const TreeMenu = observer(function ({
73
73
  onClose()
74
74
  }}
75
75
  >
76
- {showOnly === id
77
- ? 'Disable show only this node'
78
- : 'Show only this node'}
76
+ {showOnly === id ? 'Show all rows' : 'Show only this clade'}
79
77
  </MenuItem>
80
78
  )}
81
79
  {leaf || isCollapsed ? null : (
@@ -86,7 +84,7 @@ const TreeMenu = observer(function ({
86
84
  onClose()
87
85
  }}
88
86
  >
89
- Rotate this node
87
+ Rotate clade
90
88
  </MenuItem>
91
89
  )}
92
90
  {isRoot ? null : (
@@ -108,7 +106,7 @@ const TreeMenu = observer(function ({
108
106
  onClose()
109
107
  }}
110
108
  >
111
- Indicate differences from this row
109
+ Use as reference row
112
110
  </MenuItem>
113
111
  ) : null}
114
112
  {leaf && relativeTo ? (
@@ -1,8 +1,8 @@
1
1
  import React, { useState } from 'react'
2
2
 
3
- import { useTheme } from '@mui/material'
4
3
  import { observer } from 'mobx-react'
5
4
 
5
+ import { useCanvasTheme } from '../../darkReader.ts'
6
6
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
7
7
  import {
8
8
  drawTreeOverview,
@@ -22,7 +22,7 @@ import type { MsaViewModel } from '../../model.ts'
22
22
  */
23
23
  const TreeOverview = observer(function ({ model }: { model: MsaViewModel }) {
24
24
  const { effectiveTreeAreaWidth, overviewHeight, highResScaleFactor } = model
25
- const theme = useTheme()
25
+ const theme = useCanvasTheme()
26
26
  const [hover, setHover] = useState<{ id: string; rows: [number, number] }>()
27
27
  const width = Math.round(effectiveTreeAreaWidth * highResScaleFactor)
28
28
  const height = Math.round(overviewHeight * highResScaleFactor)
@@ -1,6 +1,7 @@
1
1
  import { labelFitFontSize } from '../../constants.ts'
2
2
  import { measureTextCanvas } from '../../measureTextCanvas.ts'
3
3
 
4
+ import type { MsaViewModel } from '../../model.ts'
4
5
  import type { Clade, ResolvedClade } from '../../types.ts'
5
6
 
6
7
  // The bar of the bracket mark and the space around it, in px.
@@ -19,7 +20,7 @@ export function cladeInGutter(clade: Pick<Clade, 'mark' | 'label'>) {
19
20
  }
20
21
 
21
22
  /** the pixel height of the rows a clade covers */
22
- export function cladeHeight(clade: ResolvedClade, rowHeight: number) {
23
+ function cladeHeight(clade: ResolvedClade, rowHeight: number) {
23
24
  return (clade.rows[1] - clade.rows[0] + 1) * rowHeight
24
25
  }
25
26
 
@@ -27,7 +28,7 @@ export function cladeHeight(clade: ResolvedClade, rowHeight: number) {
27
28
  * Whether a label reads across the rows. It does where they are taller than the
28
29
  * font, and runs up the bar where they are not.
29
30
  */
30
- export function cladeLabelHorizontal(
31
+ function cladeLabelHorizontal(
31
32
  clade: ResolvedClade,
32
33
  rowHeight: number,
33
34
  fontSize: number,
@@ -36,7 +37,7 @@ export function cladeLabelHorizontal(
36
37
  }
37
38
 
38
39
  /** the px between the left edge of the gutter and the label's own column */
39
- export function cladeLabelOffset(clade: Pick<Clade, 'mark'>) {
40
+ function cladeLabelOffset(clade: Pick<Clade, 'mark'>) {
40
41
  return clade.mark === 'bracket'
41
42
  ? bracketGap + bracketBarWidth + bracketGap
42
43
  : bracketGap
@@ -64,3 +65,49 @@ export function cladeGutterWidth(clades: Pick<Clade, 'mark' | 'label'>[]) {
64
65
  // the measurement here sized the gutter for
65
66
  return Math.min(maxCladeGutterWidth, Math.ceil(widest))
66
67
  }
68
+
69
+ /**
70
+ * The box each gutter label takes, in unscrolled tree-area px: from the
71
+ * label's column to the right edge of the gutter, across the clade's rows. The
72
+ * live view sets DOM text in it, and the SVG export a `<text>`.
73
+ */
74
+ export function cladeLabelBoxes(
75
+ model: Pick<
76
+ MsaViewModel,
77
+ | 'resolvedClades'
78
+ | 'cladeGutterWidth'
79
+ | 'rowHeight'
80
+ | 'effectiveTreeAreaWidth'
81
+ | 'fontSize'
82
+ >,
83
+ ) {
84
+ const {
85
+ resolvedClades,
86
+ cladeGutterWidth,
87
+ rowHeight,
88
+ effectiveTreeAreaWidth,
89
+ fontSize,
90
+ } = model
91
+ const gutterLeft = effectiveTreeAreaWidth - cladeGutterWidth
92
+ return cladeGutterWidth === 0
93
+ ? []
94
+ : resolvedClades.flatMap((clade, index) => {
95
+ const { label, markColor, rows } = clade
96
+ if (!label || !cladeInGutter(clade)) {
97
+ return []
98
+ }
99
+ const left = gutterLeft + cladeLabelOffset(clade)
100
+ return [
101
+ {
102
+ key: `${rows[0]}-${rows[1]}-${index}`,
103
+ label,
104
+ color: markColor,
105
+ left,
106
+ top: rows[0] * rowHeight,
107
+ width: effectiveTreeAreaWidth - left - bracketGap,
108
+ height: cladeHeight(clade, rowHeight),
109
+ horizontal: cladeLabelHorizontal(clade, rowHeight, fontSize),
110
+ },
111
+ ]
112
+ })
113
+ }
@@ -36,7 +36,7 @@ function inYBlock(y: number, offsetY: number, by: number, pad: number) {
36
36
  /**
37
37
  * Every node whose subtree reaches into the block, skipping the subtrees that
38
38
  * do not. A subtree's leaves are one contiguous run of rows, recorded by
39
- * clusterLayout as xMin/xMax, so a block of a 200k-tip tree visits a few dozen
39
+ * layoutTree as xMin/xMax, so a block of a 200k-tip tree visits a few dozen
40
40
  * nodes.
41
41
  *
42
42
  * A link out of a visited node is drawn even when its child is pruned, so a
@@ -706,18 +706,7 @@ export function renderTreeCanvas({
706
706
 
707
707
  // memoized on the model and shared across the tree/bubble/label passes (and
708
708
  // across all tree blocks) rather than re-traversing the hierarchy in each
709
- const {
710
- maxBranchLength,
711
- maxDepthToLeaf,
712
- showBranchLenEffective,
713
- effectiveTreeWidth,
714
- } = model
715
- // pixel x of the tips. A phylogram scales branch lengths so the longest
716
- // root-to-tip path lands at maxBranchLength; a cladogram ignores lengths and
717
- // spreads topological depth across the whole tree area instead. Reusing
718
- // maxBranchLength for the cladogram would pin every node to x=0 for a tree
719
- // with no branch lengths -- which is exactly what forces cladogram mode.
720
- const tipX = showBranchLenEffective ? maxBranchLength : effectiveTreeWidth
709
+ const { maxDepthToLeaf, effectiveTreeWidth: tipX } = model
721
710
  // built once for all three passes: each of them asks about a different node on
722
711
  // every block of every redraw, and `collapsed` is a list
723
712
  const collapsedSet = new Set(model.collapsed)
@@ -1,4 +1,5 @@
1
1
  import { descendants } from '../../hierarchy.ts'
2
+ import { makeOffscreenCanvas } from '../msa/msaRaster.ts'
2
3
  import { getNodeX } from './renderTreeCanvas.ts'
3
4
 
4
5
  import type { HierarchyNode } from '../../hierarchy.ts'
@@ -62,10 +63,10 @@ export function drawTreeOverview({
62
63
  continue
63
64
  }
64
65
  const sx = nodeX(source)
65
- const sy = rowY(source.x!)
66
+ const sy = rowY(source.x)
66
67
  for (const target of source.children) {
67
68
  const tx = nodeX(target)
68
- const ty = rowY(target.x!)
69
+ const ty = rowY(target.x)
69
70
  if (Math.abs(tx - sx) < 1 && Math.abs(ty - sy) < 1) {
70
71
  continue
71
72
  }
@@ -158,17 +159,8 @@ function overviewKeys({
158
159
  ]
159
160
  }
160
161
 
161
- function makeCanvas(width: number, height: number) {
162
- const canvas =
163
- typeof OffscreenCanvas !== 'undefined'
164
- ? new OffscreenCanvas(width, height)
165
- : typeof document !== 'undefined'
166
- ? Object.assign(document.createElement('canvas'), { width, height })
167
- : undefined
168
- const ctx = canvas?.getContext('2d')
169
- return ctx && typeof (ctx as CanvasRenderingContext2D).stroke === 'function'
170
- ? { canvas: canvas as RasterCanvas, ctx: ctx as CanvasRenderingContext2D }
171
- : undefined
162
+ function canStroke(ctx: unknown): ctx is CanvasRenderingContext2D {
163
+ return typeof (ctx as CanvasRenderingContext2D | null)?.stroke === 'function'
172
164
  }
173
165
 
174
166
  /**
@@ -198,9 +190,10 @@ export function treeOverviewImage({
198
190
  if (prev && keys.every((key, i) => key === prev.keys[i])) {
199
191
  return prev.canvas
200
192
  }
201
- const made = makeCanvas(
193
+ const made = makeOffscreenCanvas(
202
194
  Math.max(1, Math.round(width * scale)),
203
195
  Math.max(1, Math.round(height * scale)),
196
+ canStroke,
204
197
  )
205
198
  if (made) {
206
199
  made.ctx.scale(scale, scale)
package/src/constants.ts CHANGED
@@ -50,6 +50,10 @@ export const defaultScrollZoomAxis: ScrollZoomAxis = 'both'
50
50
  export const minLetterRowHeight = 8
51
51
  export const minLetterColWidth = 5
52
52
 
53
+ // A wheel-zoom frame draws a block's letters only when the block holds at most
54
+ // this many cells; the settle redraw after the gesture draws the rest
55
+ export const zoomFrameMaxLetters = 2000
56
+
53
57
  // A feature span draws its label at two pixels under its height, capped at 11,
54
58
  // so a span shorter than this holds no readable text: an underline bar, a
55
59
  // sub-row band or a features panel over small rows
@@ -68,9 +72,12 @@ export const labelFitFontSize = fontSizeFor(defaultRowHeight)
68
72
  // zoom-in buttons (which otherwise grow unbounded). minColWidth/minRowHeight are
69
73
  // the smooth-zoom floors.
70
74
  export const minColWidth = 0.2
71
- export const minRowHeight = 1
75
+ export const minRowHeight = 0.2
72
76
  export const maxCellSize = 80
73
77
 
78
+ // the settings panel's width, which it takes out of the view's
79
+ export const settingsPanelWidth = 280
80
+
74
81
  // MSA model defaults
75
82
  export const defaultBgColor = true
76
83
  export const defaultColorSchemeName = 'maeditor'
@@ -99,6 +106,7 @@ export const defaultDrawTree = true
99
106
  export const defaultDrawNodeBubbles = true
100
107
  export const defaultDrawNodeLabels = false
101
108
  export const defaultShowTreeOverview = false
109
+ export const defaultShowTreeScaleBar = false
102
110
  export const defaultOverviewHeight = 120
103
111
 
104
112
  // Neighbor joining does not run above this many rows. The distance matrix is
@@ -0,0 +1,57 @@
1
+ import { useSyncExternalStore } from 'react'
2
+
3
+ import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
4
+ import { useTheme } from '@mui/material'
5
+
6
+ import type { Theme } from '@mui/material'
7
+
8
+ // Dark Reader's Dynamic mode rewrites the page's CSS and leaves canvas pixels
9
+ // alone, so a light theme's black letters land on the dark page it paints.
10
+ // Inlined from @jbrowse/core/util/systemColorScheme, which an older host core
11
+ // lacks (see CLAUDE.md on externals).
12
+ const DARK_READER_ATTRIBUTE = 'data-darkreader-scheme'
13
+
14
+ function darkReaderIsDark() {
15
+ return (
16
+ typeof document !== 'undefined' &&
17
+ document.documentElement.getAttribute(DARK_READER_ATTRIBUTE) === 'dark'
18
+ )
19
+ }
20
+
21
+ function onDarkReaderChange(listener: () => void) {
22
+ if (
23
+ typeof MutationObserver !== 'function' ||
24
+ typeof document === 'undefined'
25
+ ) {
26
+ return () => {}
27
+ }
28
+ const observer = new MutationObserver(listener)
29
+ observer.observe(document.documentElement, {
30
+ attributes: true,
31
+ attributeFilter: [DARK_READER_ATTRIBUTE],
32
+ })
33
+ return () => {
34
+ observer.disconnect()
35
+ }
36
+ }
37
+
38
+ let darkTheme: Theme | undefined
39
+
40
+ /**
41
+ * The theme a canvas draws in: the surrounding MUI theme, or a dark one while
42
+ * Dark Reader darkens a light page. The DOM keeps the surrounding theme, which
43
+ * Dark Reader restyles itself, and the SVG export keeps it too.
44
+ */
45
+ export function useCanvasTheme() {
46
+ const theme = useTheme()
47
+ const darkReader = useSyncExternalStore(
48
+ onDarkReaderChange,
49
+ darkReaderIsDark,
50
+ () => false,
51
+ )
52
+ if (darkReader && theme.palette.mode !== 'dark') {
53
+ darkTheme ??= createJBrowseTheme({ palette: { mode: 'dark' } })
54
+ return darkTheme
55
+ }
56
+ return theme
57
+ }
package/src/element.ts CHANGED
@@ -5,16 +5,7 @@ import { mount } from './mount.tsx'
5
5
  import type { MSAViewerProps } from './components/MSAViewer.tsx'
6
6
  import type { MsaViewModel } from './model.ts'
7
7
  import type { MountedViewer } from './mount.tsx'
8
- import type {
9
- Cell,
10
- ColorScheme,
11
- ColumnTrackSpec,
12
- Feature,
13
- Highlight,
14
- MsaSelection,
15
- ResidueMapping,
16
- Viewport,
17
- } from './types.ts'
8
+ import type { Cell, Highlight, MsaSelection, Viewport } from './types.ts'
18
9
 
19
10
  type ElementData = Pick<
20
11
  MSAViewerProps,
@@ -29,6 +20,17 @@ type ElementData = Pick<
29
20
  | 'selection'
30
21
  >
31
22
 
23
+ const dataProperties = [
24
+ 'msa',
25
+ 'tree',
26
+ 'gff',
27
+ 'features',
28
+ 'colorScheme',
29
+ 'highlights',
30
+ 'columnTracks',
31
+ 'residueMappings',
32
+ ] as const satisfies (keyof ElementData)[]
33
+
32
34
  interface NightingaleManager extends HTMLElement {
33
35
  register: (element: HTMLElement) => void
34
36
  unregister: (element: HTMLElement) => void
@@ -49,10 +51,6 @@ const ownAttributes = [
49
51
  // the attributes a <nightingale-manager> writes on the elements it holds
50
52
  const nightingaleAttributes = ['display-start', 'display-end', 'highlight']
51
53
 
52
- function location(uri: string | null) {
53
- return uri ? { uri, locationType: 'UriLocation' as const } : undefined
54
- }
55
-
56
54
  function numberAttribute(value: string | null | undefined) {
57
55
  const n = value ? Number(value) : Number.NaN
58
56
  return Number.isFinite(n) ? n : undefined
@@ -119,54 +117,29 @@ export function defineMsaElement(tagName = 'jbrowse-msa') {
119
117
  #reported?: { start: number; end: number }
120
118
  #data: ElementData = {}
121
119
 
122
- get msa() {
123
- return this.#data.msa
124
- }
125
- set msa(value: string | undefined) {
126
- this.#setData({ msa: value })
127
- }
128
- get tree() {
129
- return this.#data.tree
130
- }
131
- set tree(value: string | undefined) {
132
- this.#setData({ tree: value })
133
- }
134
- get gff() {
135
- return this.#data.gff
136
- }
137
- set gff(value: string | undefined) {
138
- this.#setData({ gff: value })
139
- }
140
- get features() {
141
- return this.#data.features
142
- }
143
- set features(value: Feature[] | undefined) {
144
- this.#setData({ features: value })
145
- }
146
- get colorScheme() {
147
- return this.#data.colorScheme
148
- }
149
- set colorScheme(value: ColorScheme | undefined) {
150
- this.#setData({ colorScheme: value })
151
- }
152
- get highlights() {
153
- return this.#data.highlights
154
- }
155
- set highlights(value: Highlight[] | undefined) {
156
- this.#setData({ highlights: value })
157
- }
158
- get columnTracks() {
159
- return this.#data.columnTracks
160
- }
161
- set columnTracks(value: ColumnTrackSpec[] | undefined) {
162
- this.#setData({ columnTracks: value })
163
- }
164
- get residueMappings() {
165
- return this.#data.residueMappings
166
- }
167
- set residueMappings(value: ResidueMapping[] | undefined) {
168
- this.#setData({ residueMappings: value })
120
+ declare msa: ElementData['msa']
121
+ declare tree: ElementData['tree']
122
+ declare gff: ElementData['gff']
123
+ declare features: ElementData['features']
124
+ declare colorScheme: ElementData['colorScheme']
125
+ declare highlights: ElementData['highlights']
126
+ declare columnTracks: ElementData['columnTracks']
127
+ declare residueMappings: ElementData['residueMappings']
128
+
129
+ static {
130
+ for (const key of dataProperties) {
131
+ Object.defineProperty(MsaElement.prototype, key, {
132
+ configurable: true,
133
+ get(this: MsaElement) {
134
+ return this.#data[key]
135
+ },
136
+ set(this: MsaElement, value: ElementData[typeof key]) {
137
+ this.#setData({ [key]: value })
138
+ },
139
+ })
140
+ }
169
141
  }
142
+
170
143
  // the viewer's selection, which the reader's drags change
171
144
  get selection() {
172
145
  return this.#model ? this.#model.selection : this.#data.selection
@@ -188,9 +161,9 @@ export function defineMsaElement(tagName = 'jbrowse-msa') {
188
161
  const theme = this.getAttribute('theme')
189
162
  return {
190
163
  ...this.#data,
191
- msaFilehandle: location(this.getAttribute('msa-url')),
192
- treeFilehandle: location(this.getAttribute('tree-url')),
193
- gffFilehandle: location(this.getAttribute('gff-url')),
164
+ msaUrl: this.getAttribute('msa-url') ?? undefined,
165
+ treeUrl: this.getAttribute('tree-url') ?? undefined,
166
+ gffUrl: this.getAttribute('gff-url') ?? undefined,
194
167
  colorScheme:
195
168
  this.#data.colorScheme ??
196
169
  this.getAttribute('color-scheme') ??
@@ -0,0 +1,134 @@
1
+ import { legendRows } from './components/msa/legendRows.ts'
2
+ import { treeScaleBarHeight } from './constants.ts'
3
+
4
+ import type { LegendRow } from './components/msa/legendRows.ts'
5
+ import type { MsaViewModel } from './model.ts'
6
+ import type { Theme } from '@mui/material'
7
+
8
+ export interface ExportSvgOptions {
9
+ theme: Theme
10
+ includeMinimap?: boolean
11
+ includeTracks?: boolean
12
+ exportType: 'entire' | 'viewport'
13
+ }
14
+
15
+ // legend geometry, shared by the width calculation and the renderer so the
16
+ // reserved column on the right exactly fits the drawn legends
17
+ export const LEGEND_PAD = 8
18
+ export const LEGEND_ROW_H = 16
19
+ export const LEGEND_SWATCH = 12
20
+ export const LEGEND_FONT = 12
21
+ export const LEGEND_CHAR_W = 7
22
+
23
+ export const LEGEND_MAX_W = 360
24
+ export const LEGEND_TEXT_X = LEGEND_PAD + LEGEND_SWATCH + 6
25
+
26
+ export function getLegendWidth(rows: LegendRow[]) {
27
+ const maxLen = rows.reduce((a, r) => Math.max(a, r.label.length), 0)
28
+ const w = LEGEND_TEXT_X + LEGEND_PAD + maxLen * LEGEND_CHAR_W
29
+ return Math.min(LEGEND_MAX_W, Math.max(120, w))
30
+ }
31
+
32
+ export function legendHeight(rows: LegendRow[]) {
33
+ return LEGEND_PAD * 2 + rows.length * LEGEND_ROW_H
34
+ }
35
+
36
+ // resolved sizes/offsets (in svg user units) for the chosen export, shared by
37
+ // every layer; 'entire' renders the whole alignment unscrolled, 'viewport'
38
+ // mirrors the live scroll position
39
+ export interface Layout {
40
+ width: number
41
+ msaAreaWidth: number
42
+ height: number
43
+ contentHeight: number
44
+ trackHeight: number
45
+ // the band the minimap, the row panel headers and the tree overview stacked
46
+ // on the scale bar share across the top
47
+ topHeight: number
48
+ offsetX: number
49
+ offsetY: number
50
+ includeMinimap: boolean
51
+ legendWidth: number
52
+ }
53
+
54
+ export function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
55
+ const {
56
+ scrollX,
57
+ scrollY,
58
+ totalWidth,
59
+ totalHeight,
60
+ effectiveTreeAreaWidth,
61
+ rowPanelsWidth,
62
+ rowPanelsHeaderHeight,
63
+ totalTrackAreaHeight,
64
+ minimapHeight,
65
+ msaAreaHeight,
66
+ msaCanvasWidth,
67
+ showHorizontalScrollbar,
68
+ treeOverviewHeight,
69
+ treeScaleBar,
70
+ } = model
71
+ const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0
72
+ // the minimap reflects the live viewport scroll position, so it's only
73
+ // meaningful for a viewport export, never for the entire alignment -- and
74
+ // only when the live view shows one at all, since a minimap over an alignment
75
+ // that already fits marks a viewport wider than the bar
76
+ const includeMinimap =
77
+ opts.exportType === 'viewport' &&
78
+ !!opts.includeMinimap &&
79
+ showHorizontalScrollbar
80
+ const rows = legendRows(model.legends)
81
+ const legendWidth = rows.length > 0 ? getLegendWidth(rows) : 0
82
+ const topHeight = Math.max(
83
+ includeMinimap ? minimapHeight : 0,
84
+ rowPanelsHeaderHeight,
85
+ treeOverviewHeight + (treeScaleBar ? treeScaleBarHeight : 0),
86
+ )
87
+ const gutterWidth = effectiveTreeAreaWidth + rowPanelsWidth
88
+
89
+ // width stays content-only; the legend occupies an extra column added at the
90
+ // svg root in MsaSvg. The viewport export takes the alignment canvas's own
91
+ // size rather than the widget's -- the widget box also covers the header, the
92
+ // resize handle and the scrollbars, and rendering that box exports rows and
93
+ // columns the scrollbars hide on screen
94
+ return opts.exportType === 'entire'
95
+ ? {
96
+ width: totalWidth + gutterWidth,
97
+ msaAreaWidth: totalWidth,
98
+ height: totalHeight + trackHeight + topHeight,
99
+ contentHeight: totalHeight,
100
+ trackHeight,
101
+ topHeight,
102
+ offsetX: 0,
103
+ offsetY: 0,
104
+ includeMinimap,
105
+ legendWidth,
106
+ }
107
+ : {
108
+ width: msaCanvasWidth + gutterWidth,
109
+ msaAreaWidth: msaCanvasWidth,
110
+ height: msaAreaHeight + topHeight + trackHeight,
111
+ contentHeight: msaAreaHeight,
112
+ trackHeight,
113
+ topHeight,
114
+ offsetX: -scrollX,
115
+ offsetY: -scrollY,
116
+ includeMinimap,
117
+ legendWidth,
118
+ }
119
+ }
120
+
121
+ /**
122
+ * the exported page's size: the layout plus the legend column, and as tall as
123
+ * the legend where the key outgrows a short alignment
124
+ */
125
+ export function exportPageSize(model: MsaViewModel, layout: Layout) {
126
+ const { width, height, legendWidth, topHeight } = layout
127
+ return {
128
+ width: width + legendWidth,
129
+ height:
130
+ legendWidth > 0
131
+ ? Math.max(height, topHeight + legendHeight(legendRows(model.legends)))
132
+ : height,
133
+ }
134
+ }