react-msaview 7.0.0 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/README.md +11 -10
  2. package/bundle/index.js +122 -111
  3. package/bundle/index.js.map +4 -4
  4. package/dist/calculateBlocks.js +5 -3
  5. package/dist/calculateBlocks.js.map +1 -1
  6. package/dist/colorSchemes.js +1 -2
  7. package/dist/colorSchemes.js.map +1 -1
  8. package/dist/columnStats.d.ts +28 -0
  9. package/dist/columnStats.js +34 -0
  10. package/dist/columnStats.js.map +1 -0
  11. package/dist/components/DragHandle.d.ts +3 -2
  12. package/dist/components/DragHandle.js +11 -5
  13. package/dist/components/DragHandle.js.map +1 -1
  14. package/dist/components/Loading.js +2 -8
  15. package/dist/components/Loading.js.map +1 -1
  16. package/dist/components/MSAView.js +17 -6
  17. package/dist/components/MSAView.js.map +1 -1
  18. package/dist/components/MSAViewer.d.ts +64 -7
  19. package/dist/components/MSAViewer.js +128 -23
  20. package/dist/components/MSAViewer.js.map +1 -1
  21. package/dist/components/PortalTooltip.d.ts +11 -0
  22. package/dist/components/PortalTooltip.js +29 -0
  23. package/dist/components/PortalTooltip.js.map +1 -0
  24. package/dist/components/ResizeHandles.js +11 -29
  25. package/dist/components/ResizeHandles.js.map +1 -1
  26. package/dist/components/SequenceTextArea.js +3 -4
  27. package/dist/components/SequenceTextArea.js.map +1 -1
  28. package/dist/components/Track.js +43 -24
  29. package/dist/components/Track.js.map +1 -1
  30. package/dist/components/dialogs/AboutDialog.js +6 -6
  31. package/dist/components/dialogs/AboutDialog.js.map +1 -1
  32. package/dist/components/dialogs/AnnotationFileDialog.js +5 -2
  33. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
  34. package/dist/components/dialogs/ExportSVGDialog.js +6 -4
  35. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  36. package/dist/components/dialogs/FeatureDialog.js +8 -3
  37. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  38. package/dist/components/dialogs/TrackInfoDialog.js +1 -1
  39. package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
  40. package/dist/components/header/GappynessSlider.js +6 -11
  41. package/dist/components/header/GappynessSlider.js.map +1 -1
  42. package/dist/components/header/Header.js +4 -1
  43. package/dist/components/header/Header.js.map +1 -1
  44. package/dist/components/header/LoadWarnings.js +6 -23
  45. package/dist/components/header/LoadWarnings.js.map +1 -1
  46. package/dist/components/header/ScrollZoomIcons.d.ts +6 -0
  47. package/dist/components/header/ScrollZoomIcons.js +35 -0
  48. package/dist/components/header/ScrollZoomIcons.js.map +1 -0
  49. package/dist/components/header/TreeSettingsMenu.js +1 -3
  50. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  51. package/dist/components/header/UnshareableDataWarning.js +2 -4
  52. package/dist/components/header/UnshareableDataWarning.js.map +1 -1
  53. package/dist/components/header/ZoomControls.js +49 -6
  54. package/dist/components/header/ZoomControls.js.map +1 -1
  55. package/dist/components/header/getAnnotationsMenu.js +3 -6
  56. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  57. package/dist/components/header/settingsMenuItems.js +12 -9
  58. package/dist/components/header/settingsMenuItems.js.map +1 -1
  59. package/dist/components/import/ImportForm.js +1 -1
  60. package/dist/components/import/ImportForm.js.map +1 -1
  61. package/dist/components/import/util.js +1 -2
  62. package/dist/components/import/util.js.map +1 -1
  63. package/dist/components/minimap/Minimap.js +1 -2
  64. package/dist/components/minimap/Minimap.js.map +1 -1
  65. package/dist/components/msa/AnnotationLegend.js +27 -19
  66. package/dist/components/msa/AnnotationLegend.js.map +1 -1
  67. package/dist/components/msa/MSACanvas.js +2 -4
  68. package/dist/components/msa/MSACanvas.js.map +1 -1
  69. package/dist/components/msa/MSACanvasBlock.js +13 -15
  70. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  71. package/dist/components/msa/MSAMouseoverCanvas.js +2 -1
  72. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  73. package/dist/components/msa/domainBandCursor.d.ts +7 -11
  74. package/dist/components/msa/domainBandCursor.js +7 -11
  75. package/dist/components/msa/domainBandCursor.js.map +1 -1
  76. package/dist/components/msa/drawFeatureSpans.d.ts +59 -0
  77. package/dist/components/msa/drawFeatureSpans.js +113 -0
  78. package/dist/components/msa/drawFeatureSpans.js.map +1 -0
  79. package/dist/components/msa/legendRows.d.ts +8 -0
  80. package/dist/components/msa/legendRows.js +16 -0
  81. package/dist/components/msa/legendRows.js.map +1 -0
  82. package/dist/components/msa/msaRaster.d.ts +24 -31
  83. package/dist/components/msa/msaRaster.js +30 -39
  84. package/dist/components/msa/msaRaster.js.map +1 -1
  85. package/dist/components/msa/packDomainLanes.d.ts +20 -0
  86. package/dist/components/msa/packDomainLanes.js +33 -0
  87. package/dist/components/msa/packDomainLanes.js.map +1 -0
  88. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +3 -1
  89. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +50 -87
  90. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  91. package/dist/components/msa/renderHighlights.d.ts +15 -13
  92. package/dist/components/msa/renderHighlights.js +43 -20
  93. package/dist/components/msa/renderHighlights.js.map +1 -1
  94. package/dist/components/msa/renderMSABlock.js +12 -12
  95. package/dist/components/msa/renderMSABlock.js.map +1 -1
  96. package/dist/components/msa/tileColor.js +4 -7
  97. package/dist/components/msa/tileColor.js.map +1 -1
  98. package/dist/components/msa/useMsaBlockMouse.d.ts +5 -3
  99. package/dist/components/msa/useMsaBlockMouse.js +6 -6
  100. package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
  101. package/dist/components/rowpanels/RowPanelBlock.d.ts +9 -0
  102. package/dist/components/rowpanels/RowPanelBlock.js +86 -0
  103. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -0
  104. package/dist/components/rowpanels/RowPanelHeaders.d.ts +12 -0
  105. package/dist/components/rowpanels/RowPanelHeaders.js +38 -0
  106. package/dist/components/rowpanels/RowPanelHeaders.js.map +1 -0
  107. package/dist/components/{msa/ColumnStats.d.ts → rowpanels/RowPanels.d.ts} +2 -2
  108. package/dist/components/rowpanels/RowPanels.js +32 -0
  109. package/dist/components/rowpanels/RowPanels.js.map +1 -0
  110. package/dist/components/rowpanels/headerLayout.d.ts +1 -0
  111. package/dist/components/rowpanels/headerLayout.js +5 -0
  112. package/dist/components/rowpanels/headerLayout.js.map +1 -0
  113. package/dist/components/rowpanels/renderFeaturePanel.d.ts +21 -0
  114. package/dist/components/rowpanels/renderFeaturePanel.js +37 -0
  115. package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -0
  116. package/dist/components/rowpanels/renderRowPanel.d.ts +23 -0
  117. package/dist/components/rowpanels/renderRowPanel.js +22 -0
  118. package/dist/components/rowpanels/renderRowPanel.js.map +1 -0
  119. package/dist/components/rowpanels/renderStrip.d.ts +20 -0
  120. package/dist/components/rowpanels/renderStrip.js +26 -0
  121. package/dist/components/rowpanels/renderStrip.js.map +1 -0
  122. package/dist/components/tracks/TrackBlocks.js +3 -5
  123. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  124. package/dist/components/tracks/TrackTooltipContent.d.ts +15 -0
  125. package/dist/components/tracks/TrackTooltipContent.js +109 -0
  126. package/dist/components/tracks/TrackTooltipContent.js.map +1 -0
  127. package/dist/components/tracks/drawTracks.d.ts +4 -4
  128. package/dist/components/tracks/drawTracks.js +25 -39
  129. package/dist/components/tracks/drawTracks.js.map +1 -1
  130. package/dist/components/tracks/useTrackHover.d.ts +22 -0
  131. package/dist/components/tracks/useTrackHover.js +30 -0
  132. package/dist/components/tracks/useTrackHover.js.map +1 -0
  133. package/dist/components/tree/CladeLabels.d.ts +12 -0
  134. package/dist/components/tree/CladeLabels.js +48 -0
  135. package/dist/components/tree/CladeLabels.js.map +1 -0
  136. package/dist/components/tree/TreeBranchMenu.js +1 -1
  137. package/dist/components/tree/TreeBranchMenu.js.map +1 -1
  138. package/dist/components/tree/TreeCanvas.js +5 -4
  139. package/dist/components/tree/TreeCanvas.js.map +1 -1
  140. package/dist/components/tree/TreeCanvasBlock.js +4 -21
  141. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  142. package/dist/components/tree/TreeNodeMenu.js +1 -1
  143. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  144. package/dist/components/tree/TreeOverview.d.ts +14 -0
  145. package/dist/components/tree/TreeOverview.js +75 -0
  146. package/dist/components/tree/TreeOverview.js.map +1 -0
  147. package/dist/components/tree/TreeRuler.js +9 -13
  148. package/dist/components/tree/TreeRuler.js.map +1 -1
  149. package/dist/components/tree/cladeBrackets.d.ts +25 -0
  150. package/dist/components/tree/cladeBrackets.js +51 -0
  151. package/dist/components/tree/cladeBrackets.js.map +1 -0
  152. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +2 -2
  153. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  154. package/dist/components/tree/renderTreeCanvas.js +160 -25
  155. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  156. package/dist/components/tree/renderTreeOverview.d.ts +52 -0
  157. package/dist/components/tree/renderTreeOverview.js +138 -0
  158. package/dist/components/tree/renderTreeOverview.js.map +1 -0
  159. package/dist/components/tree/useTreeHover.js +2 -4
  160. package/dist/components/tree/useTreeHover.js.map +1 -1
  161. package/dist/constants.d.ts +16 -0
  162. package/dist/constants.js +52 -22
  163. package/dist/constants.js.map +1 -1
  164. package/dist/createPaletteMap.d.ts +7 -6
  165. package/dist/createPaletteMap.js +10 -9
  166. package/dist/createPaletteMap.js.map +1 -1
  167. package/dist/element.d.ts +13 -0
  168. package/dist/element.js +269 -0
  169. package/dist/element.js.map +1 -0
  170. package/dist/featureFields.d.ts +11 -0
  171. package/dist/featureFields.js +17 -0
  172. package/dist/featureFields.js.map +1 -0
  173. package/dist/flatToTree.js +1 -2
  174. package/dist/flatToTree.js.map +1 -1
  175. package/dist/ggplotPalettes.d.ts +10 -0
  176. package/dist/ggplotPalettes.js +58 -0
  177. package/dist/ggplotPalettes.js.map +1 -1
  178. package/dist/hierarchy.d.ts +27 -0
  179. package/dist/hierarchy.js +66 -9
  180. package/dist/hierarchy.js.map +1 -1
  181. package/dist/index.d.ts +9 -1
  182. package/dist/index.js +5 -3
  183. package/dist/index.js.map +1 -1
  184. package/dist/model/DataModel.js +2 -5
  185. package/dist/model/DataModel.js.map +1 -1
  186. package/dist/model/msaModel.d.ts +3 -2
  187. package/dist/model/msaModel.js +3 -2
  188. package/dist/model/msaModel.js.map +1 -1
  189. package/dist/model/treeModel.d.ts +30 -0
  190. package/dist/model/treeModel.js +37 -1
  191. package/dist/model/treeModel.js.map +1 -1
  192. package/dist/model.d.ts +549 -294
  193. package/dist/model.js +1412 -621
  194. package/dist/model.js.map +1 -1
  195. package/dist/mount.d.ts +11 -0
  196. package/dist/mount.js +22 -0
  197. package/dist/mount.js.map +1 -0
  198. package/dist/neighborJoining.js +8 -32
  199. package/dist/neighborJoining.js.map +1 -1
  200. package/dist/renderToStaticMarkup.d.ts +10 -19
  201. package/dist/renderToStaticMarkup.js +13 -24
  202. package/dist/renderToStaticMarkup.js.map +1 -1
  203. package/dist/renderToSvg.js +180 -56
  204. package/dist/renderToSvg.js.map +1 -1
  205. package/dist/rowCoordinateCalculations.d.ts +4 -6
  206. package/dist/rowCoordinateCalculations.js +4 -6
  207. package/dist/rowCoordinateCalculations.js.map +1 -1
  208. package/dist/scales.d.ts +22 -0
  209. package/dist/scales.js +30 -0
  210. package/dist/scales.js.map +1 -0
  211. package/dist/seqPosToGlobalCol.d.ts +2 -3
  212. package/dist/seqPosToGlobalCol.js +2 -3
  213. package/dist/seqPosToGlobalCol.js.map +1 -1
  214. package/dist/sequenceLogo.d.ts +6 -11
  215. package/dist/sequenceLogo.js +6 -11
  216. package/dist/sequenceLogo.js.map +1 -1
  217. package/dist/stripDefault.d.ts +7 -14
  218. package/dist/stripDefault.js +7 -14
  219. package/dist/stripDefault.js.map +1 -1
  220. package/dist/svgTestUtil.d.ts +186 -70
  221. package/dist/types.d.ts +221 -21
  222. package/dist/umd.d.ts +2 -0
  223. package/dist/umd.js +4 -2
  224. package/dist/umd.js.map +1 -1
  225. package/dist/useCanvasAutorun.d.ts +5 -10
  226. package/dist/useCanvasAutorun.js +5 -10
  227. package/dist/useCanvasAutorun.js.map +1 -1
  228. package/dist/useMsaSvgFigure.d.ts +20 -0
  229. package/dist/useMsaSvgFigure.js +46 -0
  230. package/dist/useMsaSvgFigure.js.map +1 -0
  231. package/dist/useWheelScroll.js +0 -4
  232. package/dist/useWheelScroll.js.map +1 -1
  233. package/dist/util.d.ts +5 -6
  234. package/dist/util.js +13 -6
  235. package/dist/util.js.map +1 -1
  236. package/dist/version.d.ts +1 -1
  237. package/dist/version.js +1 -1
  238. package/package.json +19 -12
  239. package/src/calculateBlocks.ts +5 -3
  240. package/src/colorSchemes.ts +1 -2
  241. package/src/columnStats.ts +58 -0
  242. package/src/components/DragHandle.tsx +18 -7
  243. package/src/components/Loading.tsx +4 -36
  244. package/src/components/MSAView.tsx +19 -5
  245. package/src/components/MSAViewer.tsx +240 -28
  246. package/src/components/PortalTooltip.tsx +47 -0
  247. package/src/components/ResizeHandles.tsx +17 -32
  248. package/src/components/SequenceTextArea.tsx +3 -4
  249. package/src/components/Track.tsx +57 -19
  250. package/src/components/dialogs/AboutDialog.tsx +35 -6
  251. package/src/components/dialogs/AnnotationFileDialog.tsx +10 -1
  252. package/src/components/dialogs/ExportSVGDialog.tsx +8 -3
  253. package/src/components/dialogs/FeatureDialog.tsx +11 -3
  254. package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
  255. package/src/components/header/GappynessSlider.tsx +6 -11
  256. package/src/components/header/Header.tsx +7 -1
  257. package/src/components/header/LoadWarnings.tsx +11 -25
  258. package/src/components/header/ScrollZoomIcons.tsx +52 -0
  259. package/src/components/header/TreeSettingsMenu.tsx +1 -3
  260. package/src/components/header/UnshareableDataWarning.tsx +2 -4
  261. package/src/components/header/ZoomControls.tsx +74 -14
  262. package/src/components/header/getAnnotationsMenu.ts +3 -6
  263. package/src/components/header/settingsMenuItems.ts +13 -8
  264. package/src/components/import/ImportForm.tsx +3 -5
  265. package/src/components/import/ImportFormExamples.tsx +2 -2
  266. package/src/components/import/util.ts +1 -2
  267. package/src/components/minimap/Minimap.tsx +1 -2
  268. package/src/components/msa/AnnotationLegend.tsx +48 -38
  269. package/src/components/msa/MSACanvas.tsx +3 -8
  270. package/src/components/msa/MSACanvasBlock.tsx +13 -16
  271. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -1
  272. package/src/components/msa/domainBandCursor.ts +7 -11
  273. package/src/components/msa/drawFeatureSpans.ts +205 -0
  274. package/src/components/msa/legendRows.ts +24 -0
  275. package/src/components/msa/msaRaster.ts +30 -39
  276. package/src/components/msa/packDomainLanes.ts +36 -0
  277. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +67 -123
  278. package/src/components/msa/renderHighlights.ts +53 -20
  279. package/src/components/msa/renderMSABlock.ts +14 -11
  280. package/src/components/msa/tileColor.ts +4 -7
  281. package/src/components/msa/useMsaBlockMouse.ts +6 -6
  282. package/src/components/rowpanels/RowPanelBlock.tsx +139 -0
  283. package/src/components/rowpanels/RowPanelHeaders.tsx +57 -0
  284. package/src/components/rowpanels/RowPanels.tsx +77 -0
  285. package/src/components/rowpanels/headerLayout.ts +5 -0
  286. package/src/components/rowpanels/renderFeaturePanel.ts +66 -0
  287. package/src/components/rowpanels/renderRowPanel.ts +41 -0
  288. package/src/components/rowpanels/renderStrip.ts +46 -0
  289. package/src/components/tracks/TrackBlocks.tsx +3 -5
  290. package/src/components/tracks/TrackTooltipContent.tsx +156 -0
  291. package/src/components/tracks/drawTracks.ts +25 -50
  292. package/src/components/tracks/useTrackHover.ts +42 -0
  293. package/src/components/tree/CladeLabels.tsx +77 -0
  294. package/src/components/tree/TreeBranchMenu.tsx +1 -2
  295. package/src/components/tree/TreeCanvas.tsx +2 -3
  296. package/src/components/tree/TreeCanvasBlock.tsx +8 -32
  297. package/src/components/tree/TreeNodeMenu.tsx +1 -2
  298. package/src/components/tree/TreeOverview.tsx +106 -0
  299. package/src/components/tree/TreeRuler.tsx +11 -22
  300. package/src/components/tree/cladeBrackets.ts +73 -0
  301. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
  302. package/src/components/tree/renderTreeCanvas.ts +242 -21
  303. package/src/components/tree/renderTreeOverview.ts +212 -0
  304. package/src/components/tree/useTreeHover.ts +2 -4
  305. package/src/constants.ts +61 -22
  306. package/src/createPaletteMap.ts +10 -9
  307. package/src/element.ts +321 -0
  308. package/src/featureFields.ts +19 -0
  309. package/src/flatToTree.ts +1 -2
  310. package/src/ggplotPalettes.ts +60 -0
  311. package/src/hierarchy.ts +77 -9
  312. package/src/index.ts +31 -7
  313. package/src/model/DataModel.ts +2 -5
  314. package/src/model/msaModel.ts +3 -2
  315. package/src/model/treeModel.ts +42 -0
  316. package/src/model.ts +1677 -643
  317. package/src/mount.tsx +32 -0
  318. package/src/neighborJoining.ts +8 -32
  319. package/src/renderToStaticMarkup.ts +13 -24
  320. package/src/renderToSvg.tsx +335 -68
  321. package/src/rowCoordinateCalculations.ts +4 -6
  322. package/src/scales.ts +47 -0
  323. package/src/seqPosToGlobalCol.ts +2 -3
  324. package/src/sequenceLogo.ts +6 -11
  325. package/src/stripDefault.ts +7 -14
  326. package/src/types.ts +260 -45
  327. package/src/umd.ts +4 -2
  328. package/src/useCanvasAutorun.ts +5 -10
  329. package/src/useMsaSvgFigure.ts +72 -0
  330. package/src/useWheelScroll.ts +0 -4
  331. package/src/util.ts +15 -6
  332. package/src/version.ts +1 -1
  333. package/dist/components/msa/ColumnStats.js +0 -42
  334. package/dist/components/msa/ColumnStats.js.map +0 -1
  335. package/src/components/msa/ColumnStats.tsx +0 -54
