react-msaview 6.5.0 → 8.0.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 (414) hide show
  1. package/README.md +13 -11
  2. package/bundle/index.js +122 -112
  3. package/bundle/index.js.map +4 -4
  4. package/dist/colorSchemes.js +1 -2
  5. package/dist/colorSchemes.js.map +1 -1
  6. package/dist/columnStats.d.ts +28 -0
  7. package/dist/columnStats.js +34 -0
  8. package/dist/columnStats.js.map +1 -0
  9. package/dist/components/DragHandle.d.ts +3 -2
  10. package/dist/components/DragHandle.js +11 -5
  11. package/dist/components/DragHandle.js.map +1 -1
  12. package/dist/components/Loading.js +2 -2
  13. package/dist/components/Loading.js.map +1 -1
  14. package/dist/components/MSAView.js +17 -6
  15. package/dist/components/MSAView.js.map +1 -1
  16. package/dist/components/MSAViewer.d.ts +69 -4
  17. package/dist/components/MSAViewer.js +201 -8
  18. package/dist/components/MSAViewer.js.map +1 -1
  19. package/dist/components/PortalTooltip.d.ts +11 -0
  20. package/dist/components/PortalTooltip.js +29 -0
  21. package/dist/components/PortalTooltip.js.map +1 -0
  22. package/dist/components/ResizeHandles.d.ts +3 -3
  23. package/dist/components/ResizeHandles.js +13 -24
  24. package/dist/components/ResizeHandles.js.map +1 -1
  25. package/dist/components/SequenceTextArea.js +3 -3
  26. package/dist/components/SequenceTextArea.js.map +1 -1
  27. package/dist/components/Track.js +41 -22
  28. package/dist/components/Track.js.map +1 -1
  29. package/dist/components/dialogs/{InterProScanDialog.d.ts → AnnotationFileDialog.d.ts} +2 -2
  30. package/dist/components/dialogs/AnnotationFileDialog.js +92 -0
  31. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -0
  32. package/dist/components/dialogs/ExportSVGDialog.js +5 -3
  33. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  34. package/dist/components/dialogs/FeatureDialog.js +13 -9
  35. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  36. package/dist/components/dialogs/TrackInfoDialog.js +1 -1
  37. package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
  38. package/dist/components/header/GappynessSlider.js +6 -11
  39. package/dist/components/header/GappynessSlider.js.map +1 -1
  40. package/dist/components/header/Header.js +7 -0
  41. package/dist/components/header/Header.js.map +1 -1
  42. package/dist/components/header/HeaderStatusArea.js +0 -2
  43. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  44. package/dist/components/header/LoadWarnings.d.ts +6 -0
  45. package/dist/components/header/LoadWarnings.js +35 -0
  46. package/dist/components/header/LoadWarnings.js.map +1 -0
  47. package/dist/components/header/MultiAlignmentSelector.js +0 -0
  48. package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
  49. package/dist/components/header/ScrollZoomIcons.d.ts +6 -0
  50. package/dist/components/header/ScrollZoomIcons.js +35 -0
  51. package/dist/components/header/ScrollZoomIcons.js.map +1 -0
  52. package/dist/components/header/TreeSettingsMenu.js +7 -1
  53. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  54. package/dist/components/header/UnshareableDataWarning.d.ts +6 -0
  55. package/dist/components/header/UnshareableDataWarning.js +40 -0
  56. package/dist/components/header/UnshareableDataWarning.js.map +1 -0
  57. package/dist/components/header/ZoomControls.js +49 -6
  58. package/dist/components/header/ZoomControls.js.map +1 -1
  59. package/dist/components/header/getAnnotationsMenu.js +11 -14
  60. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  61. package/dist/components/header/settingsMenuItems.js +10 -7
  62. package/dist/components/header/settingsMenuItems.js.map +1 -1
  63. package/dist/components/import/ImportForm.js +1 -1
  64. package/dist/components/import/ImportForm.js.map +1 -1
  65. package/dist/components/import/ImportFormExamples.js +2 -6
  66. package/dist/components/import/ImportFormExamples.js.map +1 -1
  67. package/dist/components/import/util.js +2 -0
  68. package/dist/components/import/util.js.map +1 -1
  69. package/dist/components/minimap/Minimap.js +1 -2
  70. package/dist/components/minimap/Minimap.js.map +1 -1
  71. package/dist/components/msa/AnnotationLegend.js +27 -19
  72. package/dist/components/msa/AnnotationLegend.js.map +1 -1
  73. package/dist/components/msa/MSACanvas.js +1 -1
  74. package/dist/components/msa/MSACanvas.js.map +1 -1
  75. package/dist/components/msa/MSACanvasBlock.js +17 -20
  76. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  77. package/dist/components/msa/MSAMouseoverCanvas.js +2 -1
  78. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  79. package/dist/components/msa/domainBandCursor.d.ts +7 -11
  80. package/dist/components/msa/domainBandCursor.js +7 -11
  81. package/dist/components/msa/domainBandCursor.js.map +1 -1
  82. package/dist/components/msa/drawFeatureSpans.d.ts +53 -0
  83. package/dist/components/msa/drawFeatureSpans.js +78 -0
  84. package/dist/components/msa/drawFeatureSpans.js.map +1 -0
  85. package/dist/components/msa/legendRows.d.ts +8 -0
  86. package/dist/components/msa/legendRows.js +16 -0
  87. package/dist/components/msa/legendRows.js.map +1 -0
  88. package/dist/components/msa/msaRaster.d.ts +30 -27
  89. package/dist/components/msa/msaRaster.js +193 -90
  90. package/dist/components/msa/msaRaster.js.map +1 -1
  91. package/dist/components/msa/packDomainLanes.d.ts +20 -0
  92. package/dist/components/msa/packDomainLanes.js +33 -0
  93. package/dist/components/msa/packDomainLanes.js.map +1 -0
  94. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +3 -1
  95. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +51 -87
  96. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  97. package/dist/components/msa/renderHighlights.d.ts +44 -4
  98. package/dist/components/msa/renderHighlights.js +99 -11
  99. package/dist/components/msa/renderHighlights.js.map +1 -1
  100. package/dist/components/msa/renderMSABlock.d.ts +1 -2
  101. package/dist/components/msa/renderMSABlock.js +28 -31
  102. package/dist/components/msa/renderMSABlock.js.map +1 -1
  103. package/dist/components/msa/renderMSAMouseover.js +9 -30
  104. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  105. package/dist/components/msa/tileColor.js +4 -7
  106. package/dist/components/msa/tileColor.js.map +1 -1
  107. package/dist/components/msa/useMsaBlockMouse.d.ts +5 -3
  108. package/dist/components/msa/useMsaBlockMouse.js +6 -6
  109. package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
  110. package/dist/components/renderCtx.d.ts +1 -1
  111. package/dist/components/rowpanels/RowPanelBlock.d.ts +9 -0
  112. package/dist/components/rowpanels/RowPanelBlock.js +86 -0
  113. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -0
  114. package/dist/components/rowpanels/RowPanelHeaders.d.ts +12 -0
  115. package/dist/components/rowpanels/RowPanelHeaders.js +38 -0
  116. package/dist/components/rowpanels/RowPanelHeaders.js.map +1 -0
  117. package/dist/components/{msa/ColumnStats.d.ts → rowpanels/RowPanels.d.ts} +2 -2
  118. package/dist/components/rowpanels/RowPanels.js +32 -0
  119. package/dist/components/rowpanels/RowPanels.js.map +1 -0
  120. package/dist/components/rowpanels/headerLayout.d.ts +1 -0
  121. package/dist/components/rowpanels/headerLayout.js +5 -0
  122. package/dist/components/rowpanels/headerLayout.js.map +1 -0
  123. package/dist/components/rowpanels/renderFeaturePanel.d.ts +21 -0
  124. package/dist/components/rowpanels/renderFeaturePanel.js +37 -0
  125. package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -0
  126. package/dist/components/rowpanels/renderRowPanel.d.ts +23 -0
  127. package/dist/components/rowpanels/renderRowPanel.js +22 -0
  128. package/dist/components/rowpanels/renderRowPanel.js.map +1 -0
  129. package/dist/components/rowpanels/renderStrip.d.ts +20 -0
  130. package/dist/components/rowpanels/renderStrip.js +26 -0
  131. package/dist/components/rowpanels/renderStrip.js.map +1 -0
  132. package/dist/components/tracks/TrackBlocks.js +13 -16
  133. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  134. package/dist/components/tracks/TrackTooltipContent.d.ts +15 -0
  135. package/dist/components/tracks/TrackTooltipContent.js +109 -0
  136. package/dist/components/tracks/TrackTooltipContent.js.map +1 -0
  137. package/dist/components/tracks/drawTracks.d.ts +30 -12
  138. package/dist/components/tracks/drawTracks.js +127 -34
  139. package/dist/components/tracks/drawTracks.js.map +1 -1
  140. package/dist/components/tracks/useTrackHover.d.ts +22 -0
  141. package/dist/components/tracks/useTrackHover.js +30 -0
  142. package/dist/components/tracks/useTrackHover.js.map +1 -0
  143. package/dist/components/tree/CladeLabels.d.ts +12 -0
  144. package/dist/components/tree/CladeLabels.js +48 -0
  145. package/dist/components/tree/CladeLabels.js.map +1 -0
  146. package/dist/components/tree/TreeCanvas.js +6 -4
  147. package/dist/components/tree/TreeCanvas.js.map +1 -1
  148. package/dist/components/tree/TreeCanvasBlock.js +6 -21
  149. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  150. package/dist/components/tree/TreeOverview.d.ts +14 -0
  151. package/dist/components/tree/TreeOverview.js +75 -0
  152. package/dist/components/tree/TreeOverview.js.map +1 -0
  153. package/dist/components/tree/TreeRuler.js +11 -5
  154. package/dist/components/tree/TreeRuler.js.map +1 -1
  155. package/dist/components/tree/cladeBrackets.d.ts +25 -0
  156. package/dist/components/tree/cladeBrackets.js +51 -0
  157. package/dist/components/tree/cladeBrackets.js.map +1 -0
  158. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +2 -2
  159. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  160. package/dist/components/tree/renderTreeCanvas.d.ts +0 -1
  161. package/dist/components/tree/renderTreeCanvas.js +221 -47
  162. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  163. package/dist/components/tree/renderTreeOverview.d.ts +52 -0
  164. package/dist/components/tree/renderTreeOverview.js +138 -0
  165. package/dist/components/tree/renderTreeOverview.js.map +1 -0
  166. package/dist/components/tree/scaleBar.d.ts +10 -0
  167. package/dist/components/tree/scaleBar.js +24 -0
  168. package/dist/components/tree/scaleBar.js.map +1 -0
  169. package/dist/components/tree/useTreeHover.js +2 -4
  170. package/dist/components/tree/useTreeHover.js.map +1 -1
  171. package/dist/constants.d.ts +18 -1
  172. package/dist/constants.js +51 -8
  173. package/dist/constants.js.map +1 -1
  174. package/dist/createPaletteMap.d.ts +7 -6
  175. package/dist/createPaletteMap.js +10 -9
  176. package/dist/createPaletteMap.js.map +1 -1
  177. package/dist/element.d.ts +13 -0
  178. package/dist/element.js +269 -0
  179. package/dist/element.js.map +1 -0
  180. package/dist/featureFields.d.ts +11 -0
  181. package/dist/featureFields.js +17 -0
  182. package/dist/featureFields.js.map +1 -0
  183. package/dist/fetchUtils.d.ts +7 -7
  184. package/dist/fetchUtils.js +4 -32
  185. package/dist/fetchUtils.js.map +1 -1
  186. package/dist/flatToTree.js +1 -2
  187. package/dist/flatToTree.js.map +1 -1
  188. package/dist/ggplotPalettes.d.ts +10 -0
  189. package/dist/ggplotPalettes.js +58 -0
  190. package/dist/ggplotPalettes.js.map +1 -1
  191. package/dist/hierarchy.d.ts +29 -1
  192. package/dist/hierarchy.js +82 -19
  193. package/dist/hierarchy.js.map +1 -1
  194. package/dist/index.d.ts +10 -1
  195. package/dist/index.js +10 -2
  196. package/dist/index.js.map +1 -1
  197. package/dist/model/DataModel.js +4 -4
  198. package/dist/model/DataModel.js.map +1 -1
  199. package/dist/model/msaModel.d.ts +3 -2
  200. package/dist/model/msaModel.js +3 -2
  201. package/dist/model/msaModel.js.map +1 -1
  202. package/dist/model/treeModel.d.ts +30 -0
  203. package/dist/model/treeModel.js +37 -1
  204. package/dist/model/treeModel.js.map +1 -1
  205. package/dist/model.d.ts +792 -259
  206. package/dist/model.js +2092 -675
  207. package/dist/model.js.map +1 -1
  208. package/dist/mount.d.ts +11 -0
  209. package/dist/mount.js +22 -0
  210. package/dist/mount.js.map +1 -0
  211. package/dist/neighborJoining.js +8 -32
  212. package/dist/neighborJoining.js.map +1 -1
  213. package/dist/renderToStaticMarkup.d.ts +15 -13
  214. package/dist/renderToStaticMarkup.js +37 -25
  215. package/dist/renderToStaticMarkup.js.map +1 -1
  216. package/dist/renderToSvg.js +198 -68
  217. package/dist/renderToSvg.js.map +1 -1
  218. package/dist/rowCoordinateCalculations.d.ts +26 -28
  219. package/dist/rowCoordinateCalculations.js +13 -39
  220. package/dist/rowCoordinateCalculations.js.map +1 -1
  221. package/dist/scales.d.ts +22 -0
  222. package/dist/scales.js +30 -0
  223. package/dist/scales.js.map +1 -0
  224. package/dist/seqPosToGlobalCol.d.ts +2 -3
  225. package/dist/seqPosToGlobalCol.js +5 -4
  226. package/dist/seqPosToGlobalCol.js.map +1 -1
  227. package/dist/sequenceLogo.d.ts +6 -11
  228. package/dist/sequenceLogo.js +6 -11
  229. package/dist/sequenceLogo.js.map +1 -1
  230. package/dist/stripDefault.d.ts +7 -14
  231. package/dist/stripDefault.js +7 -14
  232. package/dist/stripDefault.js.map +1 -1
  233. package/dist/svgTestUtil.d.ts +306 -124
  234. package/dist/types.d.ts +295 -3
  235. package/dist/umd.d.ts +2 -0
  236. package/dist/umd.js +4 -2
  237. package/dist/umd.js.map +1 -1
  238. package/dist/useCanvasAutorun.d.ts +5 -10
  239. package/dist/useCanvasAutorun.js +5 -10
  240. package/dist/useCanvasAutorun.js.map +1 -1
  241. package/dist/useMsaSvgFigure.d.ts +20 -0
  242. package/dist/useMsaSvgFigure.js +46 -0
  243. package/dist/useMsaSvgFigure.js.map +1 -0
  244. package/dist/util.d.ts +10 -4
  245. package/dist/util.js +37 -10
  246. package/dist/util.js.map +1 -1
  247. package/dist/version.d.ts +1 -1
  248. package/dist/version.js +1 -1
  249. package/dist/wuss.d.ts +14 -0
  250. package/dist/wuss.js +55 -0
  251. package/dist/wuss.js.map +1 -0
  252. package/package.json +23 -14
  253. package/src/colorSchemes.ts +1 -2
  254. package/src/columnStats.ts +58 -0
  255. package/src/components/DragHandle.tsx +18 -7
  256. package/src/components/Loading.tsx +14 -8
  257. package/src/components/MSAView.tsx +19 -5
  258. package/src/components/MSAViewer.tsx +326 -10
  259. package/src/components/PortalTooltip.tsx +47 -0
  260. package/src/components/ResizeHandles.tsx +21 -36
  261. package/src/components/SequenceTextArea.tsx +3 -3
  262. package/src/components/Track.tsx +55 -18
  263. package/src/components/dialogs/AnnotationFileDialog.tsx +162 -0
  264. package/src/components/dialogs/ExportSVGDialog.tsx +7 -2
  265. package/src/components/dialogs/FeatureDialog.tsx +13 -9
  266. package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
  267. package/src/components/header/GappynessSlider.tsx +6 -11
  268. package/src/components/header/Header.tsx +10 -0
  269. package/src/components/header/HeaderStatusArea.tsx +0 -5
  270. package/src/components/header/LoadWarnings.tsx +48 -0
  271. package/src/components/header/MultiAlignmentSelector.tsx +0 -0
  272. package/src/components/header/ScrollZoomIcons.tsx +52 -0
  273. package/src/components/header/TreeSettingsMenu.tsx +7 -1
  274. package/src/components/header/UnshareableDataWarning.tsx +56 -0
  275. package/src/components/header/ZoomControls.tsx +74 -14
  276. package/src/components/header/getAnnotationsMenu.ts +13 -17
  277. package/src/components/header/settingsMenuItems.ts +11 -6
  278. package/src/components/import/ImportForm.tsx +2 -2
  279. package/src/components/import/ImportFormExamples.tsx +4 -8
  280. package/src/components/import/util.ts +2 -0
  281. package/src/components/minimap/Minimap.tsx +1 -2
  282. package/src/components/msa/AnnotationLegend.tsx +48 -38
  283. package/src/components/msa/MSACanvas.tsx +3 -5
  284. package/src/components/msa/MSACanvasBlock.tsx +17 -21
  285. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -1
  286. package/src/components/msa/domainBandCursor.ts +7 -11
  287. package/src/components/msa/drawFeatureSpans.ts +150 -0
  288. package/src/components/msa/legendRows.ts +24 -0
  289. package/src/components/msa/msaRaster.ts +236 -88
  290. package/src/components/msa/packDomainLanes.ts +36 -0
  291. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +69 -123
  292. package/src/components/msa/renderHighlights.ts +141 -10
  293. package/src/components/msa/renderMSABlock.ts +29 -38
  294. package/src/components/msa/renderMSAMouseover.ts +9 -39
  295. package/src/components/msa/tileColor.ts +4 -7
  296. package/src/components/msa/useMsaBlockMouse.ts +6 -6
  297. package/src/components/renderCtx.ts +1 -0
  298. package/src/components/rowpanels/RowPanelBlock.tsx +139 -0
  299. package/src/components/rowpanels/RowPanelHeaders.tsx +57 -0
  300. package/src/components/rowpanels/RowPanels.tsx +77 -0
  301. package/src/components/rowpanels/headerLayout.ts +5 -0
  302. package/src/components/rowpanels/renderFeaturePanel.ts +66 -0
  303. package/src/components/rowpanels/renderRowPanel.ts +41 -0
  304. package/src/components/rowpanels/renderStrip.ts +46 -0
  305. package/src/components/tracks/TrackBlocks.tsx +13 -18
  306. package/src/components/tracks/TrackTooltipContent.tsx +156 -0
  307. package/src/components/tracks/drawTracks.ts +169 -38
  308. package/src/components/tracks/useTrackHover.ts +42 -0
  309. package/src/components/tree/CladeLabels.tsx +77 -0
  310. package/src/components/tree/TreeCanvas.tsx +4 -3
  311. package/src/components/tree/TreeCanvasBlock.tsx +10 -32
  312. package/src/components/tree/TreeOverview.tsx +106 -0
  313. package/src/components/tree/TreeRuler.tsx +29 -5
  314. package/src/components/tree/cladeBrackets.ts +73 -0
  315. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
  316. package/src/components/tree/renderTreeCanvas.ts +312 -48
  317. package/src/components/tree/renderTreeOverview.ts +212 -0
  318. package/src/components/tree/scaleBar.ts +23 -0
  319. package/src/components/tree/useTreeHover.ts +2 -4
  320. package/src/constants.ts +61 -8
  321. package/src/createPaletteMap.ts +10 -9
  322. package/src/element.ts +321 -0
  323. package/src/featureFields.ts +19 -0
  324. package/src/fetchUtils.ts +7 -40
  325. package/src/flatToTree.ts +1 -2
  326. package/src/ggplotPalettes.ts +60 -0
  327. package/src/hierarchy.ts +97 -20
  328. package/src/index.ts +46 -6
  329. package/src/model/DataModel.ts +5 -4
  330. package/src/model/msaModel.ts +3 -2
  331. package/src/model/treeModel.ts +42 -0
  332. package/src/model.ts +2492 -793
  333. package/src/mount.tsx +32 -0
  334. package/src/neighborJoining.ts +8 -32
  335. package/src/rawImports.d.ts +9 -0
  336. package/src/renderToStaticMarkup.ts +41 -25
  337. package/src/renderToSvg.tsx +365 -92
  338. package/src/rowCoordinateCalculations.ts +16 -62
  339. package/src/scales.ts +47 -0
  340. package/src/seqPosToGlobalCol.ts +5 -4
  341. package/src/sequenceLogo.ts +6 -11
  342. package/src/stripDefault.ts +7 -14
  343. package/src/types.ts +352 -12
  344. package/src/umd.ts +4 -2
  345. package/src/useCanvasAutorun.ts +5 -10
  346. package/src/useMsaSvgFigure.ts +72 -0
  347. package/src/util.ts +40 -14
  348. package/src/version.ts +1 -1
  349. package/src/wuss.ts +62 -0
  350. package/dist/components/dialogs/InterProScanDialog.js +0 -137
  351. package/dist/components/dialogs/InterProScanDialog.js.map +0 -1
  352. package/dist/components/dialogs/InterProScanFileDialog.d.ts +0 -7
  353. package/dist/components/dialogs/InterProScanFileDialog.js +0 -64
  354. package/dist/components/dialogs/InterProScanFileDialog.js.map +0 -1
  355. package/dist/components/msa/ColumnStats.js +0 -42
  356. package/dist/components/msa/ColumnStats.js.map +0 -1
  357. package/dist/launchInterProScan.d.ts +0 -13
  358. package/dist/launchInterProScan.js +0 -110
  359. package/dist/launchInterProScan.js.map +0 -1
  360. package/dist/useColorContrast.d.ts +0 -10
  361. package/dist/useColorContrast.js +0 -13
  362. package/dist/useColorContrast.js.map +0 -1
  363. package/src/clustalX.test.ts +0 -74
  364. package/src/colSpace.test.ts +0 -67
  365. package/src/collapseLogic.test.ts +0 -115
  366. package/src/colorSchemes.docs.test.ts +0 -57
  367. package/src/columnCounts.test.ts +0 -73
  368. package/src/columnStats.test.ts +0 -94
  369. package/src/columnTracks.test.ts +0 -97
  370. package/src/components/dialogs/InterProScanDialog.tsx +0 -214
  371. package/src/components/dialogs/InterProScanFileDialog.tsx +0 -138
  372. package/src/components/minimap/minimapLayout.test.ts +0 -71
  373. package/src/components/msa/ColumnStats.tsx +0 -54
  374. package/src/components/msa/msaRaster.test.ts +0 -145
  375. package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +0 -51
  376. package/src/components/tree/renderTreeCanvas.test.ts +0 -296
  377. package/src/dataInitialized.test.ts +0 -42
  378. package/src/domainBands.test.ts +0 -122
  379. package/src/domainLetterColor.test.ts +0 -80
  380. package/src/exportFileName.test.ts +0 -40
  381. package/src/fetchUtils.test.ts +0 -83
  382. package/src/flatToTree.test.ts +0 -51
  383. package/src/hierarchy.test.ts +0 -228
  384. package/src/highlights.test.ts +0 -70
  385. package/src/impgRender.test.tsx +0 -68
  386. package/src/largeTree.test.ts +0 -67
  387. package/src/launchInterProScan.ts +0 -167
  388. package/src/loadErrorDisplay.test.tsx +0 -102
  389. package/src/modelFilehandleLoaders.test.ts +0 -137
  390. package/src/modelReset.test.ts +0 -120
  391. package/src/neighborJoining.test.ts +0 -164
  392. package/src/parseAsn1.test.ts +0 -36
  393. package/src/propertyConservation.test.ts +0 -36
  394. package/src/renderColumnTracksSvg.test.tsx +0 -108
  395. package/src/renderDomainsSvg.test.tsx +0 -118
  396. package/src/renderHighlightsSvg.test.tsx +0 -85
  397. package/src/renderMinimapSvg.test.tsx +0 -62
  398. package/src/renderRasterSvg.test.tsx +0 -144
  399. package/src/renderSequenceLogoSvg.test.tsx +0 -165
  400. package/src/renderToSvg.test.tsx +0 -65
  401. package/src/reset.test.ts +0 -61
  402. package/src/rowCoordinateCalculations.test.ts +0 -211
  403. package/src/rowSpace.test.ts +0 -46
  404. package/src/segmentOverlay.test.ts +0 -70
  405. package/src/seqPosToGlobalCol.test.ts +0 -59
  406. package/src/sequenceLogo.test.ts +0 -88
  407. package/src/setFontSize.test.ts +0 -33
  408. package/src/stripDefault.test.ts +0 -48
  409. package/src/stripDefaultSnapshot.test.ts +0 -87
  410. package/src/treeMetadata.test.ts +0 -39
  411. package/src/useCanvasAutorun.test.tsx +0 -98
  412. package/src/useColorContrast.ts +0 -18
  413. package/src/viewChrome.test.tsx +0 -134
  414. package/src/wheelZoom.test.ts +0 -46
