jbrowse-plugin-msaview 2.2.3 → 2.2.4

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 (162) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +229 -0
  3. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +23 -18
  4. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -1
  5. package/dist/AddHighlightModel/MsaToGenomeHighlight.js +23 -13
  6. package/dist/AddHighlightModel/MsaToGenomeHighlight.js.map +1 -1
  7. package/dist/AddHighlightModel/index.js +8 -1
  8. package/dist/AddHighlightModel/index.js.map +1 -1
  9. package/dist/AddHighlightModel/util.d.ts +2 -2
  10. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +2 -2
  11. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js +5 -11
  12. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js.map +1 -1
  13. package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js +5 -1
  14. package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js.map +1 -1
  15. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +16 -16
  16. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -1
  17. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +38 -46
  18. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js.map +1 -1
  19. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +4 -3
  20. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +4 -3
  21. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js.map +1 -1
  22. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.d.ts +9 -0
  23. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js +76 -0
  24. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js.map +1 -0
  25. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js +35 -13
  26. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js.map +1 -1
  27. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js +6 -12
  28. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js.map +1 -1
  29. package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.d.ts +6 -0
  30. package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js +15 -0
  31. package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js.map +1 -1
  32. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +12 -34
  33. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -1
  34. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.d.ts +1 -0
  35. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js +1 -0
  36. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js.map +1 -1
  37. package/dist/LaunchMsaView/components/TabPanel.d.ts +2 -2
  38. package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +2 -2
  39. package/dist/LaunchMsaView/components/TranscriptSelector.js +3 -6
  40. package/dist/LaunchMsaView/components/TranscriptSelector.js.map +1 -1
  41. package/dist/LaunchMsaView/components/useSWRFeatureSequence.js +6 -4
  42. package/dist/LaunchMsaView/components/useSWRFeatureSequence.js.map +1 -1
  43. package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +16 -0
  44. package/dist/LaunchMsaView/components/useTranscriptSelection.js +31 -0
  45. package/dist/LaunchMsaView/components/useTranscriptSelection.js.map +1 -0
  46. package/dist/LaunchMsaView/components/util.d.ts +3 -1
  47. package/dist/LaunchMsaView/components/util.js +12 -2
  48. package/dist/LaunchMsaView/components/util.js.map +1 -1
  49. package/dist/LaunchMsaView/util.d.ts +2 -0
  50. package/dist/LaunchMsaView/util.js +16 -4
  51. package/dist/LaunchMsaView/util.js.map +1 -1
  52. package/dist/LaunchMsaViewExtensionPoint/index.d.ts +2 -0
  53. package/dist/LaunchMsaViewExtensionPoint/index.js +31 -0
  54. package/dist/LaunchMsaViewExtensionPoint/index.js.map +1 -0
  55. package/dist/MsaViewPanel/components/ConnectStructureDialog.d.ts +7 -0
  56. package/dist/MsaViewPanel/components/ConnectStructureDialog.js +56 -0
  57. package/dist/MsaViewPanel/components/ConnectStructureDialog.js.map +1 -0
  58. package/dist/MsaViewPanel/components/MsaViewPanel.js +4 -2
  59. package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -1
  60. package/dist/MsaViewPanel/doLaunchBlast.d.ts +1 -0
  61. package/dist/MsaViewPanel/doLaunchBlast.js +65 -19
  62. package/dist/MsaViewPanel/doLaunchBlast.js.map +1 -1
  63. package/dist/MsaViewPanel/genomeToMSA.d.ts +6 -0
  64. package/dist/MsaViewPanel/genomeToMSA.js +38 -8
  65. package/dist/MsaViewPanel/genomeToMSA.js.map +1 -1
  66. package/dist/MsaViewPanel/genomeToMSA.test.d.ts +1 -0
  67. package/dist/MsaViewPanel/genomeToMSA.test.js +244 -0
  68. package/dist/MsaViewPanel/genomeToMSA.test.js.map +1 -0
  69. package/dist/MsaViewPanel/model.d.ts +717 -226
  70. package/dist/MsaViewPanel/model.js +467 -39
  71. package/dist/MsaViewPanel/model.js.map +1 -1
  72. package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +7 -2
  73. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +26 -27
  74. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -1
  75. package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.d.ts +1 -0
  76. package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js +240 -0
  77. package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js.map +1 -0
  78. package/dist/MsaViewPanel/msaDataStore.d.ts +14 -0
  79. package/dist/MsaViewPanel/msaDataStore.js +197 -0
  80. package/dist/MsaViewPanel/msaDataStore.js.map +1 -0
  81. package/dist/MsaViewPanel/pairwiseAlignment.d.ts +27 -0
  82. package/dist/MsaViewPanel/pairwiseAlignment.js +776 -0
  83. package/dist/MsaViewPanel/pairwiseAlignment.js.map +1 -0
  84. package/dist/MsaViewPanel/pairwiseAlignment.test.d.ts +1 -0
  85. package/dist/MsaViewPanel/pairwiseAlignment.test.js +112 -0
  86. package/dist/MsaViewPanel/pairwiseAlignment.test.js.map +1 -0
  87. package/dist/MsaViewPanel/structureConnection.d.ts +27 -0
  88. package/dist/MsaViewPanel/structureConnection.js +46 -0
  89. package/dist/MsaViewPanel/structureConnection.js.map +1 -0
  90. package/dist/MsaViewPanel/structureConnection.test.d.ts +1 -0
  91. package/dist/MsaViewPanel/structureConnection.test.js +122 -0
  92. package/dist/MsaViewPanel/structureConnection.test.js.map +1 -0
  93. package/dist/MsaViewPanel/types.d.ts +13 -0
  94. package/dist/MsaViewPanel/types.js +2 -0
  95. package/dist/MsaViewPanel/types.js.map +1 -0
  96. package/dist/MsaViewPanel/util.d.ts +7 -0
  97. package/dist/MsaViewPanel/util.js +10 -0
  98. package/dist/MsaViewPanel/util.js.map +1 -1
  99. package/dist/index.d.ts +5 -5
  100. package/dist/index.js +3 -1
  101. package/dist/index.js.map +1 -1
  102. package/dist/jbrowse-plugin-msaview.umd.production.min.js +39 -90
  103. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  104. package/dist/utils/blastCache.d.ts +34 -0
  105. package/dist/utils/blastCache.js +58 -0
  106. package/dist/utils/blastCache.js.map +1 -0
  107. package/dist/utils/fetch.d.ts +1 -1
  108. package/dist/utils/fetch.js +1 -1
  109. package/dist/utils/fetch.js.map +1 -1
  110. package/dist/utils/ncbiBlast.d.ts +1 -5
  111. package/dist/utils/taxonomyNames.d.ts +5 -0
  112. package/dist/utils/taxonomyNames.js +79 -0
  113. package/dist/utils/taxonomyNames.js.map +1 -0
  114. package/dist/utils/types.d.ts +8 -5
  115. package/package.json +50 -54
  116. package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +37 -21
  117. package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +38 -17
  118. package/src/AddHighlightModel/index.tsx +9 -4
  119. package/src/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.tsx +13 -13
  120. package/src/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.ts +6 -0
  121. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +30 -23
  122. package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +64 -51
  123. package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +9 -6
  124. package/src/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.tsx +146 -0
  125. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx +53 -22
  126. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.tsx +8 -13
  127. package/src/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.ts +25 -0
  128. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +27 -47
  129. package/src/LaunchMsaView/components/PreLoadedMSA/consts.ts +1 -0
  130. package/src/LaunchMsaView/components/TabPanel.tsx +2 -2
  131. package/src/LaunchMsaView/components/TranscriptSelector.tsx +13 -20
  132. package/src/LaunchMsaView/components/useSWRFeatureSequence.ts +5 -5
  133. package/src/LaunchMsaView/components/useTranscriptSelection.ts +48 -0
  134. package/src/LaunchMsaView/components/util.ts +17 -2
  135. package/src/LaunchMsaView/index.ts +1 -1
  136. package/src/LaunchMsaView/util.ts +25 -6
  137. package/src/LaunchMsaViewExtensionPoint/index.ts +74 -0
  138. package/src/MsaViewPanel/components/ConnectStructureDialog.tsx +156 -0
  139. package/src/MsaViewPanel/components/MsaViewPanel.tsx +6 -1
  140. package/src/MsaViewPanel/doLaunchBlast.ts +83 -23
  141. package/src/MsaViewPanel/genomeToMSA.test.ts +281 -0
  142. package/src/MsaViewPanel/genomeToMSA.ts +43 -10
  143. package/src/MsaViewPanel/model.ts +590 -43
  144. package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +256 -0
  145. package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +43 -29
  146. package/src/MsaViewPanel/msaDataStore.ts +236 -0
  147. package/src/MsaViewPanel/pairwiseAlignment.test.ts +140 -0
  148. package/src/MsaViewPanel/pairwiseAlignment.ts +818 -0
  149. package/src/MsaViewPanel/structureConnection.test.ts +143 -0
  150. package/src/MsaViewPanel/structureConnection.ts +72 -0
  151. package/src/MsaViewPanel/types.ts +14 -0
  152. package/src/MsaViewPanel/util.ts +11 -0
  153. package/src/index.ts +3 -1
  154. package/src/utils/blastCache.ts +114 -0
  155. package/src/utils/fetch.ts +1 -1
  156. package/src/utils/taxonomyNames.ts +111 -0
  157. package/src/utils/types.ts +9 -1
  158. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.d.ts +0 -5
  159. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js +0 -16
  160. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js.map +0 -1
  161. package/dist/out.js +0 -55381
  162. package/src/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.ts +0 -25
