jbrowse-plugin-msaview 3.5.0 → 3.7.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 (242) hide show
  1. package/README.md +8 -0
  2. package/dist/AddHighlightModel/index.js +3 -3
  3. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +8 -2
  4. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +3 -3
  5. package/dist/BgzipFastaMsaAdapter/configSchema.js +1 -1
  6. package/dist/LaunchMsaView/cleanProteinSequence.test.d.ts +1 -0
  7. package/dist/LaunchMsaView/cleanProteinSequence.test.js +77 -0
  8. package/dist/LaunchMsaView/codingFeature.d.ts +14 -0
  9. package/dist/LaunchMsaView/codingFeature.js +38 -0
  10. package/dist/LaunchMsaView/codingFeature.test.d.ts +1 -0
  11. package/dist/LaunchMsaView/codingFeature.test.js +75 -0
  12. package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.d.ts +3 -1
  13. package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +47 -55
  14. package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.d.ts +3 -1
  15. package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +16 -17
  16. package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.d.ts +7 -3
  17. package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.js +8 -7
  18. package/dist/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.js +2 -2
  19. package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.js +14 -13
  20. package/dist/LaunchMsaView/components/BlastQuery/consts.d.ts +2 -0
  21. package/dist/LaunchMsaView/components/BlastQuery/consts.js +28 -4
  22. package/dist/LaunchMsaView/components/HelpButton.d.ts +2 -0
  23. package/dist/LaunchMsaView/components/HelpButton.js +17 -0
  24. package/dist/LaunchMsaView/components/HelpDialog.d.ts +4 -0
  25. package/dist/LaunchMsaView/components/HelpDialog.js +18 -0
  26. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +3 -1
  27. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +21 -16
  28. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.d.ts +3 -1
  29. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +34 -26
  30. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.d.ts +3 -1
  31. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +30 -26
  32. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.d.ts +2 -1
  33. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.js +18 -2
  34. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.d.ts +1 -0
  35. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.js +13 -0
  36. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +3 -1
  37. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +50 -27
  38. package/dist/LaunchMsaView/components/SequenceStatus.d.ts +14 -0
  39. package/dist/LaunchMsaView/components/SequenceStatus.js +15 -0
  40. package/dist/LaunchMsaView/components/SubmitCancelActions.d.ts +6 -2
  41. package/dist/LaunchMsaView/components/SubmitCancelActions.js +28 -13
  42. package/dist/LaunchMsaView/components/SubmitCancelActions.test.js +52 -4
  43. package/dist/LaunchMsaView/components/TabPanel.js +15 -3
  44. package/dist/LaunchMsaView/components/TabPanel.test.d.ts +1 -0
  45. package/dist/LaunchMsaView/components/TabPanel.test.js +36 -0
  46. package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +3 -1
  47. package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -2
  48. package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +12 -2
  49. package/dist/LaunchMsaView/components/calculateProteinSequence.js +38 -23
  50. package/dist/LaunchMsaView/components/fetchSeq.d.ts +4 -1
  51. package/dist/LaunchMsaView/components/fetchSeq.js +14 -4
  52. package/dist/LaunchMsaView/components/launchConnectedView.d.ts +23 -0
  53. package/dist/LaunchMsaView/components/launchConnectedView.js +39 -0
  54. package/dist/LaunchMsaView/components/launchPlacement.d.ts +8 -0
  55. package/dist/LaunchMsaView/components/launchPlacement.js +25 -0
  56. package/dist/LaunchMsaView/components/useFeatureSequence.d.ts +2 -0
  57. package/dist/LaunchMsaView/components/useFeatureSequence.js +14 -11
  58. package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +10 -1
  59. package/dist/LaunchMsaView/components/useTranscriptSelection.js +21 -3
  60. package/dist/LaunchMsaView/components/useTranscriptSelection.test.d.ts +1 -0
  61. package/dist/LaunchMsaView/components/useTranscriptSelection.test.js +57 -0
  62. package/dist/LaunchMsaView/detectQueryRow.js +4 -7
  63. package/dist/LaunchMsaView/index.js +28 -9
  64. package/dist/LaunchMsaView/launchTarget.d.ts +36 -8
  65. package/dist/LaunchMsaView/launchTarget.js +23 -13
  66. package/dist/LaunchMsaView/launchTarget.test.js +80 -15
  67. package/dist/LaunchMsaView/useQueryRowName.d.ts +12 -0
  68. package/dist/LaunchMsaView/useQueryRowName.js +11 -0
  69. package/dist/LaunchMsaView/useQueryRowName.test.js +18 -1
  70. package/dist/LaunchMsaView/util.d.ts +6 -0
  71. package/dist/LaunchMsaView/util.js +12 -3
  72. package/dist/LaunchMsaViewExtensionPoint/index.test.js +15 -0
  73. package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +6 -4
  74. package/dist/MsaViewPanel/afterCreateAutoruns.js +38 -14
  75. package/dist/MsaViewPanel/applyRegion.d.ts +23 -0
  76. package/dist/MsaViewPanel/applyRegion.js +19 -0
  77. package/dist/MsaViewPanel/applyRegion.test.d.ts +1 -0
  78. package/dist/MsaViewPanel/applyRegion.test.js +45 -0
  79. package/dist/MsaViewPanel/components/LaunchProgress.d.ts +4 -3
  80. package/dist/MsaViewPanel/components/LaunchProgress.js +10 -7
  81. package/dist/MsaViewPanel/components/MsaViewPanel.js +7 -3
  82. package/dist/MsaViewPanel/components/MsaViewPanel.test.js +24 -0
  83. package/dist/MsaViewPanel/doLaunchBlast.js +2 -1
  84. package/dist/MsaViewPanel/doLaunchBlast.test.js +12 -0
  85. package/dist/MsaViewPanel/doLaunchOrthologs.test.js +1 -1
  86. package/dist/MsaViewPanel/genomeToMSA.js +24 -6
  87. package/dist/MsaViewPanel/genomeToMSA.test.js +218 -44
  88. package/dist/MsaViewPanel/launchAutoruns.test.d.ts +1 -0
  89. package/dist/MsaViewPanel/launchAutoruns.test.js +36 -0
  90. package/dist/MsaViewPanel/model.d.ts +156 -61
  91. package/dist/MsaViewPanel/model.js +69 -3
  92. package/dist/MsaViewPanel/model.test.js +66 -0
  93. package/dist/MsaViewPanel/msaDataStore.d.ts +5 -3
  94. package/dist/MsaViewPanel/msaDataStore.js +3 -12
  95. package/dist/MsaViewPanel/msaDataStore.test.js +8 -1
  96. package/dist/MsaViewPanel/observeProteinHighlights.test.js +4 -4
  97. package/dist/MsaViewPanel/regionSnapshot.test.d.ts +1 -0
  98. package/dist/MsaViewPanel/regionSnapshot.test.js +29 -0
  99. package/dist/MsaViewPanel/resolveConnectedTranscript.d.ts +41 -4
  100. package/dist/MsaViewPanel/resolveConnectedTranscript.js +109 -68
  101. package/dist/MsaViewPanel/resolveConnectedTranscript.test.d.ts +1 -0
  102. package/dist/MsaViewPanel/resolveConnectedTranscript.test.js +149 -0
  103. package/dist/MsaViewPanel/runLaunch.d.ts +8 -4
  104. package/dist/MsaViewPanel/runLaunch.js +15 -4
  105. package/dist/MsaViewPanel/runLaunch.test.js +16 -0
  106. package/dist/MsaViewPanel/storedData.test.js +43 -0
  107. package/dist/MsaViewPanel/syncGenomeHoverToMsaColumn.test.js +15 -7
  108. package/dist/MsaViewPanel/util.d.ts +23 -23
  109. package/dist/MsaViewPanel/util.js +23 -24
  110. package/dist/index.d.ts +11 -11
  111. package/dist/index.js +17 -10
  112. package/dist/jbrowse-plugin-msaview.umd.production.min.js +54 -67
  113. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  114. package/dist/utils/blastCache.d.ts +26 -2
  115. package/dist/utils/blastCache.js +51 -39
  116. package/dist/utils/blastCache.test.js +46 -19
  117. package/dist/utils/domainCache.d.ts +1 -1
  118. package/dist/utils/domainCache.js +18 -14
  119. package/dist/utils/ebiJobDispatcher.d.ts +11 -0
  120. package/dist/utils/ebiJobDispatcher.js +12 -13
  121. package/dist/utils/ebiJobDispatcher.test.js +17 -3
  122. package/dist/utils/eutils.d.ts +14 -0
  123. package/dist/utils/eutils.js +48 -14
  124. package/dist/utils/eutils.test.d.ts +1 -0
  125. package/dist/utils/eutils.test.js +31 -0
  126. package/dist/utils/idb.d.ts +16 -3
  127. package/dist/utils/idb.js +51 -5
  128. package/dist/utils/idb.test.d.ts +1 -0
  129. package/dist/utils/idb.test.js +78 -0
  130. package/dist/utils/ncbiDomains.js +4 -4
  131. package/dist/utils/ncbiOrthologs.d.ts +1 -1
  132. package/dist/utils/ncbiOrthologs.js +18 -17
  133. package/dist/utils/ncbiOrthologs.test.js +17 -1
  134. package/dist/utils/ncbiTaxonomy.js +14 -15
  135. package/dist/utils/taxonomyNames.js +23 -19
  136. package/dist/utils/taxonomyNames.test.d.ts +1 -0
  137. package/dist/utils/taxonomyNames.test.js +33 -0
  138. package/dist/utils/unirefHomologs.js +2 -2
  139. package/dist/utils/unirefHomologs.test.js +8 -1
  140. package/dist/version.d.ts +1 -1
  141. package/dist/version.js +1 -1
  142. package/package.json +23 -23
  143. package/src/AddHighlightModel/index.tsx +1 -1
  144. package/src/BgzipFastaMsaAdapter/configSchema.ts +1 -1
  145. package/src/LaunchMsaView/cleanProteinSequence.test.ts +91 -0
  146. package/src/LaunchMsaView/codingFeature.test.ts +96 -0
  147. package/src/LaunchMsaView/codingFeature.ts +49 -0
  148. package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +60 -56
  149. package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +18 -15
  150. package/src/LaunchMsaView/components/BlastQuery/BlastPanel.tsx +24 -11
  151. package/src/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.tsx +4 -4
  152. package/src/LaunchMsaView/components/BlastQuery/CachedBlastResults.tsx +16 -15
  153. package/src/LaunchMsaView/components/BlastQuery/consts.ts +31 -4
  154. package/src/LaunchMsaView/components/HelpButton.tsx +33 -0
  155. package/src/LaunchMsaView/components/HelpDialog.tsx +78 -0
  156. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +54 -34
  157. package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +46 -23
  158. package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +41 -28
  159. package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.ts +16 -0
  160. package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.tsx +24 -2
  161. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +81 -25
  162. package/src/LaunchMsaView/components/SequenceStatus.tsx +29 -0
  163. package/src/LaunchMsaView/components/SubmitCancelActions.test.tsx +88 -4
  164. package/src/LaunchMsaView/components/SubmitCancelActions.tsx +43 -16
  165. package/src/LaunchMsaView/components/TabPanel.test.tsx +51 -0
  166. package/src/LaunchMsaView/components/TabPanel.tsx +16 -4
  167. package/src/LaunchMsaView/components/TranscriptSelector.tsx +6 -1
  168. package/src/LaunchMsaView/components/calculateProteinSequence.ts +51 -22
  169. package/src/LaunchMsaView/components/fetchSeq.ts +25 -5
  170. package/src/LaunchMsaView/components/launchConnectedView.ts +56 -0
  171. package/src/LaunchMsaView/components/launchPlacement.tsx +41 -0
  172. package/src/LaunchMsaView/components/useFeatureSequence.ts +18 -11
  173. package/src/LaunchMsaView/components/useTranscriptSelection.test.tsx +66 -0
  174. package/src/LaunchMsaView/components/useTranscriptSelection.ts +41 -2
  175. package/src/LaunchMsaView/detectQueryRow.ts +5 -7
  176. package/src/LaunchMsaView/index.ts +32 -13
  177. package/src/LaunchMsaView/launchTarget.test.ts +88 -15
  178. package/src/LaunchMsaView/launchTarget.ts +49 -14
  179. package/src/LaunchMsaView/useQueryRowName.test.ts +28 -1
  180. package/src/LaunchMsaView/useQueryRowName.ts +17 -0
  181. package/src/LaunchMsaView/util.ts +15 -5
  182. package/src/LaunchMsaViewExtensionPoint/index.test.ts +16 -0
  183. package/src/LaunchMsaViewExtensionPoint/index.ts +28 -1
  184. package/src/MsaViewPanel/afterCreateAutoruns.ts +46 -14
  185. package/src/MsaViewPanel/applyRegion.test.ts +55 -0
  186. package/src/MsaViewPanel/applyRegion.ts +33 -0
  187. package/src/MsaViewPanel/components/LaunchProgress.tsx +12 -5
  188. package/src/MsaViewPanel/components/MsaViewPanel.test.tsx +26 -0
  189. package/src/MsaViewPanel/components/MsaViewPanel.tsx +7 -5
  190. package/src/MsaViewPanel/doLaunchBlast.test.ts +20 -1
  191. package/src/MsaViewPanel/doLaunchBlast.ts +2 -1
  192. package/src/MsaViewPanel/doLaunchOrthologs.test.ts +1 -1
  193. package/src/MsaViewPanel/genomeToMSA.test.ts +255 -59
  194. package/src/MsaViewPanel/genomeToMSA.ts +30 -5
  195. package/src/MsaViewPanel/launchAutoruns.test.ts +45 -0
  196. package/src/MsaViewPanel/model.test.ts +75 -0
  197. package/src/MsaViewPanel/model.ts +76 -3
  198. package/src/MsaViewPanel/msaDataStore.test.ts +14 -2
  199. package/src/MsaViewPanel/msaDataStore.ts +4 -12
  200. package/src/MsaViewPanel/observeProteinHighlights.test.ts +4 -4
  201. package/src/MsaViewPanel/regionSnapshot.test.ts +34 -0
  202. package/src/MsaViewPanel/resolveConnectedTranscript.test.ts +183 -0
  203. package/src/MsaViewPanel/resolveConnectedTranscript.ts +160 -67
  204. package/src/MsaViewPanel/runLaunch.test.ts +19 -0
  205. package/src/MsaViewPanel/runLaunch.ts +21 -4
  206. package/src/MsaViewPanel/storedData.test.ts +58 -0
  207. package/src/MsaViewPanel/syncGenomeHoverToMsaColumn.test.ts +17 -7
  208. package/src/MsaViewPanel/util.ts +38 -29
  209. package/src/index.ts +19 -10
  210. package/src/utils/blastCache.test.ts +59 -20
  211. package/src/utils/blastCache.ts +62 -39
  212. package/src/utils/domainCache.ts +27 -15
  213. package/src/utils/ebiJobDispatcher.test.ts +18 -3
  214. package/src/utils/ebiJobDispatcher.ts +12 -16
  215. package/src/utils/eutils.test.ts +39 -0
  216. package/src/utils/eutils.ts +64 -14
  217. package/src/utils/idb.test.ts +106 -0
  218. package/src/utils/idb.ts +57 -5
  219. package/src/utils/ncbiDomains.ts +4 -4
  220. package/src/utils/ncbiOrthologs.test.ts +20 -0
  221. package/src/utils/ncbiOrthologs.ts +26 -28
  222. package/src/utils/ncbiTaxonomy.ts +17 -20
  223. package/src/utils/taxonomyNames.test.ts +41 -0
  224. package/src/utils/taxonomyNames.ts +33 -19
  225. package/src/utils/unirefHomologs.test.ts +11 -1
  226. package/src/utils/unirefHomologs.ts +2 -2
  227. package/src/version.ts +1 -1
  228. package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.d.ts +0 -16
  229. package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.js +0 -31
  230. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +0 -16
  231. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +0 -16
  232. package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.d.ts +0 -9
  233. package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.js +0 -15
  234. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +0 -11
  235. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +0 -21
  236. package/dist/LaunchMsaView/components/geneticCodes.d.ts +0 -15
  237. package/dist/LaunchMsaView/components/geneticCodes.js +0 -227
  238. package/src/LaunchMsaView/components/BlastQuery/blastLaunchView.ts +0 -59
  239. package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +0 -43
  240. package/src/LaunchMsaView/components/OrthologQuery/orthologLaunchView.ts +0 -31
  241. package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +0 -38
  242. package/src/LaunchMsaView/components/geneticCodes.ts +0 -298
