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
@@ -17,9 +17,9 @@ export default function BlastSettingsDialog({ handleClose, ebiEmail, }) {
17
17
  return (React.createElement(Dialog, { open: true, maxWidth: "lg", onClose: () => {
18
18
  handleClose();
19
19
  } },
20
- React.createElement(DialogTitle, null, "BLAST Settings"),
20
+ React.createElement(DialogTitle, null, "EBI settings"),
21
21
  React.createElement(DialogContent, null,
22
- React.createElement(Typography, { variant: "subtitle2", className: classes.help }, "Searches run at EBI, which asks for a contact address on every job so they can reach whoever is generating the load. If your site sends real volume, use your own."),
22
+ React.createElement(Typography, { variant: "subtitle2", className: classes.help }, "Every search this tab runs is submitted to EBI with this address, which is how they reach whoever is generating the load. If your site sends real volume, use your own."),
23
23
  React.createElement(TextField2, { autoFocus: true, margin: "dense", label: "EBI contact email", fullWidth: true, variant: "outlined", value: tempEbiEmail, className: classes.field, onChange: e => {
24
24
  setTempEbiEmail(e.target.value);
25
25
  } }),
@@ -5,7 +5,8 @@ import { Button, IconButton, List, ListItem, ListItemButton, ListItemText, Typog
5
5
  import { observer } from 'mobx-react';
6
6
  import { makeStyles } from 'tss-react/mui';
7
7
  import { featureMatchesId, getGeneIdentifiers, getLinearGenomeView, getSortedTranscriptFeatures, } from '../../util';
8
- import { blastLaunchViewFromCache } from './blastLaunchView';
8
+ import { builtAlignmentLook, launchConnectedView } from '../launchConnectedView';
9
+ import { useLaunchPlacement } from '../launchPlacement';
9
10
  import { useCachedBlastResults } from './useCachedBlastResults';
10
11
  const useStyles = makeStyles()({
11
12
  header: {
@@ -53,22 +54,22 @@ const CachedBlastResults = observer(function ({ model, handleClose, feature, })
53
54
  const { classes } = useStyles();
54
55
  const view = getLinearGenomeView(model);
55
56
  const [operationError, setOperationError] = useState();
57
+ const [sideBySide] = useLaunchPlacement();
56
58
  const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature]);
57
59
  const { results, error, isLoading, handleDelete, handleClearAll } = useCachedBlastResults(geneIds);
58
60
  const handleUseCached = (cached) => {
59
- // reconnect the cached MSA to the genome: the cached query row is named
60
- // 'QUERY' (react-msaview's default querySeqName) and corresponds to the
61
- // transcript stored as transcriptId. Resolving it here restores the
62
- // MSA<->genome navigation and hover-sync a fresh BLAST gets.
63
- const { transcriptId } = cached;
64
- const transcript = transcriptId
65
- ? getSortedTranscriptFeatures(feature).find(t => featureMatchesId(t, transcriptId))
66
- : undefined;
67
- blastLaunchViewFromCache({
61
+ // the cached query row is the plugin's default `QUERY`, translated from
62
+ // the transcript stored as transcriptId, so that transcript relinks it
63
+ const { transcriptId, msa, tree, treeMetadata } = cached;
64
+ launchConnectedView({
68
65
  view,
69
- cached,
70
- newViewTitle: `BLAST - ${getResultDisplayName(cached)}`,
71
- connectedFeature: transcript?.toJSON(),
66
+ feature: transcriptId
67
+ ? getSortedTranscriptFeatures(feature).find(t => featureMatchesId(t, transcriptId))
68
+ : undefined,
69
+ placement: sideBySide ? 'splitRight' : 'stack',
70
+ displayName: `BLAST - ${getResultDisplayName(cached)}`,
71
+ ...builtAlignmentLook,
72
+ data: { msa, tree, treeMetadata },
72
73
  });
73
74
  handleClose();
74
75
  };
@@ -31,6 +31,7 @@ export type BlastDatabase = (typeof blastDatabaseOptions)[number];
31
31
  export declare const defaultBlastDatabase: BlastDatabase;
32
32
  export declare const searchPrograms: readonly ["blastp", "phmmer"];
33
33
  export type SearchProgram = (typeof searchPrograms)[number];
34
+ export declare const searchProgramLabels: Record<SearchProgram, string>;
34
35
  /**
35
36
  * phmmer offers PDB, AlphaFold, Ensembl Genomes, MEROPS and ChEMBL too, but
36
37
  * targets outside UniProt carry no species in their description, so those rows
@@ -65,3 +66,4 @@ export type SearchChoice = {
65
66
  };
66
67
  export declare function defaultSearchFor(program: SearchProgram): SearchChoice;
67
68
  export declare function databaseOptionsFor(program: SearchProgram): readonly ["uniprotkb_swissprot", "uniprotkb", "pan_proteomes", "uniprotkb_trembl"] | readonly ["swissprot", "uniprotkb", "uniprotrefprot", "rp75", "rp55", "rp35", "rp15"];
69
+ export declare function databaseLabel(database: BlastDatabase | PhmmerDatabase): string;
@@ -22,10 +22,10 @@ export const ebiMsaAlgorithms = [
22
22
  */
23
23
  export const msaAlgorithms = [...ebiMsaAlgorithms, 'browser'];
24
24
  export const msaAlgorithmLabels = {
25
- clustalo: 'clustalo (EBI)',
26
- muscle: 'muscle (EBI)',
27
- kalign: 'kalign (EBI)',
28
- mafft: 'mafft (EBI)',
25
+ clustalo: 'Clustal Omega (EBI)',
26
+ muscle: 'MUSCLE (EBI)',
27
+ kalign: 'Kalign (EBI)',
28
+ mafft: 'MAFFT (EBI)',
29
29
  browser: 'in browser, query-anchored',
30
30
  };
31
31
  /**
@@ -45,6 +45,10 @@ export const blastDatabaseOptions = [
45
45
  // many near-identical TrEMBL entries an alignment reads poorly
46
46
  export const defaultBlastDatabase = 'uniprotkb_swissprot';
47
47
  export const searchPrograms = ['blastp', 'phmmer'];
48
+ export const searchProgramLabels = {
49
+ blastp: 'blastp (sequence search)',
50
+ phmmer: 'phmmer (profile HMM search)',
51
+ };
48
52
  /**
49
53
  * phmmer offers PDB, AlphaFold, Ensembl Genomes, MEROPS and ChEMBL too, but
50
54
  * targets outside UniProt carry no species in their description, so those rows
@@ -85,3 +89,23 @@ export function defaultSearchFor(program) {
85
89
  export function databaseOptionsFor(program) {
86
90
  return program === 'phmmer' ? phmmerDatabaseOptions : blastDatabaseOptions;
87
91
  }
92
+ /**
93
+ * What each database is called in the menu. The keys are what EBI is sent and
94
+ * are not negotiable; `uniprotkb_swissprot` and `swissprot` are the same
95
+ * collection under each service's own name, so they read the same here.
96
+ */
97
+ const databaseLabels = {
98
+ uniprotkb_swissprot: 'UniProtKB/Swiss-Prot (curated)',
99
+ swissprot: 'UniProtKB/Swiss-Prot (curated)',
100
+ uniprotkb: 'UniProtKB (all entries)',
101
+ uniprotkb_trembl: 'UniProtKB/TrEMBL (unreviewed)',
102
+ pan_proteomes: 'Pan-proteomes',
103
+ uniprotrefprot: 'UniProt reference proteomes',
104
+ rp75: 'Representative proteomes, 75%',
105
+ rp55: 'Representative proteomes, 55%',
106
+ rp35: 'Representative proteomes, 35%',
107
+ rp15: 'Representative proteomes, 15% (widest spread)',
108
+ };
109
+ export function databaseLabel(database) {
110
+ return databaseLabels[database];
111
+ }
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export default function HelpButton(): React.JSX.Element;
@@ -0,0 +1,17 @@
1
+ import React, { Suspense, lazy, useState } from 'react';
2
+ import Help from '@mui/icons-material/Help';
3
+ import { IconButton, Tooltip } from '@mui/material';
4
+ const HelpDialog = lazy(() => import('./HelpDialog'));
5
+ export default function HelpButton() {
6
+ const [show, setShow] = useState(false);
7
+ return (React.createElement(React.Fragment, null,
8
+ React.createElement(Tooltip, { title: "What each tab does" },
9
+ React.createElement(IconButton, { "aria-label": "Help", onClick: () => {
10
+ setShow(true);
11
+ } },
12
+ React.createElement(Help, null))),
13
+ show ? (React.createElement(Suspense, { fallback: null },
14
+ React.createElement(HelpDialog, { handleClose: () => {
15
+ setShow(false);
16
+ } }))) : null));
17
+ }
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export default function HelpDialog({ handleClose, }: {
3
+ handleClose: () => void;
4
+ }): React.JSX.Element;
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { Dialog } from '@jbrowse/core/ui';
3
+ import { Button, DialogActions, DialogContent, Divider, Typography, } from '@mui/material';
4
+ export default function HelpDialog({ handleClose, }) {
5
+ return (React.createElement(Dialog, { open: true, maxWidth: "md", onClose: handleClose, title: "Launching an MSA" },
6
+ React.createElement(DialogContent, null,
7
+ React.createElement(Typography, { gutterBottom: true }, "Every tab aligns the same thing: the protein the selected transcript translates to. That transcript is the query row, which is what ties alignment columns back to codons in the genome view \u2014 hovering one highlights the other, and clicking navigates."),
8
+ React.createElement(Typography, { variant: "h6", gutterBottom: true }, "Orthologs"),
9
+ React.createElement(Typography, { gutterBottom: true }, "Precomputed sets, looked up rather than searched for: NCBI and PANTHER give one gene per species, and a UniRef cluster gives every UniProtKB entry within 50% identity of the query, one per species, from any organism. Nothing is queued, so this is the quickest route to \"this gene across species\"."),
10
+ React.createElement(Typography, { variant: "h6", gutterBottom: true }, "BLAST query"),
11
+ React.createElement(Typography, { gutterBottom: true }, "Searches run at EBI's Job Dispatcher, which searches UniProtKB. Swiss-Prot returns curated sequences that align more cleanly than the many near-identical entries a TrEMBL search brings back. blastp finds the hits and the chosen aligner then aligns them \u2014 \"in browser\" needs no second EBI job. phmmer instead searches with a profile HMM built from the query and aligns as it goes, so its output is the alignment and nothing is realigned; a hit matching the query in more than one place appears once per matched region. The Representative Proteomes (15% to 75%) spread the hits across all of life."),
12
+ React.createElement(Typography, { gutterBottom: true }, "The EBI queue is the wait, and it runs from seconds to many minutes. Searching NCBI's nr needs the Manual option, which links out to NCBI's own site: NCBI no longer lets a browser read responses from Blast.cgi."),
13
+ React.createElement(Typography, { variant: "h6", gutterBottom: true }, "Pre-loaded MSA datasets and Manual upload"),
14
+ React.createElement(Typography, { gutterBottom: true }, "Both take an alignment that already exists \u2014 one a dataset the site configured, the other a file or pasted text. In each case the row matching the selected transcript is found by comparing residues, not by name, because aligners rename the query on the way through.")),
15
+ React.createElement(Divider, null),
16
+ React.createElement(DialogActions, null,
17
+ React.createElement(Button, { onClick: handleClose, color: "primary" }, "Close"))));
18
+ }
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
2
  import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
- export default function LaunchMsaViewDialog({ handleClose, feature, model, }: {
3
+ export default function LaunchMsaViewDialog({ handleClose, feature, model, preferredTranscriptId, }: {
4
4
  handleClose: () => void;
5
5
  feature: Feature;
6
6
  model: AbstractTrackModel;
7
+ /** the isoform the right-click landed on, when this opened on its gene */
8
+ preferredTranscriptId?: string;
7
9
  }): React.JSX.Element;
@@ -3,12 +3,14 @@ import { Dialog } from '@jbrowse/core/ui';
3
3
  import { getSession } from '@jbrowse/core/util';
4
4
  import { Tab, Tabs } from '@mui/material';
5
5
  import BlastPanel from './BlastQuery/BlastPanel';
6
+ import HelpButton from './HelpButton';
6
7
  import ManualMSALoader from './ManualMSALoader/ManualMSALoader';
7
8
  import OrthologPanel from './OrthologQuery/OrthologPanel';
8
9
  import PreLoadedMSA from './PreLoadedMSA/PreLoadedMSADataPanel';
9
10
  import { readMsaDatasets } from './PreLoadedMSA/types';
10
11
  import TabPanel from './TabPanel';
11
- export default function LaunchMsaViewDialog({ handleClose, feature, model, }) {
12
+ import { LaunchPlacementProvider } from './launchPlacement';
13
+ export default function LaunchMsaViewDialog({ handleClose, feature, model, preferredTranscriptId, }) {
12
14
  const session = getSession(model);
13
15
  const datasets = readMsaDatasets(session.jbrowse);
14
16
  const hasPreloadedDatasets = !!datasets?.length;
@@ -16,19 +18,22 @@ export default function LaunchMsaViewDialog({ handleClose, feature, model, }) {
16
18
  // that BLAST takes 10+ minutes to answer worse (see utils/ncbiOrthologs.ts)
17
19
  const [value, setValue] = useState('orthologs');
18
20
  return (React.createElement(Dialog, { maxWidth: "xl", title: "Launch MSA view", open: true, onClose: handleClose },
19
- React.createElement(Tabs, { value: value, onChange: (_event, newValue) => {
20
- setValue(newValue);
21
- } },
22
- React.createElement(Tab, { label: "Orthologs (fast)", value: "orthologs" }),
23
- React.createElement(Tab, { label: "BLAST query", value: "ncbi_blast" }),
24
- hasPreloadedDatasets ? (React.createElement(Tab, { label: "Pre-loaded MSA datasets", value: "preloaded_msa" })) : null,
25
- React.createElement(Tab, { label: "Manual upload", value: "manual_msa" })),
26
- React.createElement(TabPanel, { value: value, index: "orthologs" },
27
- React.createElement(OrthologPanel, { handleClose: handleClose, feature: feature, model: model })),
28
- React.createElement(TabPanel, { value: value, index: "ncbi_blast" },
29
- React.createElement(BlastPanel, { handleClose: handleClose, feature: feature, model: model })),
30
- hasPreloadedDatasets ? (React.createElement(TabPanel, { value: value, index: "preloaded_msa" },
31
- React.createElement(PreLoadedMSA, { model: model, feature: feature, handleClose: handleClose }))) : null,
32
- React.createElement(TabPanel, { value: value, index: "manual_msa" },
33
- React.createElement(ManualMSALoader, { model: model, feature: feature, handleClose: handleClose }))));
21
+ React.createElement(LaunchPlacementProvider, null,
22
+ React.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
23
+ React.createElement(Tabs, { value: value, onChange: (_event, newValue) => {
24
+ setValue(newValue);
25
+ } },
26
+ React.createElement(Tab, { label: "Orthologs", value: "orthologs" }),
27
+ React.createElement(Tab, { label: "BLAST query", value: "ncbi_blast" }),
28
+ hasPreloadedDatasets ? (React.createElement(Tab, { label: "Pre-loaded MSA datasets", value: "preloaded_msa" })) : null,
29
+ React.createElement(Tab, { label: "Manual upload", value: "manual_msa" })),
30
+ React.createElement(HelpButton, null)),
31
+ React.createElement(TabPanel, { value: value, index: "orthologs" },
32
+ React.createElement(OrthologPanel, { handleClose: handleClose, feature: feature, model: model, preferredTranscriptId: preferredTranscriptId })),
33
+ React.createElement(TabPanel, { value: value, index: "ncbi_blast" },
34
+ React.createElement(BlastPanel, { handleClose: handleClose, feature: feature, model: model, preferredTranscriptId: preferredTranscriptId })),
35
+ hasPreloadedDatasets ? (React.createElement(TabPanel, { value: value, index: "preloaded_msa" },
36
+ React.createElement(PreLoadedMSA, { model: model, feature: feature, handleClose: handleClose, preferredTranscriptId: preferredTranscriptId }))) : null,
37
+ React.createElement(TabPanel, { value: value, index: "manual_msa" },
38
+ React.createElement(ManualMSALoader, { model: model, feature: feature, handleClose: handleClose, preferredTranscriptId: preferredTranscriptId })))));
34
39
  }
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
- declare const ManualMSALoader: ({ model, feature, handleClose, }: {
3
+ declare const ManualMSALoader: ({ model, feature, handleClose, preferredTranscriptId, }: {
4
4
  model: AbstractTrackModel;
5
5
  feature: Feature;
6
6
  handleClose: () => void;
7
+ /** the isoform the user right-clicked, preselected in the picker */
8
+ preferredTranscriptId?: string;
7
9
  }) => React.JSX.Element;
8
10
  export default ManualMSALoader;
@@ -1,17 +1,30 @@
1
1
  import React, { useState } from 'react';
2
2
  import { FileSelector } from '@jbrowse/core/ui';
3
+ import { openLocation } from '@jbrowse/core/util/io';
3
4
  import { FormControl, FormControlLabel, Radio, RadioGroup } from '@mui/material';
4
5
  import { observer } from 'mobx-react';
5
6
  import { makeStyles } from 'tss-react/mui';
6
7
  import TextField2 from '../../../components/TextField2';
8
+ import { useDebounced, useFetch } from '../../../utils/useFetch';
7
9
  import { useQueryRowName } from '../../useQueryRowName';
8
10
  import { getGeneDisplayName, getLinearGenomeView } from '../../util';
9
11
  import LaunchPanelContent from '../LaunchPanelContent';
10
12
  import QueryRowSelector from '../QueryRowSelector';
13
+ import SequenceStatusMessage from '../SequenceStatus';
11
14
  import SubmitCancelActions from '../SubmitCancelActions';
12
15
  import TranscriptSelector from '../TranscriptSelector';
16
+ import { launchConnectedView, useLaunchSubmit } from '../launchConnectedView';
13
17
  import { useTranscriptSelection } from '../useTranscriptSelection';
14
- import { launchView } from './launchView';
18
+ /**
19
+ * The chosen file's text, so its query row is found by sequence the way a
20
+ * pasted alignment's is; without it a file launch named no row and never
21
+ * linked to the genome. Debounced because a URL arrives a keystroke at a time.
22
+ */
23
+ function useMsaFileText(location) {
24
+ const debounced = useDebounced(location, 500);
25
+ const { data } = useFetch(debounced ? [JSON.stringify(debounced), 'msa-file-text'] : null, () => openLocation(debounced).readFile('utf8'));
26
+ return data ?? '';
27
+ }
15
28
  const useStyles = makeStyles()({
16
29
  textAreaFont: {
17
30
  fontFamily: 'Courier New',
@@ -26,19 +39,24 @@ const useStyles = makeStyles()({
26
39
  marginBottom: 20,
27
40
  },
28
41
  });
29
- const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handleClose, }) {
42
+ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handleClose, preferredTranscriptId, }) {
30
43
  const view = getLinearGenomeView(model);
31
44
  const { classes } = useStyles();
32
- const [launchViewError, setLaunchViewError] = useState();
45
+ const { launchError, submit } = useLaunchSubmit(handleClose);
33
46
  const [inputMethod, setInputMethod] = useState('file');
34
47
  const [msaText, setMsaText] = useState('');
35
48
  const [treeText, setTreeText] = useState('');
36
49
  const [msaFileLocation, setMsaFileLocation] = useState();
37
50
  const [treeFileLocation, setTreeFileLocation] = useState();
38
- const transcriptSelection = useTranscriptSelection({ feature, view });
39
- const { selectedTranscript, proteinSequence, error } = transcriptSelection;
40
- const queryRow = useQueryRowName(msaText, proteinSequence);
41
- const e = launchViewError ?? error;
51
+ const transcriptSelection = useTranscriptSelection({
52
+ feature,
53
+ view,
54
+ preferredTranscriptId,
55
+ });
56
+ const { selectedTranscript, proteinSequence, error, sequenceStatus } = transcriptSelection;
57
+ const msaFileText = useMsaFileText(inputMethod === 'file' ? msaFileLocation : undefined);
58
+ const queryRow = useQueryRowName(inputMethod === 'file' ? msaFileText : msaText, proteinSequence);
59
+ const e = launchError ?? error;
42
60
  return (React.createElement(React.Fragment, null,
43
61
  React.createElement(LaunchPanelContent, { error: e },
44
62
  React.createElement(FormControl, { component: "fieldset" },
@@ -58,16 +76,16 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
58
76
  } })))),
59
77
  React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
60
78
  React.createElement(QueryRowSelector, { ...queryRow })),
61
- React.createElement(SubmitCancelActions, { model: model, submitDisabled: !selectedTranscript ||
79
+ React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !selectedTranscript ||
62
80
  (inputMethod === 'file' && !msaFileLocation) ||
63
- (inputMethod === 'text' && !msaText.trim()), onSubmit: () => {
64
- try {
65
- if (selectedTranscript) {
66
- setLaunchViewError(undefined);
67
- launchView({
68
- newViewTitle: getGeneDisplayName(selectedTranscript),
81
+ (inputMethod === 'text' && !msaText.trim()), onSubmit: placement => {
82
+ if (selectedTranscript) {
83
+ submit(() => {
84
+ launchConnectedView({
69
85
  view,
70
86
  feature: selectedTranscript,
87
+ placement,
88
+ displayName: getGeneDisplayName(selectedTranscript),
71
89
  querySeqName: queryRow.querySeqName,
72
90
  querySeqOffset: queryRow.querySeqOffset,
73
91
  ...(inputMethod === 'file'
@@ -75,19 +93,9 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
75
93
  msaFilehandle: msaFileLocation,
76
94
  treeFilehandle: treeFileLocation,
77
95
  }
78
- : {
79
- data: {
80
- msa: msaText,
81
- tree: treeText,
82
- },
83
- }),
96
+ : { data: { msa: msaText, tree: treeText } }),
84
97
  });
85
- handleClose();
86
- }
87
- }
88
- catch (err) {
89
- console.error(err);
90
- setLaunchViewError(err);
98
+ });
91
99
  }
92
100
  }, onCancel: handleClose })));
93
101
  });
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
- declare const OrthologPanel: ({ handleClose, feature, model, }: {
3
+ declare const OrthologPanel: ({ handleClose, feature, model, preferredTranscriptId, }: {
4
4
  model: AbstractTrackModel;
5
5
  feature: Feature;
6
6
  handleClose: () => void;
7
+ /** the isoform the user right-clicked, preselected in the picker */
8
+ preferredTranscriptId?: string;
7
9
  }) => React.JSX.Element;
8
10
  export default OrthologPanel;
@@ -4,39 +4,45 @@ import { observer } from 'mobx-react';
4
4
  import { makeStyles } from 'tss-react/mui';
5
5
  import TextField2 from '../../../components/TextField2';
6
6
  import { defaultMaxSpecies } from '../../../utils/ncbiOrthologs';
7
- import { useLocalStorage } from '../../../utils/useLocalStorage';
8
7
  import { getGeneDisplayName, getGeneIdentifiers, getLinearGenomeView, getTranscriptDisplayName, } from '../../util';
9
8
  import MsaAlgorithmSelect from '../BlastQuery/MsaAlgorithmSelect';
10
9
  import { useStoredMsaAlgorithm } from '../BlastQuery/searchChoiceStorage';
11
10
  import LaunchPanelContent from '../LaunchPanelContent';
11
+ import SequenceStatusMessage from '../SequenceStatus';
12
12
  import SubmitCancelActions from '../SubmitCancelActions';
13
13
  import TranscriptSelector from '../TranscriptSelector';
14
+ import { builtAlignmentLook, launchConnectedView, useLaunchSubmit, } from '../launchConnectedView';
14
15
  import { useTranscriptSelection } from '../useTranscriptSelection';
15
- import OrthologSourceSelect, { ORTHOLOG_SOURCE_STORAGE_KEY, } from './OrthologSourceSelect';
16
+ import OrthologSourceSelect, { useStoredOrthologSource, } from './OrthologSourceSelect';
16
17
  import QuerySpeciesSelect from './QuerySpeciesSelect';
17
- import { orthologLaunchView } from './orthologLaunchView';
18
18
  const useStyles = makeStyles()({
19
19
  selectField: {
20
20
  width: 180,
21
21
  },
22
22
  });
23
+ // the N was literal: the helper text said "the closest N species" whatever the
24
+ // box held
23
25
  const rowsHint = {
24
- ncbi: 'the closest N species NCBI has',
25
- panther: 'the closest N species PANTHER has',
26
- uniref: 'one per species, reviewed entries first',
26
+ ncbi: rows => `the ${rows} closest species NCBI has`,
27
+ panther: rows => `the ${rows} closest species PANTHER has`,
28
+ uniref: rows => `${rows} rows, one per species, reviewed entries first`,
27
29
  };
28
- const OrthologPanel = observer(function ({ handleClose, feature, model, }) {
30
+ const OrthologPanel = observer(function ({ handleClose, feature, model, preferredTranscriptId, }) {
29
31
  const { classes } = useStyles();
30
32
  const view = getLinearGenomeView(model);
31
- const [launchViewError, setLaunchViewError] = useState();
33
+ const { launchError, submit } = useLaunchSubmit(handleClose);
32
34
  const [taxId, setTaxId] = useState(9606);
33
- const [source, setSource] = useLocalStorage(ORTHOLOG_SOURCE_STORAGE_KEY, 'ncbi');
35
+ const [source, setSource] = useStoredOrthologSource();
34
36
  const [msaAlgorithm, setMsaAlgorithm] = useStoredMsaAlgorithm();
35
37
  const [maxSpecies, setMaxSpecies] = useState(String(defaultMaxSpecies));
36
38
  const geneCandidates = useMemo(() => getGeneIdentifiers(feature), [feature]);
37
- const transcriptSelection = useTranscriptSelection({ feature, view });
38
- const { selectedTranscript, proteinSequence } = transcriptSelection;
39
- const e = transcriptSelection.error ?? launchViewError;
39
+ const transcriptSelection = useTranscriptSelection({
40
+ feature,
41
+ view,
42
+ preferredTranscriptId,
43
+ });
44
+ const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
45
+ const e = transcriptSelection.error ?? launchError;
40
46
  const rowCount = Number(maxSpecies);
41
47
  const rowCountValid = Number.isInteger(rowCount) && rowCount >= 2;
42
48
  return (React.createElement(React.Fragment, null,
@@ -48,16 +54,19 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, }) {
48
54
  React.createElement(MsaAlgorithmSelect, { className: classes.selectField, value: msaAlgorithm, onChange: setMsaAlgorithm }),
49
55
  React.createElement(TextField2, { variant: "outlined", label: "Rows to align", className: classes.selectField, type: "number", value: maxSpecies, onChange: event => {
50
56
  setMaxSpecies(event.target.value);
51
- }, error: !rowCountValid, helperText: rowsHint[source] })),
57
+ }, error: !rowCountValid, helperText: rowCountValid
58
+ ? rowsHint[source](rowCount)
59
+ : 'a whole number, 2 or more' })),
52
60
  React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection })),
53
- React.createElement(SubmitCancelActions, { model: model, submitDisabled: !proteinSequence || !rowCountValid, onSubmit: () => {
54
- try {
55
- if (selectedTranscript) {
56
- setLaunchViewError(undefined);
57
- orthologLaunchView({
58
- feature: selectedTranscript,
61
+ React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !rowCountValid, onSubmit: placement => {
62
+ if (selectedTranscript) {
63
+ submit(() => {
64
+ launchConnectedView({
59
65
  view,
60
- newViewTitle: `Orthologs - ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
66
+ feature: selectedTranscript,
67
+ placement,
68
+ displayName: `Orthologs - ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
69
+ ...builtAlignmentLook,
61
70
  orthologParams: {
62
71
  taxId,
63
72
  source,
@@ -68,12 +77,7 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, }) {
68
77
  proteinSequence,
69
78
  },
70
79
  });
71
- handleClose();
72
- }
73
- }
74
- catch (e) {
75
- console.error(e);
76
- setLaunchViewError(e);
80
+ });
77
81
  }
78
82
  }, onCancel: handleClose })));
79
83
  });
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import type { OrthologSource } from '../../../MsaViewPanel/model';
3
- export declare const ORTHOLOG_SOURCE_STORAGE_KEY = "msaview-ortholog-source";
4
3
  export declare const orthologSourceLabels: Record<OrthologSource, string>;
4
+ export declare function validOrthologSource(stored: unknown): OrthologSource;
5
+ export declare function useStoredOrthologSource(): readonly ["ncbi" | "panther" | "uniref", (source: OrthologSource) => void];
5
6
  export default function OrthologSourceSelect({ value, onChange, className, }: {
6
7
  value: OrthologSource;
7
8
  onChange: (val: OrthologSource) => void;
@@ -1,12 +1,28 @@
1
1
  import React from 'react';
2
2
  import { MenuItem } from '@mui/material';
3
3
  import TextField2 from '../../../components/TextField2';
4
- export const ORTHOLOG_SOURCE_STORAGE_KEY = 'msaview-ortholog-source';
4
+ import { useLocalStorage } from '../../../utils/useLocalStorage';
5
+ const ORTHOLOG_SOURCE_STORAGE_KEY = 'msaview-ortholog-source';
5
6
  export const orthologSourceLabels = {
6
7
  ncbi: 'NCBI orthologs',
7
8
  panther: 'PANTHER',
8
9
  uniref: 'UniRef cluster',
9
10
  };
11
+ const orthologSources = Object.keys(orthologSourceLabels);
12
+ // another plugin version on the same origin may have stored a source this one
13
+ // lacks, and the tab indexes its hints by it
14
+ export function validOrthologSource(stored) {
15
+ return orthologSources.find(s => s === stored) ?? 'ncbi';
16
+ }
17
+ export function useStoredOrthologSource() {
18
+ const [stored, setStored] = useLocalStorage(ORTHOLOG_SOURCE_STORAGE_KEY, 'ncbi');
19
+ return [
20
+ validOrthologSource(stored),
21
+ (source) => {
22
+ setStored(source);
23
+ },
24
+ ];
25
+ }
10
26
  // Which species a source can answer for, in the words a reader picking one
11
27
  // needs: NCBI's ortholog sets stop at vertebrates and insects, PANTHER's run
12
28
  // from human to yeast and Arabidopsis, and a UniRef cluster is every UniProtKB
@@ -19,5 +35,5 @@ const hints = {
19
35
  export default function OrthologSourceSelect({ value, onChange, className, }) {
20
36
  return (React.createElement(TextField2, { variant: "outlined", label: "Source", className: className, select: true, value: value, helperText: hints[value], onChange: event => {
21
37
  onChange(event.target.value);
22
- } }, Object.keys(orthologSourceLabels).map(val => (React.createElement(MenuItem, { value: val, key: val }, orthologSourceLabels[val])))));
38
+ } }, orthologSources.map(val => (React.createElement(MenuItem, { value: val, key: val }, orthologSourceLabels[val])))));
23
39
  }
@@ -0,0 +1,13 @@
1
+ import { expect, test } from 'vitest';
2
+ import { validOrthologSource } from './OrthologSourceSelect';
3
+ test('a stored source this version knows comes back as it went in', () => {
4
+ expect(validOrthologSource('panther')).toBe('panther');
5
+ expect(validOrthologSource('uniref')).toBe('uniref');
6
+ });
7
+ // the Orthologs tab is the dialog's default, and it looked its helper text up
8
+ // by whatever was stored, so an unknown value threw on open
9
+ test('anything else falls back to NCBI', () => {
10
+ expect(validOrthologSource('ensembl')).toBe('ncbi');
11
+ expect(validOrthologSource(undefined)).toBe('ncbi');
12
+ expect(validOrthologSource({ source: 'panther' })).toBe('ncbi');
13
+ });
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
- declare const PreLoadedMSA: ({ model, feature, handleClose, }: {
3
+ declare const PreLoadedMSA: ({ model, feature, handleClose, preferredTranscriptId, }: {
4
4
  model: AbstractTrackModel;
5
5
  feature: Feature;
6
6
  handleClose: () => void;
7
+ /** the isoform the user right-clicked, preselected in the picker */
8
+ preferredTranscriptId?: string;
7
9
  }) => React.JSX.Element;
8
10
  export default PreLoadedMSA;