@@ -0,0 +1,150 @@
1
+ import type { HierarchyNode } from '../../hierarchy.ts'
2
+ import type { Annotation, NodeWithIdsAndLength } from '../../types.ts'
3
+ import type { RenderCtx } from '../renderCtx.ts'
4
+
5
+ /** one drawn span: a feature, the lane it sits in, and its row's depth */
6
+ export interface SpanBand {
7
+ annotation: Annotation
8
+ lane: number
9
+ laneCount: number
10
+ }
11
+
12
+ /** one row's spans, at the row baseline `y` the tree layout gives it */
13
+ export interface SpanRow<T extends SpanBand> {
14
+ y: number
15
+ bands: T[]
16
+ }
17
+
18
+ /**
19
+ * Where a band lands inside its row. The alignment overlay fills the row, bars
20
+ * it along the bottom, or stacks thin sub-rows; a `features` row panel divides
21
+ * the row between its lanes.
22
+ */
23
+ export interface SpanLayout {
24
+ /** the band height in a row needing `laneCount` lanes */
25
+ height: (laneCount: number) => number
26
+ /** the band's top edge, from its row baseline, its lane and its height */
27
+ top: (y: number, lane: number, height: number) => number
28
+ /** how far a strand head reaches past the body */
29
+ headLength: (height: number) => number
30
+ }
31
+
32
+ /** the rows of `bandsByRow` that `leaves` covers, in display order */
33
+ export function spanRows<T extends SpanBand>(
34
+ leaves: HierarchyNode<NodeWithIdsAndLength>[],
35
+ bandsByRow: Map<string, T[]>,
36
+ ): SpanRow<T>[] {
37
+ const rows: SpanRow<T>[] = []
38
+ for (const leaf of leaves) {
39
+ const bands = bandsByRow.get(leaf.data.name)
40
+ if (bands && bands.length > 0) {
41
+ rows.push({ y: leaf.x!, bands })
42
+ }
43
+ }
44
+ return rows
45
+ }
46
+
47
+ /**
48
+ * The span mark, in whatever x space the caller maps its bands into: alignment
49
+ * columns for the domain overlay, panel pixels for a `features` row panel. A
50
+ * feature with a strand draws as an arrow, one without as a box, and a label
51
+ * draws inside the span wherever the text fits.
52
+ *
53
+ * `xOf` returns the span's `[xStart, xEnd]` in the context's coordinates, and
54
+ * the context is translated so that `y - rowHeight` is the row's top edge.
55
+ */
56
+ export function drawFeatureSpans<T extends SpanBand>({
57
+ ctx,
58
+ rows,
59
+ layout,
60
+ xOf,
61
+ colors,
62
+ labelOf,
63
+ labelDrawn,
64
+ contrastText,
65
+ xMin,
66
+ xMax,
67
+ }: {
68
+ ctx: RenderCtx
69
+ rows: SpanRow<T>[]
70
+ layout: SpanLayout
71
+ xOf: (band: T) => [number, number]
72
+ colors: Map<Annotation, { fill: string; stroke: string }>
73
+ labelOf?: (band: T) => string | undefined
74
+ labelDrawn?: (band: T) => void
75
+ contrastText: (color: string) => string
76
+ xMin: number
77
+ xMax: number
78
+ }) {
79
+ for (const { y, bands } of rows) {
80
+ const h = layout.height(bands[0]!.laneCount)
81
+ const headLen = layout.headLength(h)
82
+ for (const band of bands) {
83
+ const [xStart, xEnd] = xOf(band)
84
+ const w = xEnd - xStart
85
+ if (xEnd < xMin || xStart > xMax) {
86
+ continue
87
+ }
88
+ const t = layout.top(y, band.lane, h)
89
+ const { fill, stroke } = colors.get(band.annotation)!
90
+ ctx.fillStyle = fill
91
+ ctx.strokeStyle = stroke
92
+ const { strand } = band.annotation
93
+ if (strand === undefined) {
94
+ ctx.fillRect(xStart, t, w, h)
95
+ ctx.strokeRect(xStart, t, w, h)
96
+ } else {
97
+ drawGeneArrow({ ctx, x: xStart, t, w, h, headLen, strand })
98
+ }
99
+ const label = labelOf?.(band)
100
+ if (label !== undefined) {
101
+ const fontSize = Math.min(h - 2, 11)
102
+ ctx.font = `${fontSize}px sans-serif`
103
+ if (ctx.measureText(label).width + 2 <= w) {
104
+ labelDrawn?.(band)
105
+ ctx.fillStyle = contrastText(fill)
106
+ ctx.textAlign = 'center'
107
+ ctx.textBaseline = 'middle'
108
+ ctx.fillText(label, xStart + w / 2, t + h / 2)
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ // A gene arrow: a full-width rectangular body spanning the feature's
116
+ // start..end columns, plus a triangular head that points *beyond* that end in
117
+ // the strand direction (right for +, left for -). Keeping the body aligned to
118
+ // the exact start/end columns means + and - strand features read as having the
119
+ // same boundaries; the arrow lives outside them purely to show transcription
120
+ // direction, preserving the column-by-column homology down the rows.
121
+ function drawGeneArrow({
122
+ ctx,
123
+ x,
124
+ t,
125
+ w,
126
+ h,
127
+ headLen,
128
+ strand,
129
+ }: {
130
+ ctx: RenderCtx
131
+ x: number
132
+ t: number
133
+ w: number
134
+ h: number
135
+ headLen: number
136
+ strand: number
137
+ }) {
138
+ const dir = strand > 0 ? 1 : -1
139
+ const bodyStart = strand > 0 ? x : x + w
140
+ const bodyEnd = strand > 0 ? x + w : x
141
+ ctx.beginPath()
142
+ ctx.moveTo(bodyStart, t)
143
+ ctx.lineTo(bodyEnd, t)
144
+ ctx.lineTo(bodyEnd + dir * headLen, t + h / 2)
145
+ ctx.lineTo(bodyEnd, t + h)
146
+ ctx.lineTo(bodyStart, t + h)
147
+ ctx.closePath()
148
+ ctx.fill()
149
+ ctx.stroke()
150
+ }
@@ -0,0 +1,24 @@
1
+ import type { Legend } from '../../types.ts'
2
+
3
+ export interface LegendRow {
4
+ key: string
5
+ label: string
6
+ color?: string
7
+ hint?: string
8
+ }
9
+
10
+ // the legends flattened into the rows both the overlay and the export stack
11
+ // top to bottom. A title row separates one legend from the next, and a lone
12
+ // legend needs no title.
13
+ export function legendRows(legends: Legend[]): LegendRow[] {
14
+ const titled = legends.length > 1
15
+ return legends.flatMap(legend => [
16
+ ...(titled ? [{ key: legend.id, label: legend.title }] : []),
17
+ ...legend.entries.map(entry => ({
18
+ key: `${legend.id}:${entry.id}`,
19
+ label: entry.label,
20
+ color: entry.color,
21
+ hint: entry.id,
22
+ })),
23
+ ])
24
+ }
@@ -36,9 +36,8 @@ const colorCache = new Map<string, number>()
36
36
 
37
37
  /**
38
38
  * CSS color string -> the packed pixel an ImageData holds for it. Painting a 1x1
39
- * canvas and reading it back handles every form a color scheme can produce --
40
- * hex, rgb(), hsl(), named -- without a parser of our own, and the cache means
41
- * each distinct color is resolved once per session rather than once per cell.
39
+ * canvas and reading it back parses hex, rgb(), hsl() and named colors; the
40
+ * cache resolves each distinct color once per session.
42
41
  */
43
42
  export function cssColorToPixel(css: string) {
44
43
  const hit = colorCache.get(css)
@@ -63,14 +62,29 @@ export function cssColorToPixel(css: string) {
63
62
  return packed
64
63
  }
65
64
 
65
+ function cellPixelFn(spec: RasterSpec, toPixel: (css: string) => number) {
66
+ const { columns, relativeTo, colorAt, bg, hover } = spec
67
+ const bgPixel = toPixel(bg)
68
+ const hoverPixel = toPixel(hover)
69
+ const reference = relativeTo ? columns.get(relativeTo) : undefined
70
+ return (name: string, col: number, letter: string) => {
71
+ if (reference && name !== relativeTo && letter === reference[col]) {
72
+ return hoverPixel
73
+ }
74
+ const color = colorAt(col, letter)
75
+ return color === undefined ? bgPixel : toPixel(color)
76
+ }
77
+ }
78
+
66
79
  /**
67
80
  * Packs a rectangle of the alignment into one pixel per cell, in `rowNames`
68
81
  * order, ready to hand to putImageData. Cells past the end of a row, and rows
69
- * with no sequence at all, stay transparent -- what the per-cell tile painter
70
- * also leaves behind.
82
+ * with no sequence, stay transparent, as in the per-cell painter.
71
83
  *
72
- * `colStep`/`rowStep` sample instead of covering, which is how the minimap fits
73
- * a whole alignment into a 12px bar.
84
+ * `colStep`/`rowStep` sample every nth cell, which fits a whole alignment into
85
+ * the minimap's 12px bar. `colSpan`/`rowSpan` average each block of that many
86
+ * cells into one pixel, so a zoom below a device pixel per column still shows
87
+ * every column's color without blending neighbouring rows.
74
88
  */
75
89
  export function rasterPixels({
76
90
  spec,
@@ -80,7 +94,10 @@ export function rasterPixels({
80
94
  height,
81
95
  colStep = 1,
82
96
  rowStep = 1,
97
+ colSpan = 1,
98
+ rowSpan = 1,
83
99
  toPixel = cssColorToPixel,
100
+ out = new Uint32Array(width * height),
84
101
  }: {
85
102
  spec: RasterSpec
86
103
  col0: number
@@ -89,13 +106,27 @@ export function rasterPixels({
89
106
  height: number
90
107
  colStep?: number
91
108
  rowStep?: number
109
+ colSpan?: number
110
+ rowSpan?: number
92
111
  toPixel?: (css: string) => number
112
+ out?: Uint32Array
93
113
  }) {
94
- const { rowNames, columns, relativeTo, colorAt, bg, hover } = spec
95
- const out = new Uint32Array(width * height)
96
- const bgPixel = toPixel(bg)
97
- const hoverPixel = toPixel(hover)
98
- const reference = relativeTo ? columns.get(relativeTo) : undefined
114
+ if (colSpan > 1 || rowSpan > 1) {
115
+ averagePixels({
116
+ spec,
117
+ col0,
118
+ row0,
119
+ width,
120
+ height,
121
+ colSpan,
122
+ rowSpan,
123
+ toPixel,
124
+ out,
125
+ })
126
+ return out
127
+ }
128
+ const { rowNames, columns } = spec
129
+ const cellPixel = cellPixelFn(spec, toPixel)
99
130
 
100
131
  for (let r = 0; r < height; r++) {
101
132
  const name = rowNames[row0 + r * rowStep]
@@ -106,7 +137,6 @@ export function rasterPixels({
106
137
  if (!str) {
107
138
  continue
108
139
  }
109
- const isReference = name === relativeTo
110
140
  const base = r * width
111
141
  for (let c = 0; c < width; c++) {
112
142
  const col = col0 + c * colStep
@@ -114,21 +144,92 @@ export function rasterPixels({
114
144
  if (letter === undefined) {
115
145
  break
116
146
  }
117
- if (reference && !isReference && letter === reference[col]) {
118
- out[base + c] = hoverPixel
119
- } else {
120
- const color = colorAt(col, letter)
121
- out[base + c] = color === undefined ? bgPixel : toPixel(color)
122
- }
147
+ out[base + c] = cellPixel(name, col, letter)
123
148
  }
124
149
  }
125
150
  return out
126
151
  }
127
152
 
128
- // Getting a context object back is not the same as getting one that can do
129
- // this: the headless render shim hands back a context carrying only measureText,
130
- // which a truthiness test accepts and putImageData then does not survive. Name
131
- // the methods the raster actually calls, so every caller degrades together.
153
+ function averagePixels({
154
+ spec,
155
+ col0,
156
+ row0,
157
+ width,
158
+ height,
159
+ colSpan,
160
+ rowSpan,
161
+ toPixel,
162
+ out,
163
+ }: {
164
+ spec: RasterSpec
165
+ col0: number
166
+ row0: number
167
+ width: number
168
+ height: number
169
+ colSpan: number
170
+ rowSpan: number
171
+ toPixel: (css: string) => number
172
+ out: Uint32Array
173
+ }) {
174
+ const { rowNames, columns } = spec
175
+ const cellPixel = cellPixelFn(spec, toPixel)
176
+ const cells = colSpan * rowSpan
177
+ const rShift = littleEndian ? 0 : 24
178
+ const gShift = littleEndian ? 8 : 16
179
+ const bShift = littleEndian ? 16 : 8
180
+ const aShift = littleEndian ? 24 : 0
181
+ const names: string[] = []
182
+ const strs: string[] = []
183
+
184
+ for (let r = 0; r < height; r++) {
185
+ names.length = 0
186
+ strs.length = 0
187
+ for (let dr = 0; dr < rowSpan; dr++) {
188
+ const name = rowNames[row0 + r * rowSpan + dr]
189
+ const str = name === undefined ? undefined : columns.get(name)
190
+ if (name !== undefined && str) {
191
+ names.push(name)
192
+ strs.push(str)
193
+ }
194
+ }
195
+ const base = r * width
196
+ for (let c = 0; c < width; c++) {
197
+ // premultiplied, so a cell past the end of a row dilutes the alpha of its
198
+ // pixel rather than darkening its color
199
+ let sumR = 0
200
+ let sumG = 0
201
+ let sumB = 0
202
+ let sumA = 0
203
+ for (let i = 0; i < strs.length; i++) {
204
+ const str = strs[i]!
205
+ const name = names[i]!
206
+ for (let dc = 0; dc < colSpan; dc++) {
207
+ const col = col0 + c * colSpan + dc
208
+ const letter = str[col]
209
+ if (letter === undefined) {
210
+ break
211
+ }
212
+ const p = cellPixel(name, col, letter)
213
+ const a = (p >>> aShift) & 255
214
+ sumR += ((p >>> rShift) & 255) * a
215
+ sumG += ((p >>> gShift) & 255) * a
216
+ sumB += ((p >>> bShift) & 255) * a
217
+ sumA += a
218
+ }
219
+ }
220
+ out[base + c] =
221
+ sumA === 0
222
+ ? 0
223
+ : packRgba(
224
+ Math.round(sumR / sumA),
225
+ Math.round(sumG / sumA),
226
+ Math.round(sumB / sumA),
227
+ Math.round(sumA / cells),
228
+ )
229
+ }
230
+ }
231
+ }
232
+
132
233
  function usableRasterCtx(ctx: unknown): ctx is RasterCtx {
133
234
  const c = ctx as Record<string, unknown> | null
134
235
  return (
@@ -154,13 +255,17 @@ function makeRasterCanvas(width: number, height: number) {
154
255
  : undefined
155
256
  }
156
257
 
157
- function paint(pixels: Uint32Array, width: number, height: number) {
258
+ function paint(
259
+ width: number,
260
+ height: number,
261
+ fill: (out: Uint32Array) => void,
262
+ ) {
158
263
  const made = makeRasterCanvas(width, height)
159
264
  if (!made) {
160
265
  return undefined
161
266
  }
162
267
  const image = made.ctx.createImageData(width, height)
163
- new Uint32Array(image.data.buffer).set(pixels)
268
+ fill(new Uint32Array(image.data.buffer))
164
269
  made.ctx.putImageData(image, 0, 0)
165
270
  return made.canvas
166
271
  }
@@ -185,8 +290,8 @@ interface RasterCache {
185
290
 
186
291
  const caches = new WeakMap<object, RasterCache>()
187
292
 
188
- // Everything a cell's color depends on. A change to any of them makes every
189
- // cached tile stale, which a fresh cache expresses without tracking which.
293
+ // Everything a cell's color depends on; a change to any of them starts a fresh
294
+ // cache.
190
295
  function rasterKeys(model: MsaViewModel, theme: Theme) {
191
296
  return [
192
297
  model.columns,
@@ -223,8 +328,29 @@ function getCache(model: MsaViewModel, theme: Theme) {
223
328
  return next
224
329
  }
225
330
 
226
- function getTile(cache: RasterCache, tileRow: number, tileCol: number) {
227
- const key = `${tileRow}_${tileCol}`
331
+ // Cells averaged into one tile pixel along an axis: the smallest power of two
332
+ // that brings a pixel back to at least one device pixel. Powers of two keep the
333
+ // number of distinct tile sets a zoom sweep builds to a handful.
334
+ function cellsPerPixel(devicePixelsPerCell: number) {
335
+ return devicePixelsPerCell >= 1
336
+ ? 1
337
+ : 2 ** Math.ceil(Math.log2(1 / devicePixelsPerCell))
338
+ }
339
+
340
+ function getTile({
341
+ cache,
342
+ tileRow,
343
+ tileCol,
344
+ colSpan,
345
+ rowSpan,
346
+ }: {
347
+ cache: RasterCache
348
+ tileRow: number
349
+ tileCol: number
350
+ colSpan: number
351
+ rowSpan: number
352
+ }) {
353
+ const key = `${colSpan}_${rowSpan}_${tileRow}_${tileCol}`
228
354
  const hit = cache.tiles.get(key)
229
355
  if (hit) {
230
356
  // re-inserting makes this the newest entry: a Map iterates in insertion
@@ -233,18 +359,31 @@ function getTile(cache: RasterCache, tileRow: number, tileCol: number) {
233
359
  cache.tiles.set(key, hit)
234
360
  return hit
235
361
  }
236
- const col0 = tileCol * rasterTileSize
237
- const row0 = tileRow * rasterTileSize
238
- const width = Math.min(rasterTileSize, cache.numColumns - col0)
239
- const height = Math.min(rasterTileSize, cache.numRows - row0)
362
+ const col0 = tileCol * rasterTileSize * colSpan
363
+ const row0 = tileRow * rasterTileSize * rowSpan
364
+ const width = Math.min(
365
+ rasterTileSize,
366
+ Math.ceil((cache.numColumns - col0) / colSpan),
367
+ )
368
+ const height = Math.min(
369
+ rasterTileSize,
370
+ Math.ceil((cache.numRows - row0) / rowSpan),
371
+ )
240
372
  if (width <= 0 || height <= 0) {
241
373
  return undefined
242
374
  }
243
- const canvas = paint(
244
- rasterPixels({ spec: cache.spec, col0, row0, width, height }),
245
- width,
246
- height,
247
- )
375
+ const canvas = paint(width, height, out => {
376
+ rasterPixels({
377
+ spec: cache.spec,
378
+ col0,
379
+ row0,
380
+ width,
381
+ height,
382
+ colSpan,
383
+ rowSpan,
384
+ out,
385
+ })
386
+ })
248
387
  if (!canvas) {
249
388
  return undefined
250
389
  }
@@ -261,8 +400,12 @@ function getTile(cache: RasterCache, tileRow: number, tileCol: number) {
261
400
  /**
262
401
  * Paints the alignment background of one MSA block from cached
263
402
  * one-pixel-per-cell tiles. The tiles do not depend on the zoom level, so a zoom
264
- * frame costs a handful of drawImage calls instead of a fillRect per visible
265
- * cell -- which ran to 885ms per block at the minimum column width.
403
+ * frame costs a handful of drawImage calls. A fillRect per visible cell measured
404
+ * 885ms per block at the minimum column width.
405
+ *
406
+ * Below a device pixel per cell the tiles average cells together, per axis.
407
+ * The browser's image smoothing blurs both axes, which turns every row
408
+ * boundary into a gradient at fit-to-width, where only the columns are narrow.
266
409
  */
267
410
  export function drawMsaRaster({
268
411
  ctx,
@@ -279,17 +422,18 @@ export function drawMsaRaster({
279
422
  }) {
280
423
  const { colWidth, rowHeight, blockSize, highResScaleFactor } = model
281
424
  const cache = getCache(model, theme)
425
+ const colSpan = cellsPerPixel(colWidth * highResScaleFactor)
426
+ const rowSpan = cellsPerPixel(rowHeight * highResScaleFactor)
282
427
 
283
428
  ctx.resetTransform()
284
429
  ctx.scale(highResScaleFactor, highResScaleFactor)
285
430
  ctx.translate(-offsetX, -offsetY)
286
- // below a pixel per cell a tile carries more cells than the block has room
287
- // for, so let the browser downsample it rather than letting whichever cell
288
- // landed last win
289
- ctx.imageSmoothingEnabled = colWidth < 1 || rowHeight < 1
431
+ ctx.imageSmoothingEnabled = false
290
432
 
291
- const tileWidth = rasterTileSize * colWidth
292
- const tileHeight = rasterTileSize * rowHeight
433
+ const pixelWidth = colWidth * colSpan
434
+ const pixelHeight = rowHeight * rowSpan
435
+ const tileWidth = rasterTileSize * pixelWidth
436
+ const tileHeight = rasterTileSize * pixelHeight
293
437
  const firstCol = Math.max(0, Math.floor(offsetX / tileWidth))
294
438
  const lastCol = Math.floor((offsetX + blockSize) / tileWidth)
295
439
  const firstRow = Math.max(0, Math.floor(offsetY / tileHeight))
@@ -297,7 +441,7 @@ export function drawMsaRaster({
297
441
 
298
442
  for (let tileRow = firstRow; tileRow <= lastRow; tileRow++) {
299
443
  for (let tileCol = firstCol; tileCol <= lastCol; tileCol++) {
300
- const tile = getTile(cache, tileRow, tileCol)
444
+ const tile = getTile({ cache, tileRow, tileCol, colSpan, rowSpan })
301
445
  if (tile) {
302
446
  ctx.drawImage(
303
447
  tile,
@@ -307,8 +451,8 @@ export function drawMsaRaster({
307
451
  tile.height,
308
452
  tileCol * tileWidth,
309
453
  tileRow * tileHeight,
310
- tile.width * colWidth,
311
- tile.height * rowHeight,
454
+ tile.width * pixelWidth,
455
+ tile.height * pixelHeight,
312
456
  )
313
457
  }
314
458
  }
@@ -320,12 +464,11 @@ export function drawMsaRaster({
320
464
  /**
321
465
  * A raster canvas as a PNG data URI, for embedding in the SVG export.
322
466
  *
323
- * toDataURL is the synchronous read-back. The DOM canvas has it, and so does a
324
- * node canvas standing in for OffscreenCanvas headlessly; a browser
325
- * OffscreenCanvas -- which is what the tile and thumbnail caches hold wherever
326
- * it exists -- gets copied onto a DOM canvas first. Returns undefined wherever
327
- * the read-back is unavailable (jsdom) rather than throwing, which is the
328
- * signal to fall back to drawing the thing in vector.
467
+ * toDataURL is the synchronous read-back. The DOM canvas and a headless node
468
+ * canvas have it; a browser OffscreenCanvas, which the tile and thumbnail
469
+ * caches hold where it exists, is copied onto a DOM canvas first. Returns
470
+ * undefined where read-back is unavailable (jsdom), and the caller then draws
471
+ * in vector.
329
472
  */
330
473
  export function canvasHref(canvas: RasterCanvas | undefined) {
331
474
  if (!canvas) {
@@ -352,10 +495,10 @@ export function canvasHref(canvas: RasterCanvas | undefined) {
352
495
  }
353
496
  }
354
497
 
355
- // A canvas much past this many pixels fails to allocate, and the browser's own
356
- // per-side limit is lower still, so a raster bigger than either samples down.
357
- // The result is still drawn across the same rectangle -- it loses cell-exact
358
- // detail at a size where no figure could show it anyway.
498
+ // A canvas much past this many pixels fails to allocate, and the browser's
499
+ // per-side limit is lower still, so a larger raster averages down per axis and
500
+ // is drawn across the same rectangle. An alignment that hits the limit on
501
+ // columns alone keeps every row.
359
502
  const maxImagePixels = 64e6
360
503
  const maxImageSide = 16384
361
504
 
@@ -363,14 +506,12 @@ const maxImageSide = 16384
363
506
  * The alignment rectangle [col0, col0+numCols) x [row0, row0+numRows) as a PNG
364
507
  * data URI, for the SVG export.
365
508
  *
366
- * SVG has no blit, so the export cannot reuse the tile cache the live canvas
367
- * draws from: one <image> is the whole background instead. That is the
368
- * difference between an export that scales and one that does not -- the vector
369
- * path emits a <rect> per cell, and a 200x500 alignment exhausts the heap
370
- * building them.
509
+ * SVG has no blit, so the export draws the whole background as one <image>. The
510
+ * vector path emits a <rect> per cell, and a 200x500 alignment exhausts the
511
+ * heap building them.
371
512
  *
372
- * Returns undefined wherever a canvas cannot be read back (jsdom, notably),
373
- * which is the signal to keep the per-cell path.
513
+ * Returns undefined where a canvas cannot be read back (jsdom), and the caller
514
+ * then keeps the per-cell path.
374
515
  */
375
516
  export function rasterImageHref({
376
517
  model,
@@ -391,38 +532,46 @@ export function rasterImageHref({
391
532
  return undefined
392
533
  }
393
534
  const cache = getCache(model, theme)
394
- let step = 1
535
+ let colSpan = 1
536
+ let rowSpan = 1
537
+ while (Math.ceil(numCols / colSpan) > maxImageSide) {
538
+ colSpan *= 2
539
+ }
540
+ while (Math.ceil(numRows / rowSpan) > maxImageSide) {
541
+ rowSpan *= 2
542
+ }
395
543
  while (
396
- Math.ceil(numCols / step) * Math.ceil(numRows / step) > maxImagePixels ||
397
- Math.ceil(numCols / step) > maxImageSide ||
398
- Math.ceil(numRows / step) > maxImageSide
544
+ Math.ceil(numCols / colSpan) * Math.ceil(numRows / rowSpan) >
545
+ maxImagePixels
399
546
  ) {
400
- step *= 2
547
+ if (numCols / colSpan >= numRows / rowSpan) {
548
+ colSpan *= 2
549
+ } else {
550
+ rowSpan *= 2
551
+ }
401
552
  }
402
- const width = Math.ceil(numCols / step)
403
- const height = Math.ceil(numRows / step)
553
+ const width = Math.ceil(numCols / colSpan)
554
+ const height = Math.ceil(numRows / rowSpan)
404
555
 
405
556
  return canvasHref(
406
- paint(
557
+ paint(width, height, out => {
407
558
  rasterPixels({
408
559
  spec: cache.spec,
409
560
  col0,
410
561
  row0,
411
562
  width,
412
563
  height,
413
- colStep: step,
414
- rowStep: step,
415
- }),
416
- width,
417
- height,
418
- ),
564
+ colSpan,
565
+ rowSpan,
566
+ out,
567
+ })
568
+ }),
419
569
  )
420
570
  }
421
571
 
422
572
  /**
423
- * The whole alignment sampled down to a strip small enough to sit behind the
424
- * minimap thumb, cached alongside the block tiles because it goes stale under
425
- * exactly the same conditions.
573
+ * The whole alignment sampled down to a strip behind the minimap thumb, cached
574
+ * with the block tiles since the same keys invalidate both.
426
575
  */
427
576
  export function msaThumbnail({
428
577
  model,
@@ -447,7 +596,7 @@ export function msaThumbnail({
447
596
  const h = Math.ceil(numRows / rowStep)
448
597
  cache.thumbnail = {
449
598
  height,
450
- canvas: paint(
599
+ canvas: paint(w, h, out => {
451
600
  rasterPixels({
452
601
  spec: cache.spec,
453
602
  col0: 0,
@@ -456,10 +605,9 @@ export function msaThumbnail({
456
605
  height: h,
457
606
  colStep,
458
607
  rowStep,
459
- }),
460
- w,
461
- h,
462
- ),
608
+ out,
609
+ })
610
+ }),
463
611
  }
464
612
  }
465
613
  return cache.thumbnail.canvas