package/README.md CHANGED
@@ -14,6 +14,14 @@ MSAView plugin running in JBrowse 2
14
14
 
15
15
  ## Demo
16
16
 
17
+ [p53 across vertebrates, linked to the genome and the structure](https://jbrowse.org/code/jb2/main/?config=https://jbrowse.org/ucsc/hg38/config.json&session=spec-{%22views%22:[{%22type%22:%22LinearGenomeView%22,%22id%22:%22lgv1%22,%22assembly%22:%22hg38%22,%22loc%22:%22chr17:7,661,779-7,687,538%22,%22tracks%22:[%22hg38-ncbiRefSeqSelect%22,%22hg38-clinvarMain%22]},{%22type%22:%22MsaView%22,%22displayName%22:%22p53%20across%20vertebrates%22,%22msaFileLocation%22:{%22uri%22:%22https://gmod.org/JBrowseMSA/demo/data/p53/p53-vertebrates.afa%22},%22treeFileLocation%22:{%22uri%22:%22https://gmod.org/JBrowseMSA/demo/data/p53/p53-vertebrates.nh%22},%22querySeqName%22:%22Human%22,%22relativeTo%22:%22Human%22,%22connectedViewId%22:%22lgv1%22,%22connectedTranscript%22:%22NM_000546.6%22,%22placement%22:%22splitRight%22,%22colorSchemeName%22:%22clustalx_protein_dynamic%22,%22highlights%22:[{%22row%22:%22Human%22,%22start%22:102,%22end%22:292,%22label%22:%22DNA-binding%22,%22color%22:%22rgba%28255,140,0,0.15%29%22},{%22row%22:%22Human%22,%22start%22:175,%22end%22:175,%22label%22:%22R175%22},{%22row%22:%22Human%22,%22start%22:245,%22end%22:245,%22label%22:%22G245%22},{%22row%22:%22Human%22,%22start%22:248,%22end%22:248,%22label%22:%22R248%22},{%22row%22:%22Human%22,%22start%22:249,%22end%22:249,%22label%22:%22R249%22},{%22row%22:%22Human%22,%22start%22:273,%22end%22:273,%22label%22:%22R273%22},{%22row%22:%22Human%22,%22start%22:282,%22end%22:282,%22label%22:%22R282%22}],%22columnTracks%22:[{%22id%22:%22clinvar%22,%22name%22:%22ClinVar%20pathogenic%20missense%22,%22kind%22:%22bar%22,%22row%22:%22Human%22,%22color%22:%22%23c0392b%22,%22height%22:60,%22max%22:8,%22values%22:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,2,4,3,0,6,0,0,0,0,0,0,0,0,0,0,1,2,1,5,0,0,2,2,3,1,1,4,0,0,2,0,0,2,0,3,0,0,0,1,0,0,0,6,2,0,0,1,1,3,6,2,0,1,0,2,1,1,0,0,2,0,0,2,1,3,0,3,3,1,3,5,1,3,0,0,0,0,0,0,0,0,1,0,0,4,3,1,1,1,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,3,2,1,1,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,3,0,4,0,2,4,6,2,3,5,2,0,4,5,4,1,6,2,1,2,0,1,3,0,0,0,2,1,0,0,0,0,0,2,3,3,0,1,4,1,3,6,0,2,1,1,3,0,3,8,3,1,0,2,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,5,0,0,0,1,1,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{%22id%22:%22alphamissense%22,%22name%22:%22AlphaMissense%20mean%20%28x100%29%22,%22kind%22:%22bar%22,%22row%22:%22Human%22,%22color%22:%22%231565c0%22,%22height%22:60,%22max%22:100,%22values%22:[49,27,19,11,12,14,18,10,12,14,28,17,40,57,73,65,65,68,95,29,30,72,95,21,24,60,39,21,18,17,18,16,17,12,11,13,14,13,14,21,22,20,17,20,16,16,15,14,17,17,15,13,37,29,11,19,16,11,11,12,14,20,14,12,11,20,13,19,15,14,12,13,15,17,14,17,15,19,20,17,15,15,22,22,19,22,20,24,17,23,67,41,46,66,63,37,90,98,63,35,36,20,76,15,99,13,58,48,98,26,81,57,97,43,17,64,87,68,90,98,96,92,73,81,98,91,95,23,13,81,56,97,84,97,98,87,86,81,91,84,94,93,89,66,81,57,77,26,20,13,90,69,17,52,70,49,91,98,87,88,94,87,95,89,48,54,52,76,76,68,92,87,98,78,99,100,98,97,100,96,85,50,38,52,32,74,52,32,83,87,71,47,99,95,89,95,91,96,95,81,20,25,69,48,98,24,64,93,17,23,84,56,99,92,91,93,70,89,76,91,77,27,92,87,57,92,58,35,54,68,75,87,85,87,74,93,96,100,95,98,99,100,94,100,100,98,97,100,99,93,93,59,91,91,87,92,93,99,87,22,21,94,24,76,83,99,98,36,61,97,93,93,99,95,100,97,100,100,99,100,100,97,84,60,98,96,47,62,13,18,56,34,17,17,11,9,9,16,11,15,13,18,20,17,81,75,26,26,23,13,12,14,16,18,18,14,15,15,47,54,46,19,13,34,24,50,56,88,36,87,48,93,63,98,73,40,89,90,45,60,88,51,36,81,77,55,79,75,87,31,54,67,23,18,17,16,25,22,13,15,15,16,20,18,15,20,25,13,15,56,21,47,45,21,16,16,15,21,16,20,66,47,13,26,19,66,13,28,18,16,47,54,66]},{%22id%22:%22mavedb%22,%22name%22:%22MaveDB%20nutlin-3,%20p53WT%22,%22kind%22:%22bar%22,%22row%22:%22Human%22,%22color%22:%22%232e7d32%22,%22height%22:60,%22max%22:2,%22values%22:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.6,0,0.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,0,0,0.2,0.4,0,0,0,0,0.1,0,0.8,0,0.7,0.3,1.6,0.1,1.4,0.1,1.3,0,0,0,0.1,0,0.1,0.5,0.2,0.4,0.4,0.8,0.8,1.4,1.5,0.1,0.1,1.2,1,1.7,1.1,1.4,1.3,0.8,0.5,0.5,0.8,0.4,1.2,0.4,1.4,0.7,1.2,0.6,1.1,0.5,0.4,0.6,1.5,1.3,0,0.7,1.3,0.3,1.4,1,1.4,0.8,1.4,0.8,1.8,0.8,0.3,0,0.2,0.8,0.4,0,0.6,0.7,1.7,1,1.2,2,1.1,0.6,2,0.8,0.7,0.3,0.1,0.4,0.2,0.3,0.1,0.1,0.5,0.8,0.1,0.1,1.4,1.6,1.4,0.7,1.3,0.1,0.1,0,0,0,0.9,0.3,1.8,0,0,1,0,0.1,0.9,0.7,1.2,1.1,1.5,1.4,0.6,1.3,0.5,1.5,0.4,0.3,0.6,0.3,0.1,0.4,0.3,0.2,0.7,0.5,0.6,1.4,0.5,1.4,0.6,1.7,1.1,1.9,0.8,1.3,1.3,1.7,0.9,1.7,2.2,2,0.9,1.5,2.1,1,1.4,0.3,1.3,1.2,1.4,0.8,1.4,1.4,0.9,0.5,0.5,0.7,0.5,0.5,0.8,1.6,1,0.9,0.8,1.5,1.3,1.5,1.5,1.7,1.8,0.5,0.4,2,0,0.9,0.6,1,0,0,1,1.8,0,0.1,0.2,0.1,0.2,0,0.1,0.4,0.4,0.3,0.4,0.6,0.4,0.6,0.5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],%22region%22:{%22row%22:%22Human%22,%22start%22:170,%22end%22:290}},{%22type%22:%22ProteinView%22,%22uniprotId%22:%22P04637%22,%22transcriptId%22:%22NM_000546.6%22,%22connectedViewId%22:%22lgv1%22,%22connectedView%22:{%22assembly%22:%22hg38%22,%22loc%22:%22chr17:7,661,779-7,687,538%22,%22tracks%22:[%22hg38-ncbiRefSeqSelect%22]}}]}):
18
+ TP53 in hg38 with ClinVar, a vertebrate p53 alignment carrying ClinVar,
19
+ AlphaMissense and MaveDB per-residue tracks, and the AlphaFold model of P04637.
20
+ Hovering a codon, a column or a residue lights the other two views. See
21
+ [DEVELOPERS.md](DEVELOPERS.md#demo-p53-variant-evidence) for how the link is
22
+ built.
23
+
24
+ An older shared session:
17
25
  https://jbrowse.org/code/jb2/main/index.html?config=https://unpkg.com/jbrowse-plugin-msaview/dist/config.json&session=share-BVmmEYAoAv&password=SuQaN
18
26
 
19
27
  ## Features
@@ -3,9 +3,9 @@ import { getSession } from '@jbrowse/core/util';
3
3
  import { isMsaView } from '../MsaViewPanel/model';
4
4
  import HighlightComponents from './HighlightComponents';
5
5
  export default function AddHighlightComponentsModelF(pluginManager) {
6
- pluginManager.addToExtensionPoint('LinearGenomeView-TracksContainerComponent',
7
- // @ts-expect-error
8
- (rest, { model }) => {
6
+ pluginManager.addToExtensionPoint(
7
+ // @ts-expect-error v4 hosts have no contributeToExtensionPoint
8
+ 'LinearGenomeView-TracksContainerComponent', (rest, { model }) => {
9
9
  // Quick check: don't add any components if no MSA view exists
10
10
  const { views } = getSession(model);
11
11
  const hasMsaView = views.some(v => isMsaView(v) && v.connectedViewId === model.id);
@@ -3,8 +3,14 @@ import type { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAda
3
3
  export default class BgzipFastaMsaAdapter extends BaseAdapter {
4
4
  configureP: Promise<BaseFeatureDataAdapter> | undefined;
5
5
  refNamesP: Promise<string[]> | undefined;
6
- configurePre(): Promise<BaseFeatureDataAdapter>;
7
- configure(): Promise<BaseFeatureDataAdapter>;
6
+ configurePre(): Promise<BaseFeatureDataAdapter<import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
7
+ setSubschema(slotName: string, data: Record<string, unknown>): any;
8
+ setSlot(slotName: string, value: unknown): void;
9
+ } & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>>;
10
+ configure(): Promise<BaseFeatureDataAdapter<import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
11
+ setSubschema(slotName: string, data: Record<string, unknown>): any;
12
+ setSlot(slotName: string, value: unknown): void;
13
+ } & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>>;
8
14
  getMSARefs(): Promise<string[]>;
9
15
  getMsaRegex(): RegExp;
10
16
  refNameToMsaId(refName: string): string;
@@ -1,7 +1,7 @@
1
1
  import type PluginManager from '@jbrowse/core/PluginManager';
2
2
  export default function configSchemaF(pluginManager: PluginManager): import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
3
- msaRegex: {
4
- type: string;
5
- defaultValue: string;
3
+ readonly msaRegex: {
4
+ readonly type: "string";
5
+ readonly defaultValue: "_";
6
6
  };
7
7
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<import("@jbrowse/core/configuration").AnyConfigurationSchemaType, undefined>>;
@@ -7,6 +7,6 @@ export default function configSchemaF(pluginManager) {
7
7
  defaultValue: '_',
8
8
  },
9
9
  }, {
10
- baseConfiguration: base?.configSchema,
10
+ baseConfiguration: base.configSchema,
11
11
  });
12
12
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,77 @@
1
+ import { SimpleFeature } from '@jbrowse/core/util';
2
+ import { genomeToTranscriptSeqMapping } from 'g2p_mapper';
3
+ import { describe, expect, test } from 'vitest';
4
+ import { transcriptPosToVisibleCol } from '../MsaViewPanel/util';
5
+ import { getProteinSequenceFromFeature } from './components/calculateProteinSequence';
6
+ import { findQueryRow } from './detectQueryRow';
7
+ import { cleanProteinSequence } from './util';
8
+ describe('cleanProteinSequence', () => {
9
+ test('drops the stop and a trailing partial codon', () => {
10
+ expect(cleanProteinSequence('MAWK*')).toBe('MAWK');
11
+ expect(cleanProteinSequence('MAWK&')).toBe('MAWK');
12
+ });
13
+ test('keeps a residue for every other codon', () => {
14
+ expect(cleanProteinSequence('&MAWK*')).toBe('XMAWK');
15
+ expect(cleanProteinSequence('MA*WK*')).toBe('MAXWK');
16
+ expect(cleanProteinSequence('MA&WK')).toBe('MAXWK');
17
+ });
18
+ });
19
+ // what the genome view hovers is a codon, and what lights is the query row's
20
+ // residue at g2p's number for that codon, so the two have to count alike
21
+ describe('a launched query row read through g2p', () => {
22
+ function rowResidueUnder(genomePos, json) {
23
+ const seq = json.seq;
24
+ const feature = new SimpleFeature(json);
25
+ const row = cleanProteinSequence(getProteinSequenceFromFeature({ seq, feature }));
26
+ const { g2p } = genomeToTranscriptSeqMapping(feature.toJSON());
27
+ const col = transcriptPosToVisibleCol({
28
+ querySeqName: 'QUERY',
29
+ querySeqOffset: 0,
30
+ seqPosToVisibleCol: (_, p) => (p < row.length ? p : row.length),
31
+ visibleColToSeqPos: (_, c) => (c < row.length ? c : undefined),
32
+ }, g2p[genomePos]);
33
+ return row[col];
34
+ }
35
+ function transcript(seq, cds) {
36
+ return {
37
+ uniqueId: 't1',
38
+ refName: 'chr1',
39
+ start: 0,
40
+ end: seq.length,
41
+ strand: 1,
42
+ type: 'mRNA',
43
+ seq,
44
+ subfeatures: [
45
+ { uniqueId: 'c1', refName: 'chr1', start: 0, end: seq.length, ...cds },
46
+ ],
47
+ };
48
+ }
49
+ test('after a partial first codon', () => {
50
+ // G | ATG GCC TGG AAA TAA: base 4 opens GCC, alanine
51
+ const json = transcript('GATGGCCTGGAAATAA', { type: 'CDS', phase: 1 });
52
+ expect(rowResidueUnder(4, json)).toBe('A');
53
+ expect(rowResidueUnder(10, json)).toBe('K');
54
+ });
55
+ test('after an internal stop', () => {
56
+ // ATG TGA GCC TGG TAA: a selenocysteine GENCODE does not annotate
57
+ const json = transcript('ATGTGAGCCTGGTAA', { type: 'CDS' });
58
+ expect(rowResidueUnder(6, json)).toBe('A');
59
+ });
60
+ });
61
+ describe('query row detection', () => {
62
+ test('matches a row a search launch built from a partial transcript', () => {
63
+ const msa = '>QUERY\nXMAWK\n>other\nMAWR-';
64
+ expect(findQueryRow(msa, '&MAWK*').match).toMatchObject({
65
+ name: 'QUERY',
66
+ quality: 'exact',
67
+ offset: 0,
68
+ });
69
+ });
70
+ test('still places a row that starts at the first whole codon', () => {
71
+ const msa = '>Query_1\nMAWK\n>other\nMAWR';
72
+ expect(findQueryRow(msa, '&MAWK*').match).toMatchObject({
73
+ name: 'Query_1',
74
+ offset: 1,
75
+ });
76
+ });
77
+ });
@@ -0,0 +1,14 @@
1
+ import type { Feature } from '@jbrowse/core/util';
2
+ export declare function isGeneLikeType(type: unknown): boolean;
3
+ export declare function isCodingFeature(feature: Feature): boolean;
4
+ /**
5
+ * Whether the feature is known not to code for anything.
6
+ *
7
+ * Not simply `!isCodingFeature`: a feature that arrived with no subfeatures at
8
+ * all says nothing either way, and a host is free to hand one over that way.
9
+ * Reading that as "no protein here" takes the menu item off a perfectly
10
+ * ordinary gene, silently, which is worse than opening a dialog that then has
11
+ * nothing to translate.
12
+ */
13
+ export declare function isKnownNonCoding(feature: Feature): boolean;
14
+ export declare function geneLikeRoot(feature: Feature): Feature;
@@ -0,0 +1,38 @@
1
+ // Copied from @jbrowse/core's featureTypes rather than imported: the barrel
2
+ // only exports it from v5, and a bundle resolving it on a v4 host reads
3
+ // undefined.
4
+ const GENE_LIKE_TYPE = /gene(_segment)?$|rna$|transcript/;
5
+ // `unknown` rather than `string | undefined`, because the type comes off a
6
+ // host's hit-test item and nothing here built it.
7
+ export function isGeneLikeType(type) {
8
+ return typeof type === 'string' && GENE_LIKE_TYPE.test(type.toLowerCase());
9
+ }
10
+ function isCDS(feature) {
11
+ return feature.get('type')?.toLowerCase() === 'cds';
12
+ }
13
+ // The feature itself counts: a viral polyprotein hangs its cleavage products
14
+ // off its CDS rather than off further CDSs.
15
+ export function isCodingFeature(feature) {
16
+ return isCDS(feature) || !!feature.get('subfeatures')?.some(isCodingFeature);
17
+ }
18
+ /**
19
+ * Whether the feature is known not to code for anything.
20
+ *
21
+ * Not simply `!isCodingFeature`: a feature that arrived with no subfeatures at
22
+ * all says nothing either way, and a host is free to hand one over that way.
23
+ * Reading that as "no protein here" takes the menu item off a perfectly
24
+ * ordinary gene, silently, which is worse than opening a dialog that then has
25
+ * nothing to translate.
26
+ */
27
+ export function isKnownNonCoding(feature) {
28
+ return !!feature.get('subfeatures')?.length && !isCodingFeature(feature);
29
+ }
30
+ // The outermost gene-like ancestor, so a click on an isoform opens the dialog
31
+ // on the gene with every transcript to choose from, as the canvas host does.
32
+ export function geneLikeRoot(feature) {
33
+ let root = feature;
34
+ for (let parent = root.parent?.(); parent && isGeneLikeType(parent.get('type')); parent = parent.parent?.()) {
35
+ root = parent;
36
+ }
37
+ return root;
38
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import { SimpleFeature } from '@jbrowse/core/util';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { geneLikeRoot, isCodingFeature, isGeneLikeType, isKnownNonCoding, } from './codingFeature';
4
+ function feature(type, subfeatures = []) {
5
+ return new SimpleFeature({
6
+ uniqueId: `${type}-${Math.random()}`,
7
+ refName: 'chr1',
8
+ start: 0,
9
+ end: 100,
10
+ type,
11
+ subfeatures: subfeatures.map(s => s.toJSON()),
12
+ });
13
+ }
14
+ describe('isGeneLikeType', () => {
15
+ it('accepts the SO spellings of gene, transcript and RNA', () => {
16
+ for (const t of [
17
+ 'gene',
18
+ 'mRNA',
19
+ 'transcript',
20
+ 'ncRNA_gene',
21
+ 'protein_coding_gene',
22
+ 'V_gene_segment',
23
+ 'primary_transcript',
24
+ 'pseudogenic_transcript',
25
+ 'lnc_RNA',
26
+ ]) {
27
+ expect(isGeneLikeType(t)).toBe(true);
28
+ }
29
+ });
30
+ it('rejects regions, matches and CDS', () => {
31
+ for (const t of ['intergenic_region', 'cDNA_match', 'CDS', 'exon']) {
32
+ expect(isGeneLikeType(t)).toBe(false);
33
+ }
34
+ expect(isGeneLikeType(undefined)).toBe(false);
35
+ });
36
+ });
37
+ describe('isCodingFeature', () => {
38
+ it('finds a CDS anywhere below the feature', () => {
39
+ const gene = feature('gene', [
40
+ feature('mRNA', [feature('exon'), feature('CDS')]),
41
+ ]);
42
+ expect(isCodingFeature(gene)).toBe(true);
43
+ });
44
+ it('is false for a transcript with exons only', () => {
45
+ const lnc = feature('lnc_RNA', [feature('exon'), feature('exon')]);
46
+ expect(isCodingFeature(lnc)).toBe(false);
47
+ });
48
+ it('counts the feature itself', () => {
49
+ expect(isCodingFeature(feature('CDS'))).toBe(true);
50
+ });
51
+ });
52
+ describe('isKnownNonCoding', () => {
53
+ it('is true for a transcript whose subfeatures are all exons', () => {
54
+ expect(isKnownNonCoding(feature('lnc_RNA', [feature('exon'), feature('exon')]))).toBe(true);
55
+ });
56
+ // a host is free to hand over a bare record, and reading that as "no protein
57
+ // here" takes the menu item off an ordinary gene without a word
58
+ it('is false for a feature that arrived with no subfeatures at all', () => {
59
+ expect(isKnownNonCoding(feature('gene'))).toBe(false);
60
+ });
61
+ it('is false when a CDS is somewhere below', () => {
62
+ expect(isKnownNonCoding(feature('gene', [feature('mRNA', [feature('CDS')])]))).toBe(false);
63
+ });
64
+ });
65
+ describe('geneLikeRoot', () => {
66
+ it('climbs from an isoform to its gene', () => {
67
+ const gene = feature('gene', [feature('mRNA', [feature('CDS')])]);
68
+ const mrna = gene.get('subfeatures')[0];
69
+ expect(geneLikeRoot(mrna)).toBe(gene);
70
+ });
71
+ it('stays put with no gene-like parent', () => {
72
+ const mrna = feature('mRNA', [feature('CDS')]);
73
+ expect(geneLikeRoot(mrna)).toBe(mrna);
74
+ });
75
+ });
@@ -1,9 +1,11 @@
1
1
  import React from 'react';
2
2
  import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
- declare const BlastAutomaticPanel: ({ handleClose, feature, model, children, }: {
3
+ declare const BlastAutomaticPanel: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
4
4
  model: AbstractTrackModel;
5
5
  feature: Feature;
6
6
  handleClose: () => void;
7
7
  children: React.ReactNode;
8
+ /** the isoform the user right-clicked, preselected in the picker */
9
+ preferredTranscriptId?: string;
8
10
  }) => React.JSX.Element;
9
11
  export default BlastAutomaticPanel;
@@ -6,24 +6,24 @@ import { makeStyles } from 'tss-react/mui';
6
6
  import TextField2 from '../../../components/TextField2';
7
7
  import { getBlastViewTitle, getGeneIdentifiers, getLinearGenomeView, } from '../../util';
8
8
  import LaunchPanelContent from '../LaunchPanelContent';
9
+ import SequenceStatusMessage from '../SequenceStatus';
9
10
  import SubmitCancelActions from '../SubmitCancelActions';
10
11
  import TranscriptSelector from '../TranscriptSelector';
12
+ import { builtAlignmentLook, launchConnectedView, useLaunchSubmit, } from '../launchConnectedView';
11
13
  import { useTranscriptSelection } from '../useTranscriptSelection';
12
14
  import CachedBlastResults from './CachedBlastResults';
13
15
  import MsaAlgorithmSelect from './MsaAlgorithmSelect';
14
- import { blastLaunchView } from './blastLaunchView';
15
- import { databaseOptionsFor, defaultMaxHits, defaultSearchFor, searchPrograms, } from './consts';
16
+ import { databaseLabel, databaseOptionsFor, defaultMaxHits, defaultSearchFor, searchProgramLabels, searchPrograms, } from './consts';
16
17
  import { useStoredMsaAlgorithm, useStoredSearchChoice, } from './searchChoiceStorage';
17
18
  import { useCachedBlastResults } from './useCachedBlastResults';
18
19
  const useStyles = makeStyles()({
19
20
  selectField: {
20
21
  width: 150,
21
22
  },
22
- // wider than the rest because the values are what the user came to read, and
23
- // `uniprotkb_swissprot` is 19 characters — at 150 the field showed
24
- // `uniprotkb_swis…`, which does not distinguish it from `uniprotkb_trembl`
23
+ // wider than the rest because the collection names are what the user came to
24
+ // read, and truncating them does not distinguish Swiss-Prot from TrEMBL
25
25
  databaseField: {
26
- width: 230,
26
+ width: 260,
27
27
  },
28
28
  cachedResultsAccordion: {
29
29
  marginTop: 20,
@@ -32,10 +32,10 @@ const useStyles = makeStyles()({
32
32
  marginTop: 20,
33
33
  },
34
34
  });
35
- const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, children, }) {
35
+ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, children, preferredTranscriptId, }) {
36
36
  const { classes } = useStyles();
37
37
  const view = getLinearGenomeView(model);
38
- const [launchViewError, setLaunchViewError] = useState();
38
+ const { launchError, submit } = useLaunchSubmit(handleClose);
39
39
  // one piece of state, not two: a program and a database that program does not
40
40
  // have is a 400 from EBI minutes after Submit, and holding them apart is what
41
41
  // would let them drift into that
@@ -47,9 +47,13 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
47
47
  const isPhmmer = search.program === 'phmmer';
48
48
  const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature]);
49
49
  const { results: cachedResults, error: cachedResultsError } = useCachedBlastResults(geneIds);
50
- const transcriptSelection = useTranscriptSelection({ feature, view });
51
- const { selectedTranscript, proteinSequence } = transcriptSelection;
52
- const e = transcriptSelection.error ?? launchViewError ?? cachedResultsError;
50
+ const transcriptSelection = useTranscriptSelection({
51
+ feature,
52
+ view,
53
+ preferredTranscriptId,
54
+ });
55
+ const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
56
+ const e = transcriptSelection.error ?? launchError ?? cachedResultsError;
53
57
  return (React.createElement(React.Fragment, null,
54
58
  React.createElement(LaunchPanelContent, { error: e },
55
59
  children,
@@ -58,69 +62,57 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
58
62
  // program replaces the database rather than keeping a name the new
59
63
  // one has never heard of
60
64
  setSearch(defaultSearchFor(event.target.value));
61
- } }, searchPrograms.map(val => (React.createElement(MenuItem, { value: val, key: val }, val)))),
65
+ } }, searchPrograms.map(val => (React.createElement(MenuItem, { value: val, key: val }, searchProgramLabels[val])))),
62
66
  React.createElement(TextField2, { variant: "outlined", label: "Database", className: classes.databaseField, select: true, value: search.database, onChange: event => {
63
67
  setSearch({
64
68
  program: search.program,
65
69
  database: event.target.value,
66
70
  });
67
- } }, databaseOptionsFor(search.program).map(val => (React.createElement(MenuItem, { value: val, key: val }, val)))),
71
+ } }, databaseOptionsFor(search.program).map(val => (React.createElement(MenuItem, { value: val, key: val }, databaseLabel(val))))),
68
72
  isPhmmer ? null : (React.createElement(MsaAlgorithmSelect, { className: classes.selectField, value: selectedMsaAlgorithm, onChange: setSelectedMsaAlgorithm })),
