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
@@ -1,18 +1,22 @@
1
1
  import React, { useCallback } 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 DragHandle, { scrollbarThumbFill } from '../DragHandle.tsx'
8
8
  import { msaThumbnail } from '../msa/msaRaster.ts'
9
- import { MINIMAP_BAR_HEIGHT, getMinimapLayout } from './minimapLayout.ts'
9
+ import {
10
+ MINIMAP_BAR_HEIGHT,
11
+ getMinimapLayout,
12
+ minimapDomainRects,
13
+ } from './minimapLayout.ts'
10
14
 
11
15
  import type { MsaViewModel } from '../../model.ts'
12
16
 
13
17
  const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
14
18
  const { minimapHeight, msaCanvasWidth, highResScaleFactor } = model
15
- const theme = useTheme()
19
+ const theme = useCanvasTheme()
16
20
  const { unit, s, w, polygonHeight, polygonPoints } = getMinimapLayout(model)
17
21
 
18
22
  // the whole alignment, sampled down to the bar
@@ -41,6 +45,10 @@ const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
41
45
  barHeight,
42
46
  )
43
47
  }
48
+ for (const r of minimapDomainRects(model, barWidth, barHeight)) {
49
+ ctx.fillStyle = r.fill
50
+ ctx.fillRect(r.x, r.y, r.w, r.h)
51
+ }
44
52
  },
45
53
  width: barWidth,
46
54
  height: barHeight,
@@ -2,7 +2,11 @@ import React from 'react'
2
2
 
3
3
  import { scrollbarThumbFill } from '../DragHandle.tsx'
4
4
  import { canvasHref, msaThumbnail } from '../msa/msaRaster.ts'
5
- import { MINIMAP_BAR_HEIGHT, getMinimapLayout } from './minimapLayout.ts'
5
+ import {
6
+ MINIMAP_BAR_HEIGHT,
7
+ getMinimapLayout,
8
+ minimapDomainRects,
9
+ } from './minimapLayout.ts'
6
10
 
7
11
  import type { MsaViewModel } from '../../model.ts'
8
12
  import type { Theme } from '@mui/material'
@@ -37,6 +41,18 @@ export default function MinimapSVG({
37
41
  preserveAspectRatio="none"
38
42
  />
39
43
  ) : null}
44
+ {minimapDomainRects(model, msaCanvasWidth, MINIMAP_BAR_HEIGHT).map(
45
+ ({ x, y, w, h, fill }) => (
46
+ <rect
47
+ key={`${x},${y},${w},${fill}`}
48
+ x={x}
49
+ y={y}
50
+ width={w}
51
+ height={h}
52
+ fill={fill}
53
+ />
54
+ ),
55
+ )}
40
56
  <rect
41
57
  x={0}
42
58
  y={0}
@@ -19,3 +19,30 @@ export function getMinimapLayout(model: MsaViewModel) {
19
19
  const polygonPoints = `${s + w},0 ${s},0 0,${polygonHeight} ${msaCanvasWidth},${polygonHeight}`
20
20
  return { unit, s, w, polygonHeight, polygonPoints }
21
21
  }
22
+
23
+ /**
24
+ * The domain overlay's boxes scaled onto the minimap bar, one per row's band,
25
+ * in the colors the alignment draws them, so the overview shows the domains
26
+ * the alignment does. Empty unless the domains fill the cells.
27
+ */
28
+ export function minimapDomainRects(
29
+ model: MsaViewModel,
30
+ width: number,
31
+ height: number,
32
+ ) {
33
+ const { cellFill, domainBands, featureColors, leaves, numColumns } = model
34
+ if (cellFill !== 'domain' || numColumns <= 0 || leaves.length === 0) {
35
+ return []
36
+ }
37
+ const colUnit = width / numColumns
38
+ const rowUnit = height / leaves.length
39
+ return leaves.flatMap((leaf, row) =>
40
+ (domainBands.get(leaf.data.name) ?? []).map(band => ({
41
+ x: band.startCol * colUnit,
42
+ y: row * rowUnit,
43
+ w: Math.max(1, (band.endCol - band.startCol) * colUnit),
44
+ h: Math.max(1, rowUnit),
45
+ fill: featureColors.get(band.annotation)?.fill ?? 'grey',
46
+ })),
47
+ )
48
+ }
@@ -3,8 +3,11 @@ import React, { useCallback, useRef } from 'react'
3
3
  import { observer } from 'mobx-react'
