jbrowse-plugin-msaview 2.2.1 → 2.2.3

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 (132) hide show
  1. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +10 -0
  2. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js +64 -0
  3. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js.map +1 -0
  4. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +7 -0
  5. package/dist/BgzipFastaMsaAdapter/configSchema.js +13 -0
  6. package/dist/BgzipFastaMsaAdapter/configSchema.js.map +1 -0
  7. package/dist/BgzipFastaMsaAdapter/index.d.ts +2 -0
  8. package/dist/BgzipFastaMsaAdapter/index.js +15 -0
  9. package/dist/BgzipFastaMsaAdapter/index.js.map +1 -0
  10. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js.map +1 -1
  11. package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.d.ts +1 -1
  12. package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js +3 -23
  13. package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js.map +1 -1
  14. package/dist/LaunchMsaView/components/EnsemblGeneTree/util.d.ts +1 -4
  15. package/dist/LaunchMsaView/components/EnsemblGeneTree/util.js +1 -37
  16. package/dist/LaunchMsaView/components/EnsemblGeneTree/util.js.map +1 -1
  17. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +1 -1
  18. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +21 -16
  19. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -1
  20. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +1 -1
  21. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js.map +1 -1
  22. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js +12 -8
  23. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js.map +1 -1
  24. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js +1 -1
  25. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js.map +1 -1
  26. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastPanel.js +8 -7
  27. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastPanel.js.map +1 -1
  28. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBISettingsDialog.js +1 -1
  29. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBISettingsDialog.js.map +1 -1
  30. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +87 -63
  31. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -1
  32. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.d.ts +7 -0
  33. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js +8 -0
  34. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js.map +1 -0
  35. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.d.ts +12 -0
  36. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.js +12 -0
  37. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.js.map +1 -0
  38. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.d.ts +5 -0
  39. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js +16 -0
  40. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js.map +1 -0
  41. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +6 -3
  42. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +3 -20
  43. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js.map +1 -1
  44. package/dist/LaunchMsaView/components/PreLoadedMSA/types.d.ts +7 -0
  45. package/dist/LaunchMsaView/components/PreLoadedMSA/types.js +2 -0
  46. package/dist/LaunchMsaView/components/PreLoadedMSA/types.js.map +1 -0
  47. package/dist/LaunchMsaView/components/TranscriptSelector.js +2 -2
  48. package/dist/LaunchMsaView/components/TranscriptSelector.js.map +1 -1
  49. package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +2 -2
  50. package/dist/LaunchMsaView/components/calculateProteinSequence.js +2 -2
  51. package/dist/LaunchMsaView/components/calculateProteinSequence.js.map +1 -1
  52. package/dist/LaunchMsaView/components/types.d.ts +1 -0
  53. package/dist/LaunchMsaView/components/useFeatureSequence.d.ts +3 -7
  54. package/dist/LaunchMsaView/components/useFeatureSequence.js +9 -62
  55. package/dist/LaunchMsaView/components/useFeatureSequence.js.map +1 -1
  56. package/dist/LaunchMsaView/components/useSWRFeatureSequence.d.ts +13 -0
  57. package/dist/LaunchMsaView/components/useSWRFeatureSequence.js +53 -0
  58. package/dist/LaunchMsaView/components/useSWRFeatureSequence.js.map +1 -0
  59. package/dist/LaunchMsaView/index.js +3 -1
  60. package/dist/LaunchMsaView/index.js.map +1 -1
  61. package/dist/MsaViewPanel/components/RIDLink.js +1 -1
  62. package/dist/MsaViewPanel/components/RIDLink.js.map +1 -1
  63. package/dist/MsaViewPanel/genomeToMSA.js +2 -2
  64. package/dist/MsaViewPanel/genomeToMSA.js.map +1 -1
  65. package/dist/MsaViewPanel/model.d.ts +23 -84
  66. package/dist/MsaViewPanel/model.js +42 -22
  67. package/dist/MsaViewPanel/model.js.map +1 -1
  68. package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +6 -3
  69. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +39 -14
  70. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -1
  71. package/dist/MsaViewPanel/util.js +2 -2
  72. package/dist/MsaViewPanel/util.js.map +1 -1
  73. package/dist/components/ExternalLink.js.map +1 -0
  74. package/dist/components/ReadOnlyTextField2.js.map +1 -0
  75. package/dist/components/TextField2.js.map +1 -0
  76. package/dist/index.d.ts +19 -0
  77. package/dist/index.js +26 -0
  78. package/dist/index.js.map +1 -1
  79. package/dist/jbrowse-plugin-msaview.umd.production.min.js +46 -33
  80. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  81. package/dist/out.js +55381 -0
  82. package/dist/utils/fetch.d.ts +3 -1
  83. package/dist/utils/fetch.js +24 -3
  84. package/dist/utils/fetch.js.map +1 -1
  85. package/package.json +7 -6
  86. package/src/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.ts +78 -0
  87. package/src/BgzipFastaMsaAdapter/configSchema.ts +18 -0
  88. package/src/BgzipFastaMsaAdapter/index.ts +18 -0
  89. package/src/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.tsx +0 -1
  90. package/src/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.ts +6 -24
  91. package/src/LaunchMsaView/components/EnsemblGeneTree/util.ts +6 -45
  92. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +23 -24
  93. package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +1 -1
  94. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx +13 -8
  95. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.tsx +1 -1
  96. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastPanel.tsx +20 -29
  97. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBISettingsDialog.tsx +1 -1
  98. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +154 -68
  99. package/src/LaunchMsaView/components/PreLoadedMSA/consts.ts +7 -0
  100. package/src/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.ts +33 -0
  101. package/src/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.ts +25 -0
  102. package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +7 -26
  103. package/src/LaunchMsaView/components/PreLoadedMSA/types.ts +8 -0
  104. package/src/LaunchMsaView/components/TranscriptSelector.tsx +2 -2
  105. package/src/LaunchMsaView/components/calculateProteinSequence.ts +3 -3
  106. package/src/LaunchMsaView/components/types.ts +1 -0
  107. package/src/LaunchMsaView/components/useFeatureSequence.ts +10 -71
  108. package/src/LaunchMsaView/components/useSWRFeatureSequence.ts +90 -0
  109. package/src/LaunchMsaView/index.ts +4 -1
  110. package/src/MsaViewPanel/components/RIDLink.tsx +1 -1
  111. package/src/MsaViewPanel/genomeToMSA.ts +2 -2
  112. package/src/MsaViewPanel/model.ts +45 -29
  113. package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +43 -18
  114. package/src/MsaViewPanel/util.ts +2 -2
  115. package/src/index.ts +31 -0
  116. package/src/utils/fetch.ts +31 -3
  117. package/dist/ExternalLink.js.map +0 -1
  118. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.d.ts +0 -1
  119. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.js +0 -12
  120. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.js.map +0 -1
  121. package/dist/ReadOnlyTextField2.js.map +0 -1
  122. package/dist/TextField2.js.map +0 -1
  123. package/src/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.ts +0 -13
  124. /package/dist/{ExternalLink.d.ts → components/ExternalLink.d.ts} +0 -0
  125. /package/dist/{ExternalLink.js → components/ExternalLink.js} +0 -0
  126. /package/dist/{ReadOnlyTextField2.d.ts → components/ReadOnlyTextField2.d.ts} +0 -0
  127. /package/dist/{ReadOnlyTextField2.js → components/ReadOnlyTextField2.js} +0 -0
  128. /package/dist/{TextField2.d.ts → components/TextField2.d.ts} +0 -0
  129. /package/dist/{TextField2.js → components/TextField2.js} +0 -0
  130. /package/src/{ExternalLink.tsx → components/ExternalLink.tsx} +0 -0
  131. /package/src/{ReadOnlyTextField2.tsx → components/ReadOnlyTextField2.tsx} +0 -0
  132. /package/src/{TextField2.tsx → components/TextField2.tsx} +0 -0