69
73
  React.createElement(TextField2, { variant: "outlined", label: "Hits", className: classes.selectField, type: "number", value: maxHits, onChange: event => {
70
74
  setMaxHits(event.target.value);
71
75
  }, error: !hitCountValid, helperText: "best-scoring sequences to keep" }),
72
76
  React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
73
- React.createElement(Typography, { className: classes.infoText },
77
+ React.createElement(Typography, { variant: "body2", className: classes.infoText },
74
78
  isPhmmer
75
- ? `phmmer searches UniProtKB with a profile HMM built from the query,
76
- so it aligns the hits as it finds them and that alignment is used
77
- directly — nothing is realigned afterwards. The tree is then built
78
- from it in the browser. A hit matching the query in more than one
79
- place appears once per matched region. rp15 spreads the hits
80
- across all of life; swissprot keeps to the curated set.`
81
- : `This panel will automatically submit a blastp query to EBI, which
82
- searches UniProtKB. swissprot returns curated sequences that align
83
- more cleanly than the many near-identical entries a TrEMBL search
84
- brings back. After completion, all the hits will be run through
85
- the chosen aligner; "in browser" needs no second EBI job.`,
79
+ ? 'phmmer aligns the hits as it finds them, so nothing is realigned afterwards.'
80
+ : 'The hits come back from EBI and are then run through the chosen aligner.',
86
81
  ' ',
87
- "EBI's queue is the wait, and it varies from seconds to many minutes. For a homolog panel with no job at all, the Orthologs tab's UniRef source is a lookup. Searching NCBI's nr needs the manual approach: NCBI no longer lets a browser read responses from Blast.cgi."),
82
+ "The EBI queue is the wait, and it runs from seconds to many minutes."),
88
83
  cachedResults.length > 0 ? (React.createElement(Accordion, { className: classes.cachedResultsAccordion },
89
84
  React.createElement(AccordionSummary, { expandIcon: React.createElement(ExpandMoreIcon, null) },
90
85
  React.createElement(Typography, null, "Previous BLAST Results")),
91
86
  React.createElement(AccordionDetails, null,
92
87
  React.createElement(CachedBlastResults, { model: model, handleClose: handleClose, feature: feature })))) : null),