4
4
 
5
5
  import { useWheelScroll } from '../../useWheelScroll.ts'
6
+ import { zoomSnapshotScale } from '../../zoomSnapshot.ts'
7
+ import { alignmentAriaLabel } from '../controls.ts'
6
8
  import Loading from './Loading.tsx'
7
9
  import MSACanvasBlock from './MSACanvasBlock.tsx'
10
+ import { WheelZoomHint, useWheelZoomHint } from './WheelZoomHint.tsx'
8
11
  import { onMsaKey } from './msaKeys.ts'
9
12
 
10
13
  import type { MsaViewModel } from '../../model.ts'
@@ -19,7 +22,16 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
19
22
  scrollZoom,
20
23
  scrollX,
21
24
  scrollY,
25
+ zoomSnapshot,
22
26
  } = model
27
+ const blocks = zoomSnapshot
28
+ ? zoomSnapshot.blocksY.flatMap(by =>
29
+ zoomSnapshot.blocksX.map(bx => [bx, by] as const),
30
+ )
31
+ : blocks2d
32
+ const scale = zoomSnapshot
33
+ ? zoomSnapshotScale(model, zoomSnapshot)
34
+ : undefined
23
35
  const ref = useRef<HTMLDivElement>(null)
24
36
  const onScrollX = useCallback(
25
37
  (d: number) => {
@@ -35,15 +47,21 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
35
47
  )
36
48
  const onZoom = useCallback(
37
49
  (scaleFactor: number, offsetX: number, offsetY: number) => {
38
- model.zoomToPos(scaleFactor, offsetX, offsetY, model.wheelZoomAxis)
50
+ model.wheelZoomToPos(scaleFactor, offsetX, offsetY)
39
51
  },
40
52
  [model],
41
53
  )
54
+ const onZoomSettle = useCallback(() => {
55
+ model.endWheelZoom()
56
+ }, [model])
57
+ const hint = useWheelZoomHint(model)
42
58
  const { onMouseDown, onMouseUp } = useWheelScroll({
43
59
  ref,
44
60
  onScrollX,
45
61
  onScrollY,
46
62
  onZoom,
63
+ onZoomSettle,
64
+ onWheelScroll: hint.onWheelScroll,
47
65
  scrollZoom,
48
66
  })
49
67
 
@@ -54,7 +72,7 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
54
72
  // col*colWidth + scrollX, row*rowHeight + scrollY
55
73
  data-testid="msa_canvas"
56
74
  tabIndex={0}
57
- aria-label="Alignment. Arrow keys scroll, shift pages, + and - zoom, Home and End jump to the first and last column. Shift-drag selects cells, and Escape clears the selection."
75
+ aria-label={alignmentAriaLabel}
58
76
  onKeyDown={event => {
59
77
  if (onMsaKey(model, event)) {
60
78
  event.preventDefault()
@@ -87,11 +105,12 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
87
105
  position: 'absolute',
88
106
  top: 0,
89
107
  left: 0,
90
- transform: `translate(${scrollX}px, ${scrollY}px)`,
108
+ transform: `translate(${scrollX}px, ${scrollY}px)${scale ? ` scale(${scale.sx}, ${scale.sy})` : ''}`,
109
+ transformOrigin: '0 0',
91
110
  willChange: 'transform',
92
111
  }}
93
112
  >
94
- {blocks2d.map(([bx, by]) => (
113
+ {blocks.map(([bx, by]) => (
95
114
  <MSACanvasBlock
96
115
  key={`${bx}_${by}`}
97
116
  model={model}
@@ -103,6 +122,7 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
103
122
  ) : (
104
123
  <Loading />
105
124
  )}
125
+ {hint.visible ? <WheelZoomHint /> : null}
106
126
  </div>
107
127
  )
108
128
  })
@@ -1,16 +1,16 @@
1
- import React from 'react'
1
+ import React, { useRef } from 'react'
2
2
 
3
3
  import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
4
- import { useTheme } from '@mui/material'
5
4
  import { observer } from 'mobx-react'
6
5
 
6
+ import { useCanvasTheme } from '../../darkReader.ts'
7
7
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
8
- import { drawMsaRaster, rasterSupported } from './msaRaster.ts'
9
- import { renderBoxFeatureCanvasBlock } from './renderBoxFeatureCanvasBlock.ts'
8
+ import { drawMsaRaster, rasterApplies } from './msaRaster.ts'
10
9
  import { renderMSABlock } from './renderMSABlock.ts'
11
10
  import { useMsaBlockMouse } from './useMsaBlockMouse.ts'
12
11
 
13
12
  import type { MsaViewModel } from '../../model.ts'
13
+ import type { ZoomSnapshot } from '../../zoomSnapshot.ts'
14
14
 
15
15
  const MSACanvasBlock = observer(function ({
16
16
  model,
@@ -22,27 +22,29 @@ const MSACanvasBlock = observer(function ({
22
22
  offsetY: number
23
23
  }) {
24
24
  const { blockSize, highResScaleFactor } = model
25
- const theme = useTheme()
25
+ const theme = useCanvasTheme()
26
26
  const { anchor, onMouseMove, onMouseDown, onClick, onMouseLeave } =
27
27
  useMsaBlockMouse({ model, offsetX, offsetY })
28
28
 
29
29
  const canvasSize = blockSize * highResScaleFactor
30
+ const drawnAt = useRef<ZoomSnapshot>(undefined)
30
31
  const ref = useCanvasAutorun({
31
32
  draw: ctx => {
33
+ // during a wheel zoom MSACanvas scales what this block drew at the
34
+ // snapshot, so the block redraws only when a new snapshot is taken
35
+ const snap = model.zoomSnapshot
36
+ if (
37
+ snap &&
38
+ (snap === drawnAt.current ||
39
+ snap.colWidth !== model.colWidth ||
40
+ snap.rowHeight !== model.rowHeight)
41
+ ) {
42
+ return
43
+ }
44
+ drawnAt.current = snap
32
45
  ctx.resetTransform()
33
46
  ctx.clearRect(0, 0, canvasSize, canvasSize)
34
- renderBoxFeatureCanvasBlock({
35
- ctx,
36
- theme,
37
- offsetX,
38
- offsetY,
39
- model,
40
- })
41
- // the alignment background comes from zoom-independent raster tiles; the
42
- // domain overlay paints its own boxes instead, and letter-color mode has
43
- // no background to paint
44
- const rasterTiles =
45
- !model.actuallyShowDomains && model.bgColor && rasterSupported()
47
+ const rasterTiles = rasterApplies(model)
46
48
  if (rasterTiles) {
47
49
  drawMsaRaster({ ctx, model, theme, offsetX, offsetY })
48
50
  }
@@ -53,6 +55,7 @@ const MSACanvasBlock = observer(function ({
53
55
  offsetY,
54
56
  model,
55
57
  rasterTiles,
58
+ domains: true,
56
59
  })
57
60
  },
58
61
  width: canvasSize,
@@ -1,10 +1,10 @@
1
1
  import React from 'react'
2
2
 
3
3
  import { isAlive } from '@jbrowse/mobx-state-tree'
4
- import { useTheme } from '@mui/material'
5
4
  import { observer } from 'mobx-react'
6
5
 
7
6
  import { msaOverlayZIndex } from '../../constants.ts'
7
+ import { useCanvasTheme } from '../../darkReader.ts'
8
8
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
9
9
  import { renderMouseover } from './renderMSAMouseover.ts'
10
10
 
@@ -16,7 +16,7 @@ const MSAMouseoverCanvas = observer(function ({
16
16
  model: MsaViewModel
17
17
  }) {
18
18
  const { height, msaCanvasWidth: width, highResScaleFactor } = model
19
- const theme = useTheme()
19
+ const theme = useCanvasTheme()
20
20
  const canvasWidth = width * highResScaleFactor
21
21
  const canvasHeight = height * highResScaleFactor
22
22
  const ref = useCanvasAutorun({
@@ -0,0 +1,56 @@
1
+ import React, { useCallback, useRef, useState } from 'react'
2
+
3
+ import { Paper, Typography } from '@mui/material'
4
+
5
+ import type { MsaViewModel } from '../../model.ts'
6
+
7
+ const hinted = new WeakSet<MsaViewModel>()
8
+
9
+ const zoomKey =
10
+ typeof navigator !== 'undefined' && navigator.platform.startsWith('Mac')
11
+ ? '⌘'
12
+ : 'Ctrl'
13
+
14
+ export function useWheelZoomHint(model: MsaViewModel) {
15
+ const [visible, setVisible] = useState(false)
16
+ const timer = useRef<ReturnType<typeof setTimeout>>(undefined)
17
+ const onWheelScroll = useCallback(
18
+ (event: WheelEvent) => {
19
+ if (
20
+ !model.scrollZoom &&
21
+ Math.abs(event.deltaY) > Math.abs(event.deltaX) &&
22
+ !hinted.has(model)
23
+ ) {
24
+ hinted.add(model)
25
+ setVisible(true)
26
+ clearTimeout(timer.current)
27
+ timer.current = setTimeout(() => {
28
+ setVisible(false)
29
+ }, 2500)
30
+ }
31
+ },
32
+ [model],
33
+ )
34
+ return { visible, onWheelScroll }
35
+ }
36
+
37
+ export function WheelZoomHint() {
38
+ return (
39
+ <Paper
40
+ elevation={3}
41
+ style={{
42
+ position: 'absolute',
43
+ top: 12,
44
+ left: '50%',
45
+ transform: 'translateX(-50%)',
46
+ padding: '4px 12px',
47
+ pointerEvents: 'none',
48
+ zIndex: 2,
49
+ }}
50
+ >
51
+ <Typography variant="body2">
52
+ {zoomKey} + scroll to zoom, or check Zoom on scroll
53
+ </Typography>
54
+ </Paper>
55
+ )
56
+ }
@@ -37,6 +37,42 @@ export interface SpanLayout {
37
37
  headRise?: (height: number) => number
38
38
  }
39
39
 
40
+ /**
41
+ * What a renderer hands the span mark: its rows, where a band lands in them,
42
+ * its x mapping, the colors and the label each feature takes.
43
+ */
44
+ export interface SpanSource<T extends SpanBand> {
45
+ rows: SpanRow<T>[]
46
+ layout: SpanLayout
47
+ xOf: (band: T) => [number, number]
48
+ colors: Map<Annotation, { fill: string; stroke: string }>
49
+ labelOf?: (band: T) => string | undefined
50
+ labelDrawn?: (band: T) => void
51
+ }
52
+
53
+ /** a span as drawn: a box, or an arrow's outline where it carries a strand */
54
+ export interface SpanShape<T extends SpanBand> {
55
+ band: T
56
+ fill: string
57
+ stroke: string
58
+ box?: { x: number; y: number; w: number; h: number }
59
+ outline?: [number, number][]
60
+ }
61
+
62
+ export interface SpanLabel {
63
+ text: string
64
+ x: number
65
+ y: number
66
+ fill: string
67
+ }
68
+
69
+ /** one row's shapes, then the labels drawn over them at `fontSize` */
70
+ export interface SpanMarkRow<T extends SpanBand> {
71
+ shapes: SpanShape<T>[]
72
+ labels: SpanLabel[]
73
+ fontSize: number
74
+ }
75
+
40
76
  /** the rows of `bandsByRow` that `leaves` covers, in display order */
41
77
  export function spanRows<T extends SpanBand>(
42
78
  leaves: HierarchyNode<NodeWithIdsAndLength>[],
@@ -53,56 +89,49 @@ export function spanRows<T extends SpanBand>(
53
89
  }
54
90
 
55
91
  /**
56
- * The span mark, in whatever x space the caller maps its bands into: alignment
57
- * columns for the domain overlay, panel pixels for a `features` row panel. A
58
- * feature with a strand draws as an arrow, one without as a box, and a label
59
- * draws inside the span wherever the text fits.
92
+ * The span mark as geometry, in whatever x space `xOf` maps its bands into:
93
+ * alignment columns for the domain overlay, panel pixels for a `features` row
94
+ * panel. A feature with a strand is an arrow, one without a box, and a label
95
+ * lands inside the span wherever `measureText` says the text fits.
60
96
  *
61
- * `xOf` returns the span's `[xStart, xEnd]` in the context's coordinates, and
62
- * the context is translated so that `y - rowHeight` is the row's top edge.
97
+ * `y - rowHeight` is the row's top edge in the coordinates `rows` gives.
63
98
  */
64
- export function drawFeatureSpans<T extends SpanBand>({
65
- ctx,
99
+ export function featureSpanMarks<T extends SpanBand>({
66
100
  rows,
67
101
  layout,
68
102
  xOf,
69
103
  colors,
70
104
  labelOf,
71
105
  labelDrawn,
72
- contrastText,
106
+ measureText,
73
107
  xMin,
74
108
  xMax,
75
- }: {
76
- ctx: RenderCtx
77
- rows: SpanRow<T>[]
78
- layout: SpanLayout
79
- xOf: (band: T) => [number, number]
80
- colors: Map<Annotation, { fill: string; stroke: string }>
81
- labelOf?: (band: T) => string | undefined
82
- labelDrawn?: (band: T) => void
83
- contrastText: (color: string) => string
109
+ }: SpanSource<T> & {
110
+ measureText: (text: string, fontSize: number) => number
84
111
  xMin: number
85
112
  xMax: number
86
- }) {
87
- // one row's spans are drawn before any of its labels. Bands are ordered
88
- // longest-first so a nested one lands on top, and drawing each label with its
89
- // own span put the child's box over the parent's text: `S` covered `RBD`, and
90
- // `RBD` read as `RB`. Reused across rows rather than allocated per row.
91
- const placed: {
92
- band: T
93
- xStart: number
94
- w: number
95
- t: number
96
- head: number
97
- fill: string
98
- }[] = []
99
-
113
+ }): SpanMarkRow<T>[] {
114
+ const out: SpanMarkRow<T>[] = []
100
115
  for (const { y, bands } of rows) {
101
116
  const h = layout.height(bands[0]!.laneCount)
102
117
  const headLen = layout.headLength(h)
103
118
  const rise = layout.headRise?.(h) ?? 0
104
119
  const labelled = labelOf !== undefined && h >= minFeatureLabelHeight
105
- placed.length = 0
120
+ const fontSize = Math.min(h - 2, 11)
121
+ const shapes: SpanShape<T>[] = []
122
+ const labels: SpanLabel[] = []
123
+ // one row's spans are drawn before any of its labels. Bands are ordered
124
+ // longest-first so a nested one lands on top, and drawing each label with
125
+ // its own span put the child's box over the parent's text: `S` covered
126
+ // `RBD`, and `RBD` read as `RB`.
127
+ const placed: {
128
+ band: T
129
+ xStart: number
130
+ w: number
131
+ t: number
132
+ head: number
133
+ fill: string
134
+ }[] = []
106
135
 
107
136
  for (const band of bands) {
108
137
  const [xStart, xEnd] = xOf(band)
@@ -112,55 +141,111 @@ export function drawFeatureSpans<T extends SpanBand>({
112
141
  }
113
142
  const t = layout.top(y, band.lane, h)
114
143
  const { fill, stroke } = colors.get(band.annotation)!
115
- ctx.fillStyle = fill
116
- ctx.strokeStyle = stroke
117
144
  const { strand } = band.annotation
118
145
  const head = strand === undefined ? 0 : Math.min(headLen, w)
119
- if (strand === undefined) {
120
- ctx.fillRect(xStart, t, w, h)
121
- ctx.strokeRect(xStart, t, w, h)
122
- } else {
123
- drawGeneArrow({ ctx, x: xStart, t, w, h, head, rise, strand })
124
- }
146
+ shapes.push(
147
+ strand === undefined
148
+ ? { band, fill, stroke, box: { x: xStart, y: t, w, h } }
149
+ : {
150
+ band,
151
+ fill,
152
+ stroke,
153
+ outline: geneArrow({ x: xStart, t, w, h, head, rise, strand }),
154
+ },
155
+ )
125
156
  if (labelled) {
126
157
  placed.push({ band, xStart, w, t, head, fill })
127
158
  }
128
159
  }
129
160
 
130
- if (placed.length > 0) {
131
- ctx.font = `${Math.min(h - 2, 11)}px sans-serif`
161
+ for (const { band, xStart, w, t, head, fill } of placed) {
162
+ // asking for the label here rather than in the pass above keeps the
163
+ // once-per-block segment numbering in drawing order
164
+ const text = labelOf!(band)
165
+ // the head narrows to a point, so only about half of it holds text
166
+ if (
167
+ text !== undefined &&
168
+ measureText(text, fontSize) + 2 <= w - head / 2
169
+ ) {
170
+ labelDrawn?.(band)
171
+ labels.push({ text, x: xStart + w / 2, y: t + h / 2, fill })
172
+ }
173
+ }
174
+ out.push({ shapes, labels, fontSize })
175
+ }
176
+ return out
177
+ }
178
+
179
+ /**
180
+ * The span mark, drawn into `ctx` from `featureSpanMarks`, with the context
181
+ * translated so that `y - rowHeight` is the row's top edge.
182
+ */
183
+ export function drawFeatureSpans<T extends SpanBand>({
184
+ ctx,
185
+ contrastText,
186
+ ...source
187
+ }: SpanSource<T> & {
188
+ ctx: RenderCtx
189
+ contrastText: (color: string) => string
190
+ xMin: number
191
+ xMax: number
192
+ }) {
193
+ let font = ''
194
+ const setFont = (fontSize: number) => {
195
+ const next = `${fontSize}px sans-serif`
196
+ if (next !== font) {
197
+ ctx.font = next
198
+ font = next
199
+ }
200
+ }
201
+ const rows = featureSpanMarks({
202
+ ...source,
203
+ measureText: (text, fontSize) => {
204
+ setFont(fontSize)
205
+ return ctx.measureText(text).width
206
+ },
207
+ })
208
+ for (const { shapes, labels, fontSize } of rows) {
209
+ for (const { fill, stroke, box, outline } of shapes) {
210
+ ctx.fillStyle = fill
211
+ ctx.strokeStyle = stroke
212
+ if (box) {
213
+ ctx.fillRect(box.x, box.y, box.w, box.h)
214
+ ctx.strokeRect(box.x, box.y, box.w, box.h)
215
+ } else if (outline) {
216
+ ctx.beginPath()
217
+ ctx.moveTo(...outline[0]!)
218
+ for (let i = 1; i < outline.length; i++) {
219
+ ctx.lineTo(...outline[i]!)
220
+ }
221
+ ctx.closePath()
222
+ ctx.fill()
223
+ ctx.stroke()
224
+ }
225
+ }
226
+ if (labels.length > 0) {
227
+ setFont(fontSize)
132
228
  ctx.textAlign = 'center'
133
229
  ctx.textBaseline = 'middle'
134
- for (const { band, xStart, w, t, head, fill } of placed) {
135
- // asking for the label here rather than in the pass above keeps the
136
- // once-per-block segment numbering in drawing order
137
- const label = labelOf!(band)
138
- // the head narrows to a point, so only about half of it holds text
139
- if (
140
- label !== undefined &&
141
- ctx.measureText(label).width + 2 <= w - head / 2
142
- ) {
143
- labelDrawn?.(band)
144
- ctx.fillStyle = contrastText(fill)
145
- ctx.fillText(label, xStart + w / 2, t + h / 2)
146
- }
230
+ for (const { text, x, y, fill } of labels) {
231
+ ctx.fillStyle = contrastText(fill)
232
+ ctx.fillText(text, x, y)
147
233
  }
148
234
  }
149
235
  }
150
236
  }
151
237
 
152
238
  /**
153
- * A gene arrow, drawn the way gggenes and gggenomes draw one: the head is the
154
- * last `head` pixels *of* the feature, tapering to a point at its end, and a
155
- * feature shorter than the head is all head. The glyph therefore covers its
239
+ * A gene arrow's outline, the way gggenes and gggenomes draw one: the head is
240
+ * the last `head` pixels *of* the feature, tapering to a point at its end, and
241
+ * a feature shorter than the head is all head. The glyph therefore covers its
156
242
  * start..end span and nothing else, so adjacent genes butt together instead of
157
243
  * biting triangles out of each other, a neighbour never draws over a label, and
158
244
  * a gene 20 columns wide reads as 20 columns wide.
159
245
  *
160
246
  * `rise` lifts the head above the band, for a bar too thin to taper.
161
247
  */
162
- function drawGeneArrow({
163
- ctx,
248
+ function geneArrow({
164
249
  x,
165
250
  t,
166
251
  w,
@@ -169,7 +254,6 @@ function drawGeneArrow({
169
254
  rise,
170
255
  strand,
171
256
  }: {
172
- ctx: RenderCtx
173
257
  x: number
174
258
  t: number
175
259
  w: number
@@ -177,29 +261,26 @@ function drawGeneArrow({
177
261
  head: number
178
262
  rise: number
179
263
  strand: number
180
- }) {
264
+ }): [number, number][] {
181
265
  const tip = strand > 0 ? x + w : x
182
266
  const tail = strand > 0 ? x : x + w
183
267
  const flange = strand > 0 ? tip - head : tip + head
184
268
  const bottom = t + h
185
269
  const crown = t - rise
186
270
  const point = (crown + bottom) / 2
187
- ctx.beginPath()
188
271
  if (head >= w) {
189
- ctx.moveTo(tail, crown)
190
- ctx.lineTo(tip, point)
191
- ctx.lineTo(tail, bottom)
192
- } else {
193
- ctx.moveTo(tail, t)
194
- ctx.lineTo(flange, t)
195
- if (rise > 0) {
196
- ctx.lineTo(flange, crown)
197
- }
198
- ctx.lineTo(tip, point)
199
- ctx.lineTo(flange, bottom)
200
- ctx.lineTo(tail, bottom)
272
+ return [
273
+ [tail, crown],
274
+ [tip, point],
275
+ [tail, bottom],
276
+ ]
201
277
  }
202
- ctx.closePath()
203
- ctx.fill()
204
- ctx.stroke()
278
+ return [
279
+ [tail, t],
280
+ [flange, t],
281
+ ...(rise > 0 ? [[flange, crown] as [number, number]] : []),
282
+ [tip, point],
283
+ [flange, bottom],
284
+ [tail, bottom],
285
+ ]
205
286
  }
@@ -27,6 +27,9 @@ export function onMsaKey(
27
27
  case '-':
28
28
  model.zoomOut()
29
29
  return true
30
+ case '0':
31
+ model.resetZoom()
32
+ return true
30
33
  case 'Home':
31
34
  model.setScrollX(0)
32
35
  return true