@@ -1,7 +1,8 @@
1
1
  import React, { useState } from 'react'
2
2
 
3
+ import { readConfObject } from '@jbrowse/core/configuration'
3
4
  import { Dialog } from '@jbrowse/core/ui'
4
- import { AbstractTrackModel, Feature } from '@jbrowse/core/util'
5
+ import { AbstractTrackModel, Feature, getSession } from '@jbrowse/core/util'
5
6
  import { Tab, Tabs } from '@mui/material'
6
7
 
7
8
  import EnsemblGeneTree from './EnsemblGeneTree/EnsemblGeneTree'
@@ -10,12 +11,7 @@ import NCBIBlastPanel from './NCBIBlastQuery/NCBIBlastPanel'
10
11
  import PreLoadedMSA from './PreLoadedMSA/PreLoadedMSADataPanel'
11
12
  import TabPanel from './TabPanel'
12
13
 
13
- const TABS = {
14
- NCBI_BLAST: 0,
15
- PRELOADED_MSA: 1,
16
- ENSEMBL_GENETREE: 2,
17
- MANUAL_MSA: 3,
18
- }
14
+ import type { Dataset } from './PreLoadedMSA/types'
19
15
 
20
16
  export default function LaunchMsaViewDialog({
21
17
  handleClose,
@@ -26,42 +22,53 @@ export default function LaunchMsaViewDialog({
26
22
  feature: Feature
27
23
  model: AbstractTrackModel
28
24
  }) {
29
- const [value, setValue] = useState(TABS.NCBI_BLAST)
25
+ const session = getSession(model)
26
+ const { jbrowse } = session
27
+ const datasets = readConfObject(jbrowse, ['msa', 'datasets']) as
28
+ | Dataset[]
29
+ | undefined
30
+ const hasPreloadedDatasets = datasets && datasets.length > 0
31
+
32
+ const [value, setValue] = useState('ncbi_blast')
30
33
 
31
- const handleChange = (_event: React.SyntheticEvent, newValue: number) => {
34
+ const handleChange = (_event: React.SyntheticEvent, newValue: string) => {
32
35
  setValue(newValue)
33
36
  }
34
37
 
35
38
  return (
36
39
  <Dialog maxWidth="xl" title="Launch MSA view" open onClose={handleClose}>
37
40
  <Tabs value={value} onChange={handleChange}>
38
- <Tab label="NCBI BLAST query" value={TABS.NCBI_BLAST} />
39
- <Tab label="Pre-loaded MSA datasets" value={TABS.PRELOADED_MSA} />
40
- <Tab label="Ensembl GeneTree" value={TABS.ENSEMBL_GENETREE} />
41
- <Tab label="Manually open MSA dataset" value={TABS.MANUAL_MSA} />
41
+ <Tab label="NCBI BLAST query" value="ncbi_blast" />
42
+ {hasPreloadedDatasets ? (
43
+ <Tab label="Pre-loaded MSA datasets" value="preloaded_msa" />
44
+ ) : null}
45
+ <Tab label="Ensembl GeneTree" value="ensembl_genetree" />
46
+ <Tab label="Manual upload" value="manual_msa" />
42
47
  </Tabs>
43
- <TabPanel value={value} index={TABS.NCBI_BLAST}>
48
+ <TabPanel value={value} index="ncbi_blast">
44
49
  <NCBIBlastPanel
45
50
  handleClose={handleClose}
46
51
  feature={feature}
47
52
  model={model}
48
53
  />
49
54
  </TabPanel>
50
- <TabPanel value={value} index={TABS.PRELOADED_MSA}>
51
- <PreLoadedMSA
52
- model={model}
53
- feature={feature}
54
- handleClose={handleClose}
55
- />
56
- </TabPanel>
57
- <TabPanel value={value} index={TABS.ENSEMBL_GENETREE}>
55
+ {hasPreloadedDatasets ? (
56
+ <TabPanel value={value} index="preloaded_msa">
57
+ <PreLoadedMSA
58
+ model={model}
59
+ feature={feature}
60
+ handleClose={handleClose}
61
+ />
62
+ </TabPanel>
63
+ ) : null}
64
+ <TabPanel value={value} index="ensembl_genetree">
58
65
  <EnsemblGeneTree
59
66
  model={model}
60
67
  feature={feature}
61
68
  handleClose={handleClose}
62
69
  />
63
70
  </TabPanel>
64
- <TabPanel value={value} index={TABS.MANUAL_MSA}>
71
+ <TabPanel value={value} index="manual_msa">
65
72
  <ManualMSALoader
66
73
  model={model}
67
74
  feature={feature}
@@ -8,8 +8,8 @@ import {
8
8
  getContainingView,
9
9
  getSession,
10
10
  } from '@jbrowse/core/util'
11
- import { openLocation } from '@jbrowse/core/util/io'
12
11
  import {
12
+ Alert,
13
13
  Button,
14
14
  DialogActions,
15
15
  DialogContent,
@@ -23,9 +23,9 @@ import { makeStyles } from 'tss-react/mui'
23
23
 
24
24
  import { launchView } from './launchView'
25
25
  import TextField2 from '../../../components/TextField2'
26
- import { getGeneDisplayName, getId, getTranscriptFeatures } from '../../util'
26
+ import { getGeneDisplayName } from '../../util'
27
27
  import TranscriptSelector from '../TranscriptSelector'
28
- import { useFeatureSequence } from '../useFeatureSequence'
28
+ import { useTranscriptSelection } from '../useTranscriptSelection'
29
29
 
30
30
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
31
31
 
@@ -56,14 +56,14 @@ const ManualMSALoader = observer(function PreLoadedMSA2({
56
56
  const [treeText, setTreeText] = useState('')
57
57
  const [msaFileLocation, setMsaFileLocation] = useState<FileLocation>()
58
58
  const [treeFileLocation, setTreeFileLocation] = useState<FileLocation>()
59
- const options = getTranscriptFeatures(feature)
60
- const [userSelection, setUserSelection] = useState(getId(options[0]))
61
- const ret = options.find(val => userSelection === getId(val))
62
- const selectedTranscript = options.find(val => getId(val) === userSelection)!
63
- const { proteinSequence, error: error2 } = useFeatureSequence({
64
- view,
65
- feature: selectedTranscript,
66
- })
59
+ const [querySeqName, setQuerySeqName] = useState('')
60
+ const {
61
+ options,
62
+ setSelectedId,
63
+ selectedTranscript,
64
+ proteinSequence,
65
+ error: error2,
66
+ } = useTranscriptSelection({ feature, view })
67
67
 
68
68
  const e = launchViewError ?? error2
69
69
  return (
@@ -143,10 +143,32 @@ const ManualMSALoader = observer(function PreLoadedMSA2({
143
143
  <TranscriptSelector
144
144
  feature={feature}
145
145
  options={options}
146
- selectedTranscriptId={userSelection}
147
- onTranscriptChange={setUserSelection}
146
+ selectedTranscript={selectedTranscript}
147
+ onTranscriptChange={setSelectedId}
148
148
  proteinSequence={proteinSequence}
149
149
  />
150
+
151
+ <TextField2
152
+ variant="outlined"
153
+ name="MSA row name"
154
+ fullWidth
155
+ required
156
+ style={{ marginTop: 20 }}
157
+ placeholder="Row name in MSA that corresponds to the selected transcript"
158
+ helperText="Required: Specify the name of the row in your MSA that should be aligned with the selected transcript"
159
+ value={querySeqName}
160
+ onChange={event => {
161
+ setQuerySeqName(event.target.value)
162
+ }}
163
+ />
164
+
165
+ {!querySeqName.trim() && (
166
+ <Alert severity="warning" style={{ marginTop: 10 }}>
167
+ Without specifying the MSA row name, clicking on the MSA will not
168
+ navigate to the corresponding genome position, and hovering
169
+ highlights will not work.
170
+ </Alert>
171
+ )}
150
172
  </DialogContent>
151
173
 
152
174
  <DialogActions>
@@ -154,50 +176,41 @@ const ManualMSALoader = observer(function PreLoadedMSA2({
154
176
  color="primary"
155
177
  variant="contained"
156
178
  disabled={
157
- !ret ||
179
+ !selectedTranscript ||
158
180
  (inputMethod === 'file' && !msaFileLocation) ||
159
181
  (inputMethod === 'text' && !msaText.trim())
160
182
  }
161
183
  onClick={() => {
162
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
163
- ;(async () => {
164
- try {
165
- if (!ret) {
166
- return
167
- }
184
+ try {
185
+ if (!selectedTranscript) {
186
+ return
187
+ }
168
188
 
169
- setLaunchViewError(undefined)
170
- launchView({
171
- session,
172
- newViewTitle: getGeneDisplayName(ret),
173
- view,
174
- feature: ret,
175
- data:
176
- inputMethod === 'file'
177
- ? {
178
- msa: msaFileLocation
179
- ? await openLocation(msaFileLocation).readFile(
180
- 'utf8',
181
- )
182
- : '',
183
- tree: treeFileLocation
184
- ? await openLocation(treeFileLocation).readFile(
185
- 'utf8',
186
- )
187
- : undefined,
188
- }
189
- : {
190
- msa: msaText,
191
- tree: treeText,
192
- },
193
- })
189
+ setLaunchViewError(undefined)
190
+ launchView({
191
+ session,
192
+ newViewTitle: getGeneDisplayName(selectedTranscript),
193
+ view,
194
+ feature: selectedTranscript,
195
+ querySeqName: querySeqName.trim() || undefined,
196
+ ...(inputMethod === 'file'
197
+ ? {
198
+ msaFilehandle: msaFileLocation,
199
+ treeFilehandle: treeFileLocation,
200
+ }
201
+ : {
202
+ data: {
203
+ msa: msaText,
204
+ tree: treeText || undefined,
205
+ },
206
+ }),
207
+ })
194
208
 
195
- handleClose()
196
- } catch (e) {
197
- console.error(e)
198
- setLaunchViewError(e)
199
- }
200
- })()
209
+ handleClose()
210
+ } catch (e) {
211
+ console.error(e)
212
+ setLaunchViewError(e)
213
+ }
201
214
  }}
202
215
  >
203
216
  Submit
@@ -7,16 +7,18 @@ export function launchView({
7
7
  newViewTitle,
8
8
  view,
9
9
  feature,
10
- msaFileLocation,
11
- treeFileLocation,
10
+ msaFilehandle,
11
+ treeFilehandle,
12
+ querySeqName,
12
13
  data,
13
14
  }: {
14
15
  session: AbstractSessionModel
15
16
  newViewTitle: string
16
17
  view: LinearGenomeViewModel
17
18
  feature: Feature
18
- msaFileLocation?: FileLocation
19
- treeFileLocation?: FileLocation
19
+ msaFilehandle?: FileLocation
20
+ treeFilehandle?: FileLocation
21
+ querySeqName?: string
20
22
  data?: {
21
23
  msa: string
22
24
  tree?: string
@@ -27,8 +29,9 @@ export function launchView({
27
29
  displayName: newViewTitle,
28
30
  connectedViewId: view.id,
29
31
  connectedFeature: feature.toJSON(),
30
- msaFileLocation,
31
- treeFileLocation,
32
+ msaFilehandle,
33
+ treeFilehandle,
34
+ querySeqName,
32
35
  data,
33
36
  })
34
37
  }
@@ -0,0 +1,146 @@
1
+ import React, { useEffect, useState } from 'react'
2
+
3
+ import { Feature, getContainingView } from '@jbrowse/core/util'
4
+ import DeleteIcon from '@mui/icons-material/Delete'
5
+ import {
6
+ Button,
7
+ IconButton,
8
+ List,
9
+ ListItem,
10
+ ListItemButton,
11
+ ListItemText,
12
+ Typography,
13
+ } from '@mui/material'
14
+ import { observer } from 'mobx-react'
15
+
16
+ import { blastLaunchViewFromCache } from './blastLaunchView'
17
+ import {
18
+ clearAllCachedResults,
19
+ deleteCachedResult,
20
+ getAllCachedResults,
21
+ } from '../../../utils/blastCache'
22
+
23
+ import type { CachedBlastResult } from '../../../utils/blastCache'
24
+ import type { AbstractTrackModel } from '@jbrowse/core/util'
25
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
26
+
27
+ const CachedBlastResults = observer(function ({
28
+ model,
29
+ handleClose,
30
+ feature,
31
+ }: {
32
+ model: AbstractTrackModel
33
+ handleClose: () => void
34
+ feature: Feature
35
+ }) {
36
+ const [results, setResults] = useState<CachedBlastResult[]>([])
37
+ const [loading, setLoading] = useState(true)
38
+ const view = getContainingView(model) as LinearGenomeViewModel
39
+
40
+ const geneId = feature.get('id')
41
+ useEffect(() => {
42
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
43
+ ;(async () => {
44
+ try {
45
+ const cached = await getAllCachedResults()
46
+ setResults(cached.filter(r => r.geneId === geneId))
47
+ setLoading(false)
48
+ } catch (e) {
49
+ console.error(e)
50
+ }
51
+ })()
52
+ }, [geneId])
53
+
54
+ const handleDelete = async (id: string) => {
55
+ await deleteCachedResult(id)
56
+ setResults(r => r.filter(result => result.id !== id))
57
+ }
58
+
59
+ const handleClearAll = async () => {
60
+ await clearAllCachedResults()
61
+ setResults([])
62
+ }
63
+
64
+ const handleUseCached = (cached: CachedBlastResult) => {
65
+ blastLaunchViewFromCache({
66
+ view,
67
+ cached,
68
+ newViewTitle: `BLAST - ${cached.geneId ?? cached.transcriptId ?? 'Unknown gene'}`,
69
+ })
70
+ handleClose()
71
+ }
72
+
73
+ if (loading) {
74
+ return <Typography>Loading cached results...</Typography>
75
+ }
76
+
77
+ if (results.length === 0) {
78
+ return (
79
+ <Typography color="textSecondary">
80
+ No cached BLAST results found for this gene. Run a BLAST query to cache
81
+ results.
82
+ </Typography>
83
+ )
84
+ }
85
+
86
+ return (
87
+ <div>
88
+ <div
89
+ style={{
90
+ display: 'flex',
91
+ justifyContent: 'space-between',
92
+ alignItems: 'center',
93
+ marginBottom: 8,
94
+ }}
95
+ >
96
+ <Typography variant="subtitle1">
97
+ Cached BLAST Results ({results.length})
98
+ </Typography>
99
+ <Button
100
+ size="small"
101
+ color="error"
102
+ onClick={() => {
103
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
104
+ handleClearAll()
105
+ }}
106
+ >
107
+ Clear All
108
+ </Button>
109
+ </div>
110
+ <List dense style={{ maxHeight: 300, overflow: 'auto' }}>
111
+ {results.map(result => (
112
+ <ListItem
113
+ key={result.id}
114
+ disablePadding
115
+ secondaryAction={
116
+ <IconButton
117
+ edge="end"
118
+ size="small"
119
+ onClick={e => {
120
+ e.stopPropagation()
121
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
122
+ handleDelete(result.id)
123
+ }}
124
+ >
125
+ <DeleteIcon fontSize="small" />
126
+ </IconButton>
127
+ }
128
+ >
129
+ <ListItemButton
130
+ onClick={() => {
131
+ handleUseCached(result)
132
+ }}
133
+ >
134
+ <ListItemText
135
+ primary={`${result.geneId ?? result.transcriptId ?? 'Unknown'} - ${result.blastDatabase}/${result.blastProgram} (${result.msaAlgorithm})`}
136
+ secondary={`${new Date(result.timestamp).toLocaleString()} - Seq: ${result.proteinSequence.slice(0, 30)}...`}
137
+ />
138
+ </ListItemButton>
139
+ </ListItem>
140
+ ))}
141
+ </List>
142
+ </div>
143
+ )
144
+ })
145
+
146
+ export default CachedBlastResults
@@ -6,8 +6,11 @@ import {
6
6
  Feature,
7
7
  getContainingView,
8
8
  } from '@jbrowse/core/util'
9
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
9
10
  import {
10
- Box,
11
+ Accordion,
12
+ AccordionDetails,
13
+ AccordionSummary,
11
14
  Button,
12
15
  DialogActions,
13
16
  DialogContent,
@@ -17,16 +20,13 @@ import {
17
20
  import { observer } from 'mobx-react'
18
21
  import { makeStyles } from 'tss-react/mui'
19
22
 
23
+ import CachedBlastResults from './CachedBlastResults'
20
24
  import { blastLaunchView } from './blastLaunchView'
21
25
  import TextField2 from '../../../components/TextField2'
22
- import {
23
- getGeneDisplayName,
24
- getId,
25
- getTranscriptDisplayName,
26
- getTranscriptFeatures,
27
- } from '../../util'
26
+ import { getAllCachedResults } from '../../../utils/blastCache'
27
+ import { getGeneDisplayName, getTranscriptDisplayName } from '../../util'
28
28
  import TranscriptSelector from '../TranscriptSelector'
29
- import { useFeatureSequence } from '../useFeatureSequence'
29
+ import { useTranscriptSelection } from '../useTranscriptSelection'
30
30
 
31
31
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
32
32
 
@@ -67,26 +67,39 @@ const NCBIBlastAutomaticPanel = observer(function ({
67
67
  useState<blastDatabaseOptionsT>('nr')
68
68
  const [selectedMsaAlgorithm, setSelectedMsaAlgorithm] =
69
69
  useState<msaAlgorithmsT>('clustalo')
70
- const options = getTranscriptFeatures(feature)
71
- const [selectedTranscriptId, setSelectedTranscriptId] = useState(
72
- getId(options[0]),
73
- )
74
70
  const [selectedBlastProgram, setSelectedBlastProgram] =
75
71
  useState<blastProgramsT>('quick-blastp')
76
- const selectedTranscript = options.find(
77
- val => getId(val) === selectedTranscriptId,
78
- )!
79
- const { error: proteinSequenceError, proteinSequence } = useFeatureSequence({
80
- view,
81
- feature: selectedTranscript,
82
- })
72
+ const [hasCachedResults, setHasCachedResults] = useState(false)
73
+ const [error, setError] = useState<unknown>()
74
+
75
+ const geneId = feature.get('id')
76
+ useEffect(() => {
77
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
78
+ ;(async () => {
79
+ try {
80
+ const results = await getAllCachedResults()
81
+ setHasCachedResults(results.some(r => r.geneId === geneId))
82
+ } catch (e) {
83
+ console.error(e)
84
+ setError(e)
85
+ }
86
+ })()
87
+ }, [geneId])
88
+
89
+ const {
90
+ options,
91
+ setSelectedId,
92
+ selectedTranscript,
93
+ proteinSequence,
94
+ error: proteinSequenceError,
95
+ } = useTranscriptSelection({ feature, view })
83
96
 
84
97
  useEffect(() => {
85
98
  if (selectedBlastDatabase === 'nr_cluster_seq') {
86
99
  setSelectedBlastProgram('blastp')
87
100
  }
88
101
  }, [selectedBlastDatabase])
89
- const e = proteinSequenceError ?? launchViewError
102
+ const e = proteinSequenceError ?? launchViewError ?? error
90
103
  const style = { width: 150 }
91
104
  return (
92
105
  <>
@@ -167,8 +180,8 @@ const NCBIBlastAutomaticPanel = observer(function ({
167
180
  <TranscriptSelector
168
181
  feature={feature}
169
182
  options={options}
170
- selectedTranscriptId={selectedTranscriptId}
171
- onTranscriptChange={setSelectedTranscriptId}
183
+ selectedTranscript={selectedTranscript}
184
+ onTranscriptChange={setSelectedId}
172
185
  proteinSequence={proteinSequence}
173
186
  />
174
187
 
@@ -182,6 +195,21 @@ const NCBIBlastAutomaticPanel = observer(function ({
182
195
  you need a COBALT alignment, please use the manual approach of
183
196
  submitting BLAST yourself and downloading the resulting files
184
197
  </Typography>
198
+
199
+ {hasCachedResults ? (
200
+ <Accordion style={{ marginTop: 20 }}>
201
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
202
+ <Typography>Previous BLAST Results</Typography>
203
+ </AccordionSummary>
204
+ <AccordionDetails>
205
+ <CachedBlastResults
206
+ model={model}
207
+ handleClose={handleClose}
208
+ feature={feature}
209
+ />
210
+ </AccordionDetails>
211
+ </Accordion>
212
+ ) : null}
185
213
  </DialogContent>
186
214
  <DialogActions>
187
215
  <Button
@@ -189,6 +217,9 @@ const NCBIBlastAutomaticPanel = observer(function ({
189
217
  variant="contained"
190
218
  onClick={() => {
191
219
  try {
220
+ if (!selectedTranscript) {
221
+ return
222
+ }
192
223
  setLaunchViewError(undefined)
193
224
  blastLaunchView({
194
225
  feature: selectedTranscript,
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react'
1
+ import React from 'react'
2
2
 
3
3
  import { ErrorMessage } from '@jbrowse/core/ui'
4
4
  import { getContainingView, shorten2 } from '@jbrowse/core/util'
@@ -7,9 +7,9 @@ import { observer } from 'mobx-react'
7
7
  import { makeStyles } from 'tss-react/mui'
8
8
 
9
9
  import ExternalLink from '../../../components/ExternalLink'
10
- import { getId, getTranscriptFeatures } from '../../util'
10
+ import { cleanProteinSequence } from '../../util'
11
11
  import TranscriptSelector from '../TranscriptSelector'
12
- import { useFeatureSequence } from '../useFeatureSequence'
12
+ import { useTranscriptSelection } from '../useTranscriptSelection'
13
13
 
14
14
  import type { AbstractTrackModel, Feature } from '@jbrowse/core/util'
15
15
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
@@ -43,15 +43,10 @@ const NCBIBlastManualPanel = observer(function ({
43
43
  }) {
44
44
  const { classes } = useStyles()
45
45
  const view = getContainingView(model) as LinearGenomeViewModel
46
- const options = getTranscriptFeatures(feature)
47
- const [userSelection, setUserSelection] = useState(getId(options[0]))
48
- const selectedTranscript = options.find(val => getId(val) === userSelection)!
49
- const { proteinSequence, error } = useFeatureSequence({
50
- view,
51
- feature: selectedTranscript,
52
- })
46
+ const { options, setSelectedId, selectedTranscript, proteinSequence, error } =
47
+ useTranscriptSelection({ feature, view })
53
48
 
54
- const s2 = proteinSequence.replaceAll('*', '').replaceAll('&', '')
49
+ const s2 = cleanProteinSequence(proteinSequence)
55
50
  const link = `${baseUrl}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${s2}`
56
51
  const link2 = `${baseUrl}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${shorten2(s2, 10)}`
57
52
 
@@ -64,8 +59,8 @@ const NCBIBlastManualPanel = observer(function ({
64
59
  <TranscriptSelector
65
60
  feature={feature}
66
61
  options={options}
67
- selectedTranscriptId={userSelection}
68
- onTranscriptChange={setUserSelection}
62
+ selectedTranscript={selectedTranscript}
63
+ onTranscriptChange={setSelectedId}
69
64
  proteinSequence={proteinSequence}
70
65
  />
71
66
 
@@ -1,6 +1,7 @@
1
1
  import { Feature, getSession } from '@jbrowse/core/util'
2
2
 
3
3
  import type { JBrowsePluginMsaViewModel } from '../../../MsaViewPanel/model'
4
+ import type { CachedBlastResult } from '../../../utils/blastCache'
4
5
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
5
6
 
6
7
  export function blastLaunchView({
@@ -25,3 +26,27 @@ export function blastLaunchView({
25
26
  blastParams,
26
27
  }) as JBrowsePluginMsaViewModel
27
28
  }
29
+
30
+ export function blastLaunchViewFromCache({
31
+ newViewTitle,
32
+ view,
33
+ cached,
34
+ }: {
35
+ newViewTitle: string
36
+ view: LinearGenomeViewModel
37
+ cached: CachedBlastResult
38
+ }) {
39
+ return getSession(view).addView('MsaView', {
40
+ type: 'MsaView',
41
+ displayName: newViewTitle,
42
+ connectedViewId: view.id,
43
+ drawNodeBubbles: true,
44
+ colWidth: 10,
45
+ rowHeight: 12,
46
+ data: {
47
+ msa: cached.msa,
48
+ tree: cached.tree,
49
+ treeMetadata: cached.treeMetadata,
50
+ },
51
+ }) as JBrowsePluginMsaViewModel
52
+ }