93
- React.createElement(SubmitCancelActions, { model: model, submitDisabled: !proteinSequence || !hitCountValid, onSubmit: () => {
94
- try {
95
- if (selectedTranscript) {
96
- setLaunchViewError(undefined);
97
- blastLaunchView({
98
- feature: selectedTranscript,
88
+ React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !hitCountValid, onSubmit: placement => {
89
+ if (selectedTranscript) {
90
+ const blastParams = search.program === 'phmmer'
91
+ ? {
92
+ searchProgram: 'phmmer',
93
+ blastDatabase: search.database,
94
+ maxHits: hitCount,
95
+ selectedTranscript: selectedTranscript.toJSON(),
96
+ proteinSequence,
97
+ }
98
+ : {
99
+ searchProgram: 'blastp',
100
+ blastDatabase: search.database,
101
+ msaAlgorithm: selectedMsaAlgorithm,
102
+ maxHits: hitCount,
103
+ selectedTranscript: selectedTranscript.toJSON(),
104
+ proteinSequence,
105
+ };
106
+ submit(() => {
107
+ launchConnectedView({
99
108
  view,
100
- newViewTitle: getBlastViewTitle(feature, selectedTranscript),
101
- blastParams: search.program === 'phmmer'
102
- ? {
103
- searchProgram: 'phmmer',
104
- blastDatabase: search.database,
105
- maxHits: hitCount,
106
- selectedTranscript: selectedTranscript.toJSON(),
107
- proteinSequence,
108
- }
109
- : {
110
- searchProgram: 'blastp',
111
- blastDatabase: search.database,
112
- msaAlgorithm: selectedMsaAlgorithm,
113
- maxHits: hitCount,
114
- selectedTranscript: selectedTranscript.toJSON(),
115
- proteinSequence,
116
- },
109
+ feature: selectedTranscript,
110
+ placement,
111
+ displayName: getBlastViewTitle(feature, selectedTranscript),
112
+ ...builtAlignmentLook,
113
+ blastParams,
117
114
  });
118
- handleClose();
119
- }
120
- }
121
- catch (e) {
122
- console.error(e);
123
- setLaunchViewError(e);
115
+ });
124
116
  }
125
117
  }, onCancel: handleClose })));
126
118
  });