@@ -1,26 +1,31 @@
1
- import React, { useEffect, useState } from 'react'
1
+ import React, { useEffect, useMemo, useState } from 'react'
2
2
 
3
- import { ErrorMessage } from '@jbrowse/core/ui'
3
+ import { readConfObject } from '@jbrowse/core/configuration'
4
+ import { ErrorMessage, LoadingEllipses, SanitizedHTML } from '@jbrowse/core/ui'
4
5
  import {
5
6
  AbstractTrackModel,
6
7
  Feature,
7
8
  getContainingView,
9
+ getEnv,
8
10
  getSession,
9
11
  } from '@jbrowse/core/util'
10
- import { Button, DialogActions, DialogContent, Typography } from '@mui/material'
12
+ import { Button, DialogActions, DialogContent, MenuItem } from '@mui/material'
11
13
  import { observer } from 'mobx-react'
14
+ import useSWR from 'swr'
12
15
  import { makeStyles } from 'tss-react/mui'
13
16
 
14
- import { fetchGeneList } from './fetchGeneList'
15
- import { preCalculatedLaunchView } from './preCalculatedLaunchView'
16
- import ExternalLink from '../../../ExternalLink'
17
+ import TextField2 from '../../../components/TextField2'
17
18
  import { getGeneDisplayName, getId, getTranscriptFeatures } from '../../util'