@@ -5,15 +5,30 @@ import { when } from 'mobx'
5
5
 
6
6
  import { visibleRowRange } from './components/getVisibleLeaves.ts'
7
7
  import MinimapSVG from './components/minimap/MinimapSVG.tsx'
8
+ import { legendRows } from './components/msa/legendRows.ts'
8
9
  import { rasterImageHref, rasterSupported } from './components/msa/msaRaster.ts'
9
10
  import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock.ts'
10
11
  import { renderPersistentHighlights } from './components/msa/renderHighlights.ts'
11
12
  import { renderMSABlock } from './components/msa/renderMSABlock.ts'
12
13
  import { visibleColRange } from './components/msa/visibleColRange.ts'
14
+ import { headerRunsAcross } from './components/rowpanels/headerLayout.ts'
15
+ import { renderRowPanels } from './components/rowpanels/renderRowPanel.ts'
13
16
  import { renderAllTracks } from './components/tracks/drawTracks.ts'
17
+ import {
18
+ bracketGap,
19
+ cladeHeight,
20
+ cladeInGutter,
21
+ cladeLabelHorizontal,
22
+ cladeLabelOffset,
23
+ } from './components/tree/cladeBrackets.ts'
14
24
  import { renderTreeCanvas } from './components/tree/renderTreeCanvas.ts'