@@ -11,10 +11,12 @@ import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
11
11
  * button -- leaving them to find the Manual upload tab, re-pick the transcript
12
12
  * they had already chosen here, and hand-type the row name.
13
13
  */
14
- declare const BlastManualPanel: ({ handleClose, feature, model, children, }: {
14
+ declare const BlastManualPanel: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
15
15
  children: React.ReactNode;
16
16
  model: AbstractTrackModel;
17
17
  feature: Feature;
18
18
  handleClose: () => void;
19
+ /** the isoform the user right-clicked, preselected in the picker */
20
+ preferredTranscriptId?: string;
19
21
  }) => React.JSX.Element;
20
22
  export default BlastManualPanel;
@@ -8,10 +8,10 @@ import TextField2 from '../../../components/TextField2';
8
8
  import { useQueryRowName } from '../../useQueryRowName';
9
9
  import { cleanProteinSequence, getGeneDisplayName, getLinearGenomeView, } from '../../util';
10
10
  import LaunchPanelContent from '../LaunchPanelContent';
11
- import { launchView } from '../ManualMSALoader/launchView';
12
11
  import QueryRowSelector from '../QueryRowSelector';
13
12
  import SubmitCancelActions from '../SubmitCancelActions';
14
13
  import TranscriptSelector from '../TranscriptSelector';