18
19
  import TranscriptSelector from '../TranscriptSelector'
19
20
  import { useFeatureSequence } from '../useFeatureSequence'
21
+ import { swrFlags } from './consts'
22
+ import { fetchMSA, fetchMSAList } from './fetchMSAData'
23
+ import { findValidTranscriptId } from './findValidTranscriptId'
24
+ import { preCalculatedLaunchView } from './preCalculatedLaunchView'
25
+ import { Dataset } from './types'
20
26
 
21
27
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
22
28
 
23
-
24
29
  const useStyles = makeStyles()({
25
30
  dialogContent: {
26
31
  width: '80em',
@@ -39,85 +44,166 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
39
44
  const session = getSession(model)
40
45
  const view = getContainingView(model) as LinearGenomeViewModel
41
46
  const { classes } = useStyles()
42
- const [error1, setError] = useState<unknown>()
43
- const [geneNameList, setGeneNameList] = useState<string[]>()
44
- useEffect(() => {
45
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
46
- ;(async () => {
47
- try {
48
- const data = await fetchGeneList()
49
- setGeneNameList(data)
50
- const set = new Set(data)
51
- const options = getTranscriptFeatures(feature)
52
- const ret = options.find(val => set.has(getId(val)))
53
- if (ret) {
54
- setUserSelection(getId(ret))
55
- }
56
- } catch (e) {
57
- console.error(e)
58
- setError(e)
59
- }
60
- })()
61
- }, [feature])
62
- const validSet = new Set(geneNameList)
63
- const options = getTranscriptFeatures(feature)
64
- const ret = options.find(val => validSet.has(getId(val)))
65
- const [userSelection, setUserSelection] = useState(getId(options[0]))
66
- const selectedTranscript = options.find(val => getId(val) === userSelection)!
67
- const { proteinSequence, error: error2 } = useFeatureSequence({
47
+ const { pluginManager } = getEnv(model)
48
+ const { assemblyNames } = view
49
+ const transcripts = getTranscriptFeatures(feature)
50
+ const [selectedTranscriptId, setSelectedTranscriptId] = useState(
51
+ getId(transcripts[0]),
52
+ )
53
+ const [viewError, setViewError] = useState<unknown>()
54
+ const selectedTranscript = selectedTranscriptId
55
+ ? transcripts.find(val => getId(val) === selectedTranscriptId)
56
+ : undefined
57
+ const { proteinSequence, error: proteinSequenceError } = useFeatureSequence({
68
58
  view,
69
59
  feature: selectedTranscript,
70
60
  })
71
61
 
72
- const e = error1 ?? error2
62
+ const { jbrowse } = session
63
+ const datasets = readConfObject(jbrowse, ['msa', 'datasets']) as
64
+ | Dataset[]
65
+ | undefined
66
+ const [selectedDatasetId, setSelectedDatasetId] = useState(
67
+ datasets?.[0]?.datasetId,
68
+ )
69
+ const selectedDataset = datasets?.find(d => d.datasetId === selectedDatasetId)
70
+ const {
71
+ data: msaList,
72
+ isLoading: msaListLoading,
73
+ error: msaListFetchError,
74
+ } = useSWR(
75
+ selectedDatasetId ? `${selectedDatasetId}-msa-list` : 'none-msa-list',
76
+ () =>
77
+ selectedDataset
78
+ ? fetchMSAList({
79
+ config: selectedDataset.adapter,
80
+ pluginManager,
81
+ })
82
+ : undefined,
83
+ swrFlags,
84
+ )
85
+ const {
86
+ data: msaData,
87
+ isLoading: msaDataLoading,
88
+ error: msaDataFetchError,
89
+ } = useSWR(
90
+ selectedTranscriptId && selectedDatasetId
91
+ ? `${selectedTranscriptId}-${selectedTranscriptId}-${msaList?.length}-msa`
92
+ : 'none-msa',
93
+ () =>
94
+ selectedTranscriptId && selectedDataset && msaList
95
+ ? fetchMSA({
96
+ msaId: selectedTranscriptId,
97
+ config: selectedDataset.adapter,
98
+ pluginManager,
99
+ })
100
+ : undefined,
101
+ swrFlags,
102
+ )
103
+
104
+ const validSet = useMemo(() => new Set(msaList), [msaList])
73
105
 
106
+ // Update selectedTranscriptId when msaList changes
107
+ useEffect(() => {
108
+ if (msaList && msaList.length > 0) {
109
+ const validId = findValidTranscriptId({
110
+ transcriptsList: transcripts,
111
+ validMsaList: msaList,
112
+ })
113
+ if (validId && validId !== selectedTranscriptId) {
114
+ setSelectedTranscriptId(validId)
115
+ }
116
+ }
117
+ }, [msaList, transcripts, selectedTranscriptId])
118
+
119
+ const e =
120
+ msaListFetchError ?? msaDataFetchError ?? proteinSequenceError ?? viewError
121
+ if (e) {
122
+ console.error(e)
123
+ }
74
124
  return (
75
125
  <>
76
126
  <DialogContent className={classes.dialogContent}>
77
127
  {e ? <ErrorMessage error={e} /> : null}
78
- <Typography>
79
- The source data for these multiple sequence alignments is from{' '}
80
- <ExternalLink href="https://hgdownload.soe.ucsc.edu/goldenPath/hg38/multiz100way/alignments/">
81
- knownCanonical.multiz100way.protAA.fa.gz
82
- </ExternalLink>
83
- </Typography>
84
- {geneNameList && !ret ? (
85
- <Typography color="error">No MSA data for this gene found</Typography>
128
+
129
+ <TextField2
130
+ select
131
+ label="Select MSA dataset"
132
+ value={selectedDatasetId}
133
+ onChange={event => {
134
+ setSelectedDatasetId(event.target.value)
135
+ }}
136
+ >
137
+ {datasets && datasets.length > 0 ? (
138
+ datasets.map(d => (
139
+ <MenuItem key={d.datasetId} value={d.datasetId}>
140
+ {d.name}
141
+ </MenuItem>
142
+ ))
143
+ ) : (
144
+ <MenuItem>No MSA datasets found</MenuItem>
145
+ )}
146
+ </TextField2>
147
+
148
+ {selectedDataset ? (
149
+ <div style={{ marginTop: 50 }}>
150
+ {!msaListLoading && msaDataLoading ? (
151
+ <LoadingEllipses
152
+ variant="h6"
153
+ message={`Loading MSA for (${selectedTranscriptId})`}
154
+ />
155
+ ) : null}
156
+ {msaListLoading ? (
157
+ <LoadingEllipses
158
+ variant="h6"
159
+ message={`Loading available MSAs for (${selectedDataset.name})`}
160
+ />
161
+ ) : null}
162
+
163
+ {msaList ? (
164
+ <div>
165
+ <SanitizedHTML html={selectedDataset.description} />
166
+ <TranscriptSelector
167
+ feature={feature}
168
+ options={transcripts}
169
+ selectedTranscriptId={selectedTranscriptId}
170
+ onTranscriptChange={setSelectedTranscriptId}
171
+ proteinSequence={proteinSequence}
172
+ validSet={validSet}
173
+ />
174
+ </div>
175
+ ) : null}
176
+ </div>
86
177
  ) : null}
87
- <TranscriptSelector
88
- feature={feature}
89
- options={options}
90
- selectedTranscriptId={userSelection}
91
- onTranscriptChange={setUserSelection}
92
- proteinSequence={proteinSequence}
93
- validSet={validSet}
94
- />
95
178
  </DialogContent>
96
179
 
97
180
  <DialogActions>
98
181
  <Button
99
182
  color="primary"
100
183
  variant="contained"
184
+ disabled={!selectedTranscript || !msaData?.length}
101
185
  onClick={() => {
102
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
103
- ;(async () => {
104
- try {
105
- if (!ret) {
106
- return
107
- }
108
- await preCalculatedLaunchView({
109
- userSelection,
110
- session,
111
- newViewTitle: getGeneDisplayName(ret),
112
- view,
113
- feature: ret,
114
- })
115
- handleClose()
116
- } catch (e) {
117
- console.error(e)
118
- setError(e)
186
+ try {
187
+ if (!selectedTranscript || !msaData) {
188
+ return
119
189
  }
120
- })()
190
+ const querySeqName = `${selectedTranscriptId}_${assemblyNames[0]}`
191
+ preCalculatedLaunchView({
192
+ session,
193
+ newViewTitle: getGeneDisplayName(selectedTranscript),
194
+ view,
195
+ querySeqName,
196
+ feature: selectedTranscript,
197
+ data: {
198
+ msa: msaData
199
+ .map(r => `>${r.get('refName')}\n${r.get('seq')}`)
200
+ .join('\n'),
201
+ },
202
+ })
203
+ handleClose()
204
+ } catch (e) {
205
+ setViewError(e)
206
+ }
121
207
  }}
122
208
  >
123
209
  Submit
@@ -0,0 +1,7 @@
1
+ export const swrFlags = {
2
+ revalidateOnFocus: false,
3
+ revalidateOnReconnect: false,
4
+ refreshWhenHidden: false,
5
+ refreshWhenOffline: false,
6
+ shouldRetryOnError: false,
7
+ }
@@ -0,0 +1,33 @@
1
+ import { AnyConfigurationModel } from '@jbrowse/core/configuration'
2
+ import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'
3
+ import { Feature } from '@jbrowse/core/util'
4
+
5
+ import type PluginManager from '@jbrowse/core/PluginManager'
6
+
7
+ export async function fetchMSAList({
8
+ config,
9
+ pluginManager,
10
+ }: {
11
+ config: AnyConfigurationModel
12
+ pluginManager: PluginManager
13
+ }): Promise<string[]> {
14
+ const result = await getAdapter(pluginManager, 'msa', config)
15
+
16
+ // @ts-expect-error
17
+ return result.dataAdapter.getMSAList()
18
+ }
19
+
20
+ export async function fetchMSA({
21
+ config,
22
+ pluginManager,
23
+ msaId,
24
+ }: {
25
+ config: AnyConfigurationModel
26
+ pluginManager: PluginManager
27
+ msaId: string
28
+ }): Promise<Feature[]> {
29
+ const result = await getAdapter(pluginManager, 'msa', config)
30
+
31
+ // @ts-expect-error
32
+ return result.dataAdapter.getMSA(msaId)
33
+ }
@@ -0,0 +1,25 @@
1
+ import { getId } from '../../util'
2
+
3
+ import type { Feature } from '@jbrowse/core/util'
4
+
5
+ // Function to find a valid transcript ID that exists in the MSA list
6
+ export function findValidTranscriptId({
7
+ transcriptsList,
8
+ validMsaList,
9
+ }: {
10
+ transcriptsList: Feature[]
11
+ validMsaList?: string[]
12
+ }) {
13
+ if (!validMsaList || validMsaList.length === 0) {
14
+ return null
15
+ }
16
+
17
+ // Try to find a transcript ID that exists in the MSA list
18
+ for (const transcript of transcriptsList) {
19
+ const id = getId(transcript)
20
+ if (id && validMsaList.includes(id)) {
21
+ return id
22
+ }
23
+ }
24
+ return null
25
+ }
@@ -1,38 +1,27 @@
1
1
  import { AbstractSessionModel, Feature } from '@jbrowse/core/util'
2
- import { ungzip } from 'pako'
3
2
 
4
3
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
5
4
 
6
- async function myfetch(url: string) {
7
- const res = await fetch(url)
8
- if (!res.ok) {
9
- throw new Error(`HTTP ${res.status} fetching ${await res.text()}`)
10
- }
11
- const data = await res.arrayBuffer()
12
- return new TextDecoder().decode(ungzip(data))
13
- }
14
-
15
- export async function preCalculatedLaunchView({
16
- userSelection,
5
+ export function preCalculatedLaunchView({
17
6
  session,
18
7
  newViewTitle,
19
8
  view,
20
9
  feature,
10
+ data,
11
+ querySeqName,
21
12
  }: {
13
+ data: { msa: string }
22
14
  session: AbstractSessionModel
23
- userSelection: string
24
15
  newViewTitle: string
25
16
  view: LinearGenomeViewModel
26
17
  feature: Feature
18
+ querySeqName: string
27
19
  }) {
28
- const d = await myfetch(
29
- `https://jbrowse.org/demos/msaview/knownCanonical/${userSelection}.mfa.gz`,
30
- )
31
-
32
20
  session.addView('MsaView', {
33
21
  type: 'MsaView',
34
22
  displayName: newViewTitle,
35
23
  treeAreaWidth: 200,
24
+ querySeqName,
36
25
  treeWidth: 100,
37
26
  drawNodeBubbles: false,
38
27
  labelsAlignRight: true,
@@ -40,15 +29,7 @@ export async function preCalculatedLaunchView({
40
29
  colWidth: 10,
41
30
  rowHeight: 12,
42
31
  colorSchemeName: 'percent_identity_dynamic',
43
- treeFilehandle: {
44
- uri: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/multiz100way/hg38.100way.nh',
45
- },
46
- treeMetadataFilehandle: {
47
- uri: 'https://s3.amazonaws.com/jbrowse.org/demos/app/species.json',
48
- },
49
- data: {
50
- msa: d,
51
- },
32
+ data,
52
33
  connectedViewId: view.id,
53
34
  connectedFeature: feature.toJSON(),
54
35
  })
@@ -0,0 +1,8 @@
1
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration'
2
+
3
+ export interface Dataset {
4
+ datasetId: string
5
+ name: string
6
+ description?: string
7
+ adapter: AnyConfigurationModel
8
+ }
@@ -4,8 +4,8 @@ import { Feature } from '@jbrowse/core/util'
4
4
  import { Button, MenuItem } from '@mui/material'
5
5
  import { makeStyles } from 'tss-react/mui'
6
6
 
7
- import ReadOnlyTextField2 from '../../ReadOnlyTextField2'
8
- import TextField2 from '../../TextField2'
7
+ import ReadOnlyTextField2 from '../../components/ReadOnlyTextField2'
8
+ import TextField2 from '../../components/TextField2'
9
9
  import {
10
10
  getGeneDisplayName,
11
11
  getId,
@@ -41,13 +41,13 @@ export function revlist(list: Feat[], seqlen: number) {
41
41
  }
42
42
 
43
43
  export function getProteinSequenceFromFeature({
44
- selectedTranscript,
44
+ feature,
45
45
  seq,
46
46
  }: {
47
47
  seq: string
48
- selectedTranscript: Feature
48
+ feature: Feature
49
49
  }) {
50
- const { subfeatures, start, strand } = selectedTranscript.toJSON()
50
+ const { subfeatures, start, strand } = feature.toJSON()
51
51
  const cds = dedupe(
52
52
  subfeatures
53
53
  ?.toSorted((a, b) => a.start - b.start)
@@ -8,4 +8,5 @@ export interface SeqState {
8
8
  seq: string
9
9
  upstream?: string
10
10
  downstream?: string
11
+ error?: unknown
11
12
  }
@@ -1,18 +1,8 @@
1
- import { useEffect, useState } from 'react'
2
-
3
- import { getSession } from '@jbrowse/core/util'
4
-
5
1
  import { getProteinSequenceFromFeature } from './calculateProteinSequence'
6
- import { fetchSeq } from './fetchSeq'
2
+ import { useSWRFeatureSequence } from './useSWRFeatureSequence'
7
3
 
8
- import type { SeqState } from './types'
9
4
  import type { Feature } from '@jbrowse/core/util'
10
5
 
11
- export interface ErrorState {
12
- error: string
13
- }
14
- const BPLIMIT = 500_000
15
-
16
6
  export function useFeatureSequence({
17
7
  view,
18
8
  feature,
@@ -20,73 +10,22 @@ export function useFeatureSequence({
20
10
  forceLoad = true,
21
11
  }: {
22
12
  view: { assemblyNames?: string[] } | undefined
23
- feature: Feature
13
+ feature?: Feature
24
14
  upDownBp?: number
25
15
  forceLoad?: boolean
26
16
  }) {
27
- const [sequence, setSequence] = useState<SeqState | ErrorState>()
28
- const [error, setError] = useState<unknown>()
29
- const assemblyName = view?.assemblyNames?.[0]
30
- useEffect(() => {
31
- if (view) {
32
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
33
- ;(async () => {
34
- try {
35
- setError(undefined)
36
- setSequence(undefined)
37
- const session = getSession(view)
38
- const { start, end, refName } = feature.toJSON() as {
39
- start: number
40
- end: number
41
- refName: string
42
- }
43
- if (!assemblyName) {
44
- throw new Error('No assembly found')
45
- }
46
- if (!forceLoad && end - start > BPLIMIT) {
47
- setSequence({
48
- error: `Genomic sequence larger than ${BPLIMIT}bp, use "force load" button to display`,
49
- })
50
- } else {
51
- const b = start - upDownBp
52
- const e = end + upDownBp
53
- setSequence({
54
- seq: await fetchSeq({
55
- start,
56
- end,
57
- refName,
58
- assemblyName,
59
- session,
60
- }),
61
- upstream: await fetchSeq({
62
- start: Math.max(0, b),
63
- end: start,
64
- refName,
65
- assemblyName,
66
- session,
67
- }),
68
- downstream: await fetchSeq({
69
- start: end,
70
- end: e,
71
- refName,
72
- assemblyName,
73
- session,
74
- }),
75
- })
76
- }
77
- } catch (e) {
78
- console.error(e)
79
- setError(e)
80
- }
81
- })()
82
- }
83
- }, [feature, view, upDownBp, assemblyName, forceLoad])
17
+ const { sequence, error } = useSWRFeatureSequence({
18
+ view,
19
+ feature,
20
+ upDownBp,
21
+ forceLoad,
22
+ })
84
23
 
85
24
  const proteinSequence =
86
- sequence && !('error' in sequence)
25
+ sequence && !('error' in sequence) && feature
87
26
  ? getProteinSequenceFromFeature({
88
27
  seq: sequence.seq,
89
- selectedTranscript: feature,
28
+ feature,
90
29
  })
91
30
  : ''
92
31
 
@@ -0,0 +1,90 @@
1
+ import { getSession } from '@jbrowse/core/util'
2
+ import useSWR from 'swr'
3
+
4
+ import { fetchSeq } from './fetchSeq'
5
+
6
+ import type { SeqState } from './types'
7
+ import type { Feature } from '@jbrowse/core/util'
8
+
9
+ const BPLIMIT = 500_000
10
+
11
+ async function featureSequenceFetcher({
12
+ feature,
13
+ assemblyName,
14
+ upDownBp,
15
+ view,
16
+ forceLoad,
17
+ }: {
18
+ feature: Feature
19
+ assemblyName: string
20
+ upDownBp: number
21
+ view: { assemblyNames?: string[] }
22
+ forceLoad: boolean
23
+ }): Promise<SeqState | undefined> {
24
+ const session = getSession(view)
25
+ const { start, end, refName } = feature.toJSON() as {
26
+ start: number
27
+ end: number
28
+ refName: string
29
+ }
30
+
31
+ const b = start - upDownBp
32
+ const e = end + upDownBp
33
+ const [seq, upstream, downstream] = await Promise.all([
34
+ fetchSeq({
35
+ start,
36
+ end,
37
+ refName,
38
+ assemblyName,
39
+ session,
40
+ }),
41
+ fetchSeq({
42
+ start: Math.max(0, b),
43
+ end: start,
44
+ refName,
45
+ assemblyName,
46
+ session,
47
+ }),
48
+ fetchSeq({
49
+ start: end,
50
+ end: e,
51
+ refName,
52
+ assemblyName,
53
+ session,
54
+ }),
55
+ ])
56
+ return { seq, upstream, downstream }
57
+ }
58
+
59
+ export function useSWRFeatureSequence({
60
+ view,
61
+ feature,
62
+ upDownBp = 0,
63
+ forceLoad = true,
64
+ }: {
65
+ view: { assemblyNames?: string[] } | undefined
66
+ feature?: Feature
67
+ upDownBp?: number
68
+ forceLoad?: boolean
69
+ }) {
70
+ const assemblyName = view?.assemblyNames?.[0]
71
+ const { data, error } = useSWR(
72
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
73
+ feature && assemblyName && view
74
+ ? [feature.id(), assemblyName, upDownBp, forceLoad, 'feature-sequence']
75
+ : null,
76
+ () =>
77
+ featureSequenceFetcher({
78
+ feature: feature!,
79
+ assemblyName: assemblyName!,
80
+ upDownBp,
81
+ view: view!,
82
+ forceLoad,
83
+ }),
84
+ )
85
+
86
+ return {
87
+ sequence: data,
88
+ error,
89
+ }
90
+ }
@@ -24,9 +24,12 @@ function extendStateModel(stateModel: IAnyModelType) {
24
24
  contextMenuItems() {
25
25
  const feature = self.contextMenuFeature
26
26
  const track = getContainingTrack(self)
27
+ const featureType = feature?.get('type')
28
+ const showMsaMenuItem =
29
+ feature && ['gene', 'mRNA', 'transcript'].includes(featureType)
27
30
  return [
28
31
  ...superContextMenuItems(),
29
- ...(feature
32
+ ...(showMsaMenuItem
30
33
  ? [
31
34
  {
32
35
  label: 'Launch MSA view',
@@ -2,7 +2,7 @@ import React from 'react'
2
2
 
3
3
  import { Typography } from '@mui/material'
4
4
 
5
- import ExternalLink from '../../ExternalLink'
5
+ import ExternalLink from '../../components/ExternalLink'
6
6
 
7
7
  function RIDLink({ baseUrl, rid }: { rid: string; baseUrl: string }) {
8
8
  return (
@@ -5,7 +5,7 @@ import { checkHovered } from './util'
5
5
 
6
6
  export function genomeToMSA({ model }: { model: JBrowsePluginMsaViewModel }) {
7
7
  const { hovered } = getSession(model)
8
- const { transcriptToMsaMap, connectedView } = model
8
+ const { querySeqName, transcriptToMsaMap, connectedView } = model
9
9
  if (
10
10
  connectedView?.initialized &&
11
11
  transcriptToMsaMap &&
@@ -15,7 +15,7 @@ export function genomeToMSA({ model }: { model: JBrowsePluginMsaViewModel }) {
15
15
  const { g2p } = transcriptToMsaMap
16
16
  const ret = g2p[hoverCoord]
17
17
  if (ret !== undefined) {
18
- return model.seqCoordToRowSpecificGlobalCoord('QUERY', ret + 1)
18
+ return model.seqCoordToRowSpecificGlobalCoord(querySeqName, ret + 1)
19
19
  }
20
20
  }
21
21
  return undefined