25
+ import { renderTreeOverview } from './components/tree/renderTreeOverview.ts'
26
+ import { treeScaleBarHeight } from './constants.ts'
27
+ import { measureTextCanvas } from './measureTextCanvas.ts'
15
28
  import { renderToStaticMarkup, svgSafeColors } from './renderToStaticMarkup.ts'
29
+ import { outlineColor } from './util.ts'
16
30
 
31
+ import type { LegendRow } from './components/msa/legendRows.ts'
17
32
  import type { MsaViewModel } from './model.ts'
18
33
  import type { Context as ContextType } from '@jbrowse/svgcanvas'
19
34
  import type { Theme } from '@mui/material'
@@ -25,28 +40,29 @@ export interface ExportSvgOptions {
25
40
  exportType: 'entire' | 'viewport'
26
41
  }
27
42
 
28
- // domain-legend geometry, shared by the width calculation and the renderer so
29
- // the reserved column on the right exactly fits the drawn legend
43
+ // legend geometry, shared by the width calculation and the renderer so the
44
+ // reserved column on the right exactly fits the drawn legends
30
45
  const LEGEND_PAD = 8
31
46
  const LEGEND_ROW_H = 16
32
47
  const LEGEND_SWATCH = 12
33
48
  const LEGEND_FONT = 12