14
+ import { launchConnectedView, useLaunchSubmit } from '../launchConnectedView';
15
15
  import { useTranscriptSelection } from '../useTranscriptSelection';
16
16
  import { BASE_BLAST_URL } from './consts';
17
17
  const useStyles = makeStyles()({
@@ -43,13 +43,17 @@ const useStyles = makeStyles()({
43
43
  * button -- leaving them to find the Manual upload tab, re-pick the transcript
44
44
  * they had already chosen here, and hand-type the row name.
45
45
  */
46
- const BlastManualPanel = observer(function ({ handleClose, feature, model, children, }) {
46
+ const BlastManualPanel = observer(function ({ handleClose, feature, model, children, preferredTranscriptId, }) {
47
47
  const { classes } = useStyles();
48
48
  const view = getLinearGenomeView(model);
49
- const [launchViewError, setLaunchViewError] = useState();
49
+ const { launchError, submit } = useLaunchSubmit(handleClose);
50
50
  const [msaText, setMsaText] = useState('');
51
51
  const [treeText, setTreeText] = useState('');
52
- const transcriptSelection = useTranscriptSelection({ feature, view });
52
+ const transcriptSelection = useTranscriptSelection({
53
+ feature,
54
+ view,
55
+ preferredTranscriptId,
56
+ });
53
57
  const { proteinSequence, selectedTranscript, error } = transcriptSelection;
54
58
  const queryRow = useQueryRowName(msaText, proteinSequence);
55
59
  const s2 = cleanProteinSequence(proteinSequence);
@@ -58,7 +62,7 @@ const BlastManualPanel = observer(function ({ handleClose, feature, model, child
58
62
  const link = `${BASE_BLAST_URL}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${s2}`;
59
63
  const link2 = `${BASE_BLAST_URL}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${shorten2(s2, 10)}`;
60
64
  return (React.createElement(React.Fragment, null,
61
- React.createElement(LaunchPanelContent, { error: launchViewError ?? error },
65
+ React.createElement(LaunchPanelContent, { error: launchError ?? error },
62
66
  children,
63
67
  React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
64
68
  React.createElement("div", { className: classes.step },
@@ -79,24 +83,19 @@ const BlastManualPanel = observer(function ({ handleClose, feature, model, child
79
83
  setTreeText(event.target.value);
80
84
  } }),
81
85
  React.createElement(QueryRowSelector, { ...queryRow })))),
82
- React.createElement(SubmitCancelActions, { model: model, submitDisabled: !selectedTranscript || !msaText.trim(), onSubmit: () => {
83
- try {
84
- if (selectedTranscript) {
85
- setLaunchViewError(undefined);
86
- launchView({
87
- newViewTitle: getGeneDisplayName(selectedTranscript),
86
+ React.createElement(SubmitCancelActions, { model: model, submitDisabled: !selectedTranscript || !msaText.trim(), onSubmit: placement => {
87
+ if (selectedTranscript) {
88
+ submit(() => {
89
+ launchConnectedView({
88
90
  view,
89
91
  feature: selectedTranscript,
92
+ placement,
93
+ displayName: getGeneDisplayName(selectedTranscript),
90
94
  querySeqName: queryRow.querySeqName,
91
95
  querySeqOffset: queryRow.querySeqOffset,
92
96
  data: { msa: msaText, tree: treeText },
93
97
  });
94
- handleClose();
95
- }
96
- }
97
- catch (e) {
98
- console.error(e);
99
- setLaunchViewError(e);
98
+ });
100
99
  }
101
100
  }, onCancel: handleClose })));
102
101
  });
@@ -1,23 +1,27 @@
1
1
  import React from 'react';
2
2
  import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
3
  declare const panelMap: {
4
- readonly automatic: ({ handleClose, feature, model, children, }: {
4
+ readonly automatic: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
5
5
  model: AbstractTrackModel;
6
6
  feature: Feature;
7
7
  handleClose: () => void;
8
8
  children: React.ReactNode;
9
+ preferredTranscriptId?: string;
9
10
  }) => React.JSX.Element;
10
- readonly manual: ({ handleClose, feature, model, children, }: {
11
+ readonly manual: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
11
12
  children: React.ReactNode;
12
13
  model: AbstractTrackModel;
13
14
  feature: Feature;
14
15
  handleClose: () => void;
16
+ preferredTranscriptId?: string;
15
17
  }) => React.JSX.Element;
16
18
  };
17
19
  export type BlastLookupMethod = keyof typeof panelMap;
18
- export default function BlastPanel({ handleClose, model, feature, }: {
20
+ export default function BlastPanel({ handleClose, model, feature, preferredTranscriptId, }: {
19
21
  handleClose: () => void;
20
22
  model: AbstractTrackModel;
21
23
  feature: Feature;
24
+ /** the isoform the user right-clicked, preselected in the picker */
25
+ preferredTranscriptId?: string;
22
26
  }): React.JSX.Element;
23
27
  export {};
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from 'react';
2
2
  import SettingsIcon from '@mui/icons-material/Settings';
3
- import { IconButton } from '@mui/material';
3
+ import { IconButton, Tooltip } from '@mui/material';
4
4
  import { makeStyles } from 'tss-react/mui';
5
5
  import { DEFAULT_EBI_EMAIL, EBI_EMAIL_STORAGE_KEY, } from '../../../utils/ebiJobDispatcher';
6
6
  import { useLocalStorage } from '../../../utils/useLocalStorage';
@@ -17,18 +17,19 @@ const panelMap = {
17
17
  automatic: BlastAutomaticPanel,
18
18
  manual: BlastManualPanel,
19
19
  };
20
- export default function BlastPanel({ handleClose, model, feature, }) {
20
+ export default function BlastPanel({ handleClose, model, feature, preferredTranscriptId, }) {
21
21
  const [lookupMethod, setLookupMethod] = useState('automatic');
22
22
  const [ebiEmail, setEbiEmail] = useLocalStorage(EBI_EMAIL_STORAGE_KEY, DEFAULT_EBI_EMAIL);
23
23
  const [settingsOpen, setSettingsOpen] = useState(false);
24
24
  const { classes } = useStyles();
25
25
  const Panel = panelMap[lookupMethod];
26
26
  return (React.createElement(React.Fragment, null,
27
- React.createElement(IconButton, { className: classes.settingsButton, size: "small", onClick: () => {
28
- setSettingsOpen(true);
29
- } },
30
- React.createElement(SettingsIcon, null)),
31
- React.createElement(Panel, { model: model, feature: feature, handleClose: handleClose },
27
+ React.createElement(Tooltip, { title: `EBI settings — jobs are submitted with ${ebiEmail}` },
28
+ React.createElement(IconButton, { className: classes.settingsButton, "aria-label": "EBI settings", size: "small", onClick: () => {
29
+ setSettingsOpen(true);
30
+ } },
31
+ React.createElement(SettingsIcon, null))),
32
+ React.createElement(Panel, { model: model, feature: feature, handleClose: handleClose, preferredTranscriptId: preferredTranscriptId },
32
33
  React.createElement(BlastMethodSelector, { lookupMethod: lookupMethod, setLookupMethod: setLookupMethod })),
33
34
  settingsOpen ? (React.createElement(BlastSettingsDialog, { ebiEmail: ebiEmail, handleClose: settings => {
34
35
  if (settings) {