34
49
  const LEGEND_CHAR_W = 7
35
50
 
51
+ // the width of a character as a fraction of the font size, close enough for a
52
+ // sans-serif average
53
+ const CHAR_WIDTH = 0.6
54
+
36
55
  const LEGEND_MAX_W = 360
37
56
  const LEGEND_TEXT_X = LEGEND_PAD + LEGEND_SWATCH + 6
38
57
 
39
- function getLegendWidth(model: MsaViewModel) {
40
- const maxLen = model.visibleDomainTypes.reduce(
41
- (a, d) => Math.max(a, d.name.length),
42
- 0,
43
- )
58
+ function getLegendWidth(rows: LegendRow[]) {
59
+ const maxLen = rows.reduce((a, r) => Math.max(a, r.label.length), 0)
44
60
  const w = LEGEND_TEXT_X + LEGEND_PAD + maxLen * LEGEND_CHAR_W
45
61
  return Math.min(LEGEND_MAX_W, Math.max(120, w))
46
62
  }
47
63
 
48
- function legendHeight(domainTypes: unknown[]) {
49
- return LEGEND_PAD * 2 + domainTypes.length * LEGEND_ROW_H
64
+ function legendHeight(rows: LegendRow[]) {
65
+ return LEGEND_PAD * 2 + rows.length * LEGEND_ROW_H
50
66
  }
51
67
 
52
68
  // resolved sizes/offsets (in svg user units) for the chosen export, shared by
@@ -58,6 +74,9 @@ interface Layout {
58
74
  height: number
59
75
  contentHeight: number
60
76
  trackHeight: number
77
+ // the band the minimap, the row panel headers and the tree overview stacked
78
+ // on the scale bar share across the top
79
+ topHeight: number
61
80
  offsetX: number
62
81
  offsetY: number
63
82
  includeMinimap: boolean
@@ -71,11 +90,15 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
71
90
  totalWidth,
72
91
  totalHeight,
73
92
  treeAreaWidth,
93
+ rowPanelsWidth,
94
+ rowPanelsHeaderHeight,
74
95
  totalTrackAreaHeight,
75
96
  minimapHeight,
76
97
  msaAreaHeight,
77
98
  msaCanvasWidth,
78
99
  showHorizontalScrollbar,
100
+ treeOverviewHeight,
101
+ treeScaleBar,
79
102
  } = model
80
103
  const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0
81
104
  // the minimap reflects the live viewport scroll position, so it's only
@@ -86,10 +109,14 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
86
109
  opts.exportType === 'viewport' &&
87
110
  !!opts.includeMinimap &&
88
111
  showHorizontalScrollbar
89
- const legendWidth =
90
- model.actuallyShowDomains && model.visibleDomainTypes.length > 0
91
- ? getLegendWidth(model)
92
- : 0
112
+ const rows = legendRows(model.legends)
113
+ const legendWidth = rows.length > 0 ? getLegendWidth(rows) : 0
114
+ const topHeight = Math.max(
115
+ includeMinimap ? minimapHeight : 0,
116
+ rowPanelsHeaderHeight,
117
+ treeOverviewHeight + (treeScaleBar ? treeScaleBarHeight : 0),
118
+ )
119
+ const gutterWidth = treeAreaWidth + rowPanelsWidth
93
120
 
94
121
  // width stays content-only; the legend occupies an extra column added at the
95
122
  // svg root in MsaSvg. The viewport export takes the alignment canvas's own
@@ -98,23 +125,24 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
98
125
  // columns the scrollbars hide on screen
99
126
  return opts.exportType === 'entire'
100
127
  ? {
101
- width: totalWidth + treeAreaWidth,
128
+ width: totalWidth + gutterWidth,
102
129
  msaAreaWidth: totalWidth,
103
- height: totalHeight + trackHeight,
130
+ height: totalHeight + trackHeight + topHeight,
104
131
  contentHeight: totalHeight,
105
132
  trackHeight,
133
+ topHeight,
106
134
  offsetX: 0,
107
135
  offsetY: 0,
108
136
  includeMinimap,
109
137
  legendWidth,
110
138
  }
111
139
  : {
112
- width: msaCanvasWidth + treeAreaWidth,
140
+ width: msaCanvasWidth + gutterWidth,
113
141
  msaAreaWidth: msaCanvasWidth,
114
- height:
115
- msaAreaHeight + (includeMinimap ? minimapHeight : 0) + trackHeight,
142
+ height: msaAreaHeight + topHeight + trackHeight,
116
143
  contentHeight: msaAreaHeight,
117
144
  trackHeight,
145
+ topHeight,
118
146
  offsetX: -scrollX,
119
147
  offsetY: -scrollY,
120
148
  includeMinimap,
@@ -147,7 +175,7 @@ export async function renderToSvg(model: MsaViewModel, opts: ExportSvgOptions) {
147
175
  return svgSafeColors(
148
176
  markup.replaceAll(/<g data-layer="([^"]+)"><\/g>/g, (match, id: string) => {
149
177
  const ctx = layers.get(id)
150
- return ctx ? `<g>${ctx.getSvg().innerHTML}</g>` : match
178
+ return ctx ? `<g id="${id}">${ctx.getSvg().innerHTML}</g>` : match
151
179
  }),
152
180
  )
153
181
  }
@@ -165,15 +193,18 @@ function MsaSvg({
165
193
  layout: Layout
166
194
  layers: LayerMap
167
195
  }) {
168
- const { width, trackHeight, includeMinimap, legendWidth } = layout
169
- const { treeAreaWidth, minimapHeight, visibleDomainTypes } = model
196
+ const { width, trackHeight, includeMinimap, legendWidth, topHeight } = layout
197
+ const { treeAreaWidth, rowPanelsWidth } = model
170
198
  const totalWidth = width + legendWidth
171
- const legendTop = includeMinimap ? minimapHeight : 0
172
- // a short alignment with many domain types has a key taller than its rows,
199
+ const legendTop = topHeight
200
+ // a short alignment with many legend entries has a key taller than its rows,
173
201
  // and the page has to hold the whole key
174
202
  const height =
175
203
  legendWidth > 0
176
- ? Math.max(layout.height, legendTop + legendHeight(visibleDomainTypes))
204
+ ? Math.max(
205
+ layout.height,
206
+ legendTop + legendHeight(legendRows(model.legends)),
207
+ )
177
208
  : layout.height
178
209
  const props = { Context, model, theme, layout, layers }
179
210
 
@@ -204,18 +235,37 @@ function MsaSvg({
204
235
  height="100%"
205
236
  fill={theme.palette.background.default}
206
237
  />
207
- {includeMinimap ? (
238
+ {topHeight > 0 ? (
208
239
  <>
209
- <g transform={`translate(${treeAreaWidth} 0)`}>
210
- <MinimapSVG model={model} theme={theme} />
211
- </g>
212
- <g transform={`translate(0 ${minimapHeight})`}>{body}</g>
240
+ {includeMinimap ? (
241
+ <g
242
+ id="minimap-panel"
243
+ transform={`translate(${treeAreaWidth + rowPanelsWidth} 0)`}
244
+ >
245
+ <MinimapSVG model={model} theme={theme} />
246
+ </g>
247
+ ) : null}
248
+ <RowPanelHeadersSVG
249
+ model={model}
250
+ theme={theme}
251
+ bandHeight={topHeight}
252
+ />
253
+ {model.showTreeOverview ? (
254
+ <TreeOverviewSVG
255
+ model={model}
256
+ theme={theme}
257
+ Context={Context}
258
+ layers={layers}
259
+ />
260
+ ) : null}
261
+ <TreeScaleBarSVG model={model} theme={theme} />
262
+ <g transform={`translate(0 ${topHeight})`}>{body}</g>
213
263
  </>
214
264
  ) : (
215
265
  body
216
266
  )}
217
267
  {legendWidth > 0 ? (
218
- <g transform={`translate(${width} ${legendTop})`}>
268
+ <g id="legend-panel" transform={`translate(${width} ${legendTop})`}>
219
269
  <LegendSVG model={model} theme={theme} width={legendWidth} />
220
270
  </g>
221
271
  ) : null}
@@ -223,8 +273,185 @@ function MsaSvg({
223
273
  )
224
274
  }
225
275
 
226
- // the domain color key drawn as a reserved column to the right of the
227
- // alignment, mirroring the on-screen AnnotationLegend overlay
276
+ // The tree overview in the top band, over the tree column, where the live view
277
+ // puts it. It is part of the figure rather than chrome: the inset is what says
278
+ // where the focused clade sits in the whole tree.
279
+ function TreeOverviewSVG({
280
+ model,
281
+ theme,
282
+ Context,
283
+ layers,
284
+ }: {
285
+ model: MsaViewModel
286
+ theme: Theme
287
+ Context: typeof ContextType
288
+ layers: LayerMap
289
+ }) {
290
+ const { treeAreaWidth, overviewHeight, id } = model
291
+ const ctx = new Context(treeAreaWidth, overviewHeight)
292
+ renderTreeOverview({
293
+ model,
294
+ ctx,
295
+ theme,
296
+ width: treeAreaWidth,
297
+ height: overviewHeight,
298
+ })
299
+ return (
300
+ <ClipGroup
301
+ panelId="tree-overview"
302
+ clipId={`tree-overview-${id}`}
303
+ width={treeAreaWidth}
304
+ height={overviewHeight}
305
+ ctx={ctx}
306
+ layers={layers}
307
+ />
308
+ )
309
+ }
310
+
311
+ // The branch-length scale bar under the tree overview, where the live view puts
312
+ // it (see TreeRuler). A phylogram whose branch lengths are drawn to a scale
313
+ // nothing states is a figure a reader cannot measure, so the export carries the
314
+ // bar even though it is chrome on screen.
315
+ function TreeScaleBarSVG({
316
+ model,
317
+ theme,
318
+ }: {
319
+ model: MsaViewModel
320
+ theme: Theme
321
+ }) {
322
+ const { marginLeft, treeScaleBar: bar, treeOverviewHeight } = model
323
+ if (!bar) {
324
+ return null
325
+ }
326
+ const y = treeOverviewHeight + treeScaleBarHeight
327
+ const color = theme.palette.text.primary
328
+ return (
329
+ <g id="tree-scalebar">
330
+ <text x={marginLeft} y={y - 9} fontSize={10} fill={color}>
331
+ {bar.label}
332
+ </text>
333
+ <path
334
+ d={`M${marginLeft} ${y - 7} v6 h${bar.px} v-6`}
335
+ fill="none"
336
+ stroke={color}
337
+ />
338
+ </g>
339
+ )
340
+ }
341
+
342
+ // Each strip's header over the column it names, turned on its side, where the
343
+ // live view puts it (see RowPanelHeaders). The baseline runs up the figure, so
344
+ // the name sits to the left of it, the way TrackLabelsSVG offsets a horizontal
345
+ // baseline by a third of the font size.
346
+ function RowPanelHeadersSVG({
347
+ model,
348
+ theme,
349
+ bandHeight,
350
+ }: {
351
+ model: MsaViewModel
352
+ theme: Theme
353
+ bandHeight: number
354
+ }) {
355
+ const { resolvedRowPanels, treeAreaWidth, fontSize } = model
356
+ const y = bandHeight - 4
357
+ return resolvedRowPanels.length === 0 ? null : (
358
+ <g id="rowpanel-headers">
359
+ {resolvedRowPanels.map(panel => {
360
+ const across = headerRunsAcross(panel.width)
361
+ const size = across ? fontSize : Math.min(fontSize, panel.width)
362
+ const center = treeAreaWidth + panel.offsetX + panel.width / 2
363
+ const x = across ? center : center + size / 3
364
+ const room = across ? panel.width : y - LEGEND_PAD
365
+ const maxChars = Math.floor(room / (size * CHAR_WIDTH))
366
+ return size < 5 ? null : (
367
+ <text
368
+ key={panel.id}
369
+ x={x}
370
+ y={y}
371
+ {...(across
372
+ ? { textAnchor: 'middle' }
373
+ : { transform: `rotate(-90 ${x} ${y})` })}
374
+ fontSize={size}
375
+ fill={theme.palette.text.primary}
376
+ >
377
+ {panel.header.length > maxChars
378
+ ? `${panel.header.slice(0, Math.max(1, maxChars - 1))}…`
379
+ : panel.header}
380
+ </text>
381
+ )
382
+ })}
383
+ </g>
384
+ )
385
+ }
386
+
387
+ // A label past the room it has, cut to what fits with an ellipsis. The measure
388
+ // is the one cladeGutterWidth sizes the gutter with, so a label the gutter
389
+ // holds keeps every character.
390
+ function clipCladeLabel(label: string, room: number, fontSize: number) {
391
+ const width = measureTextCanvas(label, fontSize)
392
+ if (width <= room) {
393
+ return label
394
+ }
395
+ const fits = Math.floor((label.length * room) / width)
396
+ return `${label.slice(0, Math.max(1, fits - 1))}…`
397
+ }
398
+
399
+ // Each bracket's label in the gutter beside its bar, where the live view puts
400
+ // it (see CladeLabels). The canvas layer draws the bar; a label longer than the
401
+ // gutter or the clade is clipped, since text that overruns runs off the figure.
402
+ function CladeLabelsSVG({
403
+ model,
404
+ theme,
405
+ offsetY,
406
+ }: {
407
+ model: MsaViewModel
408
+ theme: Theme
409
+ offsetY: number
410
+ }) {
411
+ const {
412
+ resolvedClades,
413
+ cladeGutterWidth,
414
+ rowHeight,
415
+ treeAreaWidth,
416
+ fontSize,
417
+ } = model
418
+ if (cladeGutterWidth === 0) {
419
+ return null
420
+ }
421
+ const gutterLeft = treeAreaWidth - cladeGutterWidth
422
+ return (
423
+ <g id="clade-labels">
424
+ {resolvedClades.map((clade, index) => {
425
+ const { label, markColor, rows } = clade
426
+ if (!label || !cladeInGutter(clade)) {
427
+ return null
428
+ }
429
+ const left = gutterLeft + cladeLabelOffset(clade)
430
+ const top = rows[0] * rowHeight - offsetY
431
+ const height = cladeHeight(clade, rowHeight)
432
+ const horizontal = cladeLabelHorizontal(clade, rowHeight, fontSize)
433
+ const room = horizontal ? treeAreaWidth - left - bracketGap : height
434
+ const x = horizontal ? left : left + fontSize
435
+ const y = horizontal ? top + height / 2 + fontSize / 3 : top + height
436
+ return (
437
+ <text
438
+ key={`${rows[0]}-${rows[1]}-${index}`}
439
+ x={x}
440
+ y={y}
441
+ transform={horizontal ? undefined : `rotate(-90 ${x} ${y})`}
442
+ fontSize={fontSize}
443
+ fill={markColor ?? theme.palette.text.primary}
444
+ >
445
+ {clipCladeLabel(label, room, fontSize)}
446
+ </text>
447
+ )
448
+ })}
449
+ </g>
450
+ )
451
+ }
452
+
453
+ // the color keys drawn as a reserved column to the right of the alignment,
454
+ // mirroring the on-screen AnnotationLegend overlay
228
455
  function LegendSVG({
229
456
  model,
230
457
  theme,
@@ -234,8 +461,8 @@ function LegendSVG({
234
461
  theme: Theme
235
462
  width: number
236
463
  }) {
237
- const { visibleDomainTypes, fillPalette, strokePalette } = model
238
- const boxHeight = legendHeight(visibleDomainTypes)
464
+ const rows = legendRows(model.legends)
465
+ const boxHeight = legendHeight(rows)
239
466
  // the column is capped at LEGEND_MAX_W, so a name too long for it has to be
240
467
  // clipped here -- text that overruns the reserved width runs off the figure
241
468
  const maxChars = Math.floor(
@@ -252,27 +479,30 @@ function LegendSVG({
252
479
  stroke={theme.palette.divider}
253
480
  rx={2}
254
481
  />
255
- {visibleDomainTypes.map((d, i) => {
482
+ {rows.map((row, i) => {
256
483
  const y = LEGEND_PAD + i * LEGEND_ROW_H
257
484
  return (
258
- <g key={d.accession}>
259
- <rect
260
- x={LEGEND_PAD}
261
- y={y}
262
- width={LEGEND_SWATCH}
263
- height={LEGEND_SWATCH}
264
- fill={fillPalette[d.accession]}
265
- stroke={strokePalette[d.accession]}
266
- />
485
+ <g key={row.key}>
486
+ {row.color ? (
487
+ <rect
488
+ x={LEGEND_PAD}
489
+ y={y}
490
+ width={LEGEND_SWATCH}
491
+ height={LEGEND_SWATCH}
492
+ fill={row.color}
493
+ stroke={outlineColor(row.color)}
494
+ />
495
+ ) : null}
267
496
  <text
268
- x={LEGEND_TEXT_X}
497
+ x={row.color ? LEGEND_TEXT_X : LEGEND_PAD}
269
498
  y={y + LEGEND_SWATCH - 1}
270
499
  fontSize={LEGEND_FONT}
500
+ fontWeight={row.color ? undefined : 'bold'}
271
501
  fill={theme.palette.text.primary}
272
502
  >
273
- {d.name.length > maxChars
274
- ? `${d.name.slice(0, Math.max(1, maxChars - 1))}…`
275
- : d.name}
503
+ {row.label.length > maxChars
504
+ ? `${row.label.slice(0, Math.max(1, maxChars - 1))}…`
505
+ : row.label}
276
506
  </text>
277
507
  </g>
278
508
  )
@@ -293,7 +523,7 @@ interface LayerProps {
293
523
 
294
524
  function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
295
525
  const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout
296
- const { treeAreaWidth, id } = model
526
+ const { treeAreaWidth, rowPanelsWidth, id } = model
297
527
 
298
528
  const treeCtx = new Context(treeAreaWidth, contentHeight)
299
529
  renderTreeCanvas({
@@ -305,11 +535,26 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
305
535
  highResScaleFactorOverride: 1,
306
536
  })
307
537
 
538
+ const rowPanelsCtx = rowPanelsWidth
539
+ ? new Context(rowPanelsWidth, contentHeight)
540
+ : undefined
541
+ if (rowPanelsCtx) {
542
+ renderRowPanels({
543
+ model,
544
+ theme,
545
+ ctx: rowPanelsCtx,
546
+ offsetY,
547
+ blockSizeYOverride: contentHeight,
548
+ highResScaleFactorOverride: 1,
549
+ })
550
+ }
551
+
308
552
  const raster = rasterBackground({ model, theme, layout })
309
553
 
310
554
  const msaCtx = new Context(msaAreaWidth, contentHeight)
311
555
  renderBoxFeatureCanvasBlock({
312
556
  model,
557
+ theme,
313
558
  ctx: msaCtx,
314
559
  offsetX,
315
560
  offsetY,
@@ -344,17 +589,31 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
344
589
  return (
345
590
  <>
346
591
  <ClipGroup
592
+ panelId="tree-panel"
347
593
  clipId={`tree-${id}`}
348
594
  width={treeAreaWidth}
349
595
  height={contentHeight}
350
596
  ctx={treeCtx}
351
597
  layers={layers}
352
598
  />
599
+ <CladeLabelsSVG model={model} theme={theme} offsetY={offsetY} />
600
+ {rowPanelsCtx ? (
601
+ <ClipGroup
602
+ panelId="rowpanels-panel"
603
+ clipId={`rowpanels-${id}`}
604
+ width={rowPanelsWidth}
605
+ height={contentHeight}
606
+ transform={`translate(${treeAreaWidth} 0)`}
607
+ ctx={rowPanelsCtx}
608
+ layers={layers}
609
+ />
610
+ ) : null}
353
611
  <ClipGroup
612
+ panelId="msa-panel"
354
613
  clipId={`msa-${id}`}
355
614
  width={msaAreaWidth}
356
615
  height={contentHeight}
357
- transform={`translate(${treeAreaWidth} 0)`}
616
+ transform={`translate(${treeAreaWidth + rowPanelsWidth} 0)`}
358
617
  ctx={msaCtx}
359
618
  layers={layers}
360
619
  underlay={raster}
@@ -364,15 +623,13 @@ function CoreRendering({ model, theme, layout, Context, layers }: LayerProps) {
364
623
  }
365
624
 
366
625
  /**
367
- * The alignment background as a single <image>, on the same terms the live
368
- * canvas uses it: no domain overlay painting its own boxes, background coloring
369
- * on, and a canvas we can actually read back.
626
+ * The alignment background as a single <image>, under the live canvas's
627
+ * conditions: no domain overlay boxes, background coloring on, and a canvas
628
+ * that reads back.
370
629
  *
371
- * One node replaces the <rect>-per-cell the vector path emits, which is what
372
- * lets an export of a real alignment finish at all. The image is one pixel per
373
- * cell drawn across the cells' own rectangle, so it upscales by whole cells --
374
- * image-rendering keeps those edges hard rather than smearing them, making it
375
- * pixel-for-pixel what the rects drew.
630
+ * The vector path's <rect> per cell exhausts the heap on a real alignment. The
631
+ * image is one pixel per cell, and image-rendering keeps the upscaled cell
632
+ * edges hard, so it matches the rects pixel for pixel.
376
633
  */
377
634
  function rasterBackground({
378
635
  model,
@@ -423,7 +680,7 @@ function rasterBackground({
423
680
 
424
681
  function TrackRendering({ model, theme, layout, Context, layers }: LayerProps) {
425
682
  const { trackHeight, offsetX, msaAreaWidth } = layout
426
- const { treeAreaWidth, id } = model
683
+ const { treeAreaWidth, rowPanelsWidth, id } = model
427
684
 
428
685
  const ctx = new Context(msaAreaWidth, trackHeight)
429
686
  renderAllTracks({
@@ -438,8 +695,9 @@ function TrackRendering({ model, theme, layout, Context, layers }: LayerProps) {
438
695
  return (
439
696
  <>
440
697
  <TrackLabelsSVG model={model} theme={theme} />
441
- <g transform={`translate(${treeAreaWidth} 0)`}>
698
+ <g transform={`translate(${treeAreaWidth + rowPanelsWidth} 0)`}>
442
699
  <ClipGroup
700
+ panelId="tracks-panel"
443
701
  clipId={`tracks-${id}`}
444
702
  width={msaAreaWidth}
445
703
  height={trackHeight}
@@ -465,14 +723,17 @@ function TrackLabelsSVG({
465
723
  if (!drawLabels) {
466
724
  return null
467
725
  }
468
- let y = 0
726
+ const labels = turnedOnTracks.map((track, i) => {
727
+ const { id, name, height } = track.model
728
+ const size = Math.min(height, fontSize)
729
+ const top = turnedOnTracks
730
+ .slice(0, i)
731
+ .reduce((acc, t) => acc + t.model.height, 0)
732
+ return { id, name, size, baseline: top + height / 2 + size / 3 }
733
+ })
469
734
  return (
470
735
  <>
471
- {turnedOnTracks.map(track => {
472
- const { id, name, height } = track.model
473
- const size = Math.min(height, fontSize)
474
- const baseline = y + height / 2 + size / 3
475
- y += height
736
+ {labels.map(({ id, name, size, baseline }) => {
476
737
  return size < 5 ? null : (
477
738
  <text
478
739
  key={id}
@@ -493,7 +754,12 @@ function TrackLabelsSVG({
493
754
  // Clips a svgcanvas Context to its box and leaves a placeholder for renderToSvg
494
755
  // to splice the layer's own markup into. `underlay` draws beneath that markup,
495
756
  // inside the same clip, for the parts of a layer React emits directly.
757
+ //
758
+ // `panelId` names the spliced group in the output, the same name in every
759
+ // export, so a figure can be taken apart in Illustrator or svgutils. `clipId`
760
+ // carries the model id, which keeps two viewers on one page from colliding.
496
761
  function ClipGroup({
762
+ panelId,
497
763
  clipId,
498
764
  width,
499
765
  height,
@@ -502,6 +768,7 @@ function ClipGroup({
502
768
  layers,
503
769
  underlay,
504
770
  }: {
771
+ panelId: string
505
772
  clipId: string
506
773
  width: number
507
774
  height: number
@@ -510,7 +777,7 @@ function ClipGroup({
510
777
  layers: LayerMap
511
778
  underlay?: React.ReactNode
512
779
  }) {
513
- layers.set(clipId, ctx)
780
+ layers.set(panelId, ctx)
514
781
  return (
515
782
  <>
516
783
  <defs>
@@ -520,7 +787,7 @@ function ClipGroup({
520
787
  </defs>
521
788
  <g clipPath={`url(#${clipId})`} transform={transform}>
522
789
  {underlay}
523
- <g data-layer={clipId} />
790
+ <g data-layer={panelId} />
524
791
  </g>
525
792
  </>
526
793
  )
@@ -79,9 +79,8 @@ function countBlanksBefore(blanks: number[], globalCol: number) {
79
79
  * that is its own visible index; for a hidden one it is the index of the next
80
80
  * visible column, i.e. where the hidden column collapses to.
81
81
  *
82
- * That total answer is what spans want: resolving both ends of a span through
83
- * globalColToVisibleCol instead loses the whole span whenever either end lands
84
- * in a hidden column.
82
+ * Spans use this, since globalColToVisibleCol drops a span whenever either end
83
+ * lands in a hidden column.
85
84
  *
86
85
  * @param blanks - Sorted array of global column indices that are hidden
87
86
  * @param globalCol - The global column index in the full MSA
@@ -94,9 +93,8 @@ export function visibleColsBefore(blanks: number[], globalCol: number) {
94
93
  * The ungapped position a row holds at a global column, or undefined when that
95
94
  * column is a gap in the row or past its end.
96
95
  *
97
- * A binary search of the row's seqPos index (ascending), not a scan of the row:
98
- * this answers on every mouse move, and scanning a 30k-column row per event is
99
- * the whole frame.
96
+ * Binary-searches the row's ascending seqPos index; it runs on every mouse
97
+ * move, and scanning a 30k-column row per event takes the whole frame.
100
98
  *
101
99
  * @param index - The row's seqPos -> global column index (buildSeqPosIndex)
102
100
  * @param globalCol - The global column index in the full MSA