jbrowse-plugin-msaview 2.2.2 → 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 +727 -226
  70. package/dist/MsaViewPanel/model.js +496 -52
  71. package/dist/MsaViewPanel/model.js.map +1 -1
  72. package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +10 -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 +617 -58
  144. package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +256 -0
  145. package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +42 -30
  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 -55367
  162. package/src/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.ts +0 -25
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useState } from 'react'
1
+ import React, { useState } from 'react'
2
2
 
3
3
  import { readConfObject } from '@jbrowse/core/configuration'
4
4
  import { ErrorMessage, LoadingEllipses, SanitizedHTML } from '@jbrowse/core/ui'
@@ -15,12 +15,11 @@ import useSWR from 'swr'
15
15
  import { makeStyles } from 'tss-react/mui'
16
16
 
17
17
  import TextField2 from '../../../components/TextField2'
18
- import { getGeneDisplayName, getId, getTranscriptFeatures } from '../../util'
18
+ import { getGeneDisplayName } from '../../util'
19
19
  import TranscriptSelector from '../TranscriptSelector'
20
- import { useFeatureSequence } from '../useFeatureSequence'
20
+ import { useTranscriptSelection } from '../useTranscriptSelection'
21
21
  import { swrFlags } from './consts'
22
22
  import { fetchMSA, fetchMSAList } from './fetchMSAData'
23
- import { findValidTranscriptId } from './findValidTranscriptId'
24
23
  import { preCalculatedLaunchView } from './preCalculatedLaunchView'
25
24
  import { Dataset } from './types'
26
25
 
@@ -46,18 +45,7 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
46
45
  const { classes } = useStyles()
47
46
  const { pluginManager } = getEnv(model)
48
47
  const { assemblyNames } = view
49
- const transcripts = getTranscriptFeatures(feature)
50
- const [selectedTranscriptId, setSelectedTranscriptId] = useState(
51
- getId(transcripts[0]),
52
- )
53
48
  const [viewError, setViewError] = useState<unknown>()
54
- const selectedTranscript = selectedTranscriptId
55
- ? transcripts.find(val => getId(val) === selectedTranscriptId)
56
- : undefined
57
- const { proteinSequence, error: proteinSequenceError } = useFeatureSequence({
58
- view,
59
- feature: selectedTranscript,
60
- })
61
49
 
62
50
  const { jbrowse } = session
63
51
  const datasets = readConfObject(jbrowse, ['msa', 'datasets']) as
@@ -82,18 +70,29 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
82
70
  : undefined,
83
71
  swrFlags,
84
72
  )
73
+
74
+ const {
75
+ options: transcripts,
76
+ selectedId,
77
+ setSelectedId,
78
+ selectedTranscript,
79
+ proteinSequence,
80
+ error: proteinSequenceError,
81
+ validSet,
82
+ } = useTranscriptSelection({ feature, view, validIds: msaList })
83
+
85
84
  const {
86
85
  data: msaData,
87
86
  isLoading: msaDataLoading,
88
87
  error: msaDataFetchError,
89
88
  } = useSWR(
90
- selectedTranscriptId && selectedDatasetId
91
- ? `${selectedTranscriptId}-${selectedTranscriptId}-${msaList?.length}-msa`
89
+ selectedId && selectedDatasetId
90
+ ? `${selectedId}-${selectedId}-${msaList?.length}-msa`
92
91
  : 'none-msa',
93
92
  () =>
94
- selectedTranscriptId && selectedDataset && msaList
93
+ selectedId && selectedDataset && msaList
95
94
  ? fetchMSA({
96
- msaId: selectedTranscriptId,
95
+ msaId: selectedId,
97
96
  config: selectedDataset.adapter,
98
97
  pluginManager,
99
98
  })
@@ -101,21 +100,6 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
101
100
  swrFlags,
102
101
  )
103
102
 
104
- const validSet = useMemo(() => new Set(msaList), [msaList])
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
103
  const e =
120
104
  msaListFetchError ?? msaDataFetchError ?? proteinSequenceError ?? viewError
121
105
  if (e) {
@@ -134,15 +118,11 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
134
118
  setSelectedDatasetId(event.target.value)
135
119
  }}
136
120
  >
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
- )}
121
+ {datasets?.map(d => (
122
+ <MenuItem key={d.datasetId} value={d.datasetId}>
123
+ {d.name}
124
+ </MenuItem>
125
+ ))}
146
126
  </TextField2>
147
127
 
148
128
  {selectedDataset ? (
@@ -150,7 +130,7 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
150
130
  {!msaListLoading && msaDataLoading ? (
151
131
  <LoadingEllipses
152
132
  variant="h6"
153
- message={`Loading MSA for (${selectedTranscriptId})`}
133
+ message={`Loading MSA for (${selectedId})`}
154
134
  />
155
135
  ) : null}
156
136
  {msaListLoading ? (
@@ -166,8 +146,8 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
166
146
  <TranscriptSelector
167
147
  feature={feature}
168
148
  options={transcripts}
169
- selectedTranscriptId={selectedTranscriptId}
170
- onTranscriptChange={setSelectedTranscriptId}
149
+ selectedTranscript={selectedTranscript}
150
+ onTranscriptChange={setSelectedId}
171
151
  proteinSequence={proteinSequence}
172
152
  validSet={validSet}
173
153
  />
@@ -187,7 +167,7 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
187
167
  if (!selectedTranscript || !msaData) {
188
168
  return
189
169
  }
190
- const querySeqName = `${selectedTranscriptId}_${assemblyNames[0]}`
170
+ const querySeqName = `${selectedId}_${assemblyNames[0]}`
191
171
  preCalculatedLaunchView({
192
172
  session,
193
173
  newViewTitle: getGeneDisplayName(selectedTranscript),
@@ -1,6 +1,7 @@
1
1
  export const swrFlags = {
2
2
  revalidateOnFocus: false,
3
3
  revalidateOnReconnect: false,
4
+ revalidateIfStale: false,
4
5
  refreshWhenHidden: false,
5
6
  refreshWhenOffline: false,
6
7
  shouldRetryOnError: false,
@@ -8,8 +8,8 @@ export default function TabPanel({
8
8
  ...other
9
9
  }: {
10
10
  children?: React.ReactNode
11
- index: number
12
- value: number
11
+ index: number | string
12
+ value: number | string
13
13
  }) {
14
14
  return (
15
15
  <div role="tabpanel" hidden={value !== index} {...other}>
@@ -25,23 +25,20 @@ const useStyles = makeStyles()({
25
25
  export default function TranscriptSelector({
26
26
  feature,
27
27
  options,
28
- selectedTranscriptId,
28
+ selectedTranscript,
29
29
  onTranscriptChange,
30
30
  proteinSequence,
31
31
  validSet,
32
32
  }: {
33
33
  feature: Feature
34
34
  options: Feature[]
35
- selectedTranscriptId: string
35
+ selectedTranscript: Feature | undefined
36
36
  onTranscriptChange: (transcriptId: string) => void
37
37
  proteinSequence: string | undefined
38
38
  validSet?: Set<string>
39
39
  }) {
40
40
  const { classes } = useStyles()
41
41
  const [showSequence, setShowSequence] = useState(false)
42
- const selectedTranscript = options.find(
43
- val => getId(val) === selectedTranscriptId,
44
- )!
45
42
 
46
43
  return (
47
44
  <>
@@ -51,26 +48,22 @@ export default function TranscriptSelector({
51
48
  label={`Choose isoform of ${getGeneDisplayName(feature)}`}
52
49
  select
53
50
  className={classes.minWidth}
54
- value={selectedTranscriptId}
51
+ value={getId(selectedTranscript)}
55
52
  onChange={event => {
56
53
  onTranscriptChange(event.target.value)
57
54
  }}
58
55
  >
59
- {options
60
- .toSorted(
61
- (a, b) => getTranscriptLength(b).len - getTranscriptLength(a).len,
56
+ {options.map(val => {
57
+ const inSet = validSet ? validSet.has(getId(val)) : true
58
+ const { len, mod } = getTranscriptLength(val)
59
+ return (
60
+ <MenuItem value={getId(val)} key={val.id()} disabled={!inSet}>
61
+ {getTranscriptDisplayName(val)} ({len} aa){' '}
62
+ {mod ? ` (possible fragment)` : ''}
63
+ {validSet ? (inSet ? ' (has data)' : ' (no data)') : ''}
64
+ </MenuItem>
62
65
  )
63
- .map(val => {
64
- const inSet = validSet ? validSet.has(getId(val)) : true
65
- const { len, mod } = getTranscriptLength(val)
66
- return (
67
- <MenuItem value={getId(val)} key={val.id()} disabled={!inSet}>
68
- {getTranscriptDisplayName(val)} ({len} aa){' '}
69
- {mod ? ` (possible fragment)` : ''}
70
- {validSet ? (inSet ? ' (has data)' : ' (no data)') : ''}
71
- </MenuItem>
72
- )
73
- })}
66
+ })}
74
67
  </TextField2>
75
68
  <div style={{ alignContent: 'center', marginLeft: 20 }}>
76
69
  <Button
@@ -6,20 +6,16 @@ import { fetchSeq } from './fetchSeq'
6
6
  import type { SeqState } from './types'
7
7
  import type { Feature } from '@jbrowse/core/util'
8
8
 
9
- const BPLIMIT = 500_000
10
-
11
9
  async function featureSequenceFetcher({
12
10
  feature,
13
11
  assemblyName,
14
12
  upDownBp,
15
13
  view,
16
- forceLoad,
17
14
  }: {
18
15
  feature: Feature
19
16
  assemblyName: string
20
17
  upDownBp: number
21
18
  view: { assemblyNames?: string[] }
22
- forceLoad: boolean
23
19
  }): Promise<SeqState | undefined> {
24
20
  const session = getSession(view)
25
21
  const { start, end, refName } = feature.toJSON() as {
@@ -79,8 +75,12 @@ export function useSWRFeatureSequence({
79
75
  assemblyName: assemblyName!,
80
76
  upDownBp,
81
77
  view: view!,
82
- forceLoad,
83
78
  }),
79
+ {
80
+ revalidateOnFocus: false,
81
+ revalidateOnReconnect: false,
82
+ revalidateIfStale: false,
83
+ },
84
84
  )
85
85
 
86
86
  return {
@@ -0,0 +1,48 @@
1
+ import { useEffect, useMemo, useState } from 'react'
2
+
3
+ import { getId, getSortedTranscriptFeatures } from '../util'
4
+ import { useFeatureSequence } from './useFeatureSequence'
5
+
6
+ import type { Feature } from '@jbrowse/core/util'
7
+
8
+ export function useTranscriptSelection({
9
+ feature,
10
+ view,
11
+ validIds,
12
+ }: {
13
+ feature: Feature
14
+ view: { assemblyNames?: string[] } | undefined
15
+ validIds?: string[]
16
+ }) {
17
+ const options = getSortedTranscriptFeatures(feature)
18
+ const [selectedId, setSelectedId] = useState(getId(options[0]))
19
+ const selectedTranscript = options.find(val => getId(val) === selectedId)
20
+ const { proteinSequence, error } = useFeatureSequence({
21
+ view,
22
+ feature: selectedTranscript,
23
+ })
24
+
25
+ const validSet = useMemo(
26
+ () => (validIds ? new Set(validIds) : undefined),
27
+ [validIds],
28
+ )
29
+
30
+ useEffect(() => {
31
+ if (validIds && validIds.length > 0 && !validIds.includes(selectedId)) {
32
+ const validOption = options.find(opt => validIds.includes(getId(opt)))
33
+ if (validOption) {
34
+ setSelectedId(getId(validOption))
35
+ }
36
+ }
37
+ }, [validIds, options, selectedId])
38
+
39
+ return {
40
+ options,
41
+ selectedId,
42
+ setSelectedId,
43
+ selectedTranscript,
44
+ proteinSequence,
45
+ error,
46
+ validSet,
47
+ }
48
+ }
@@ -1,5 +1,20 @@
1
- export function makeId(h: { accession: string; sciname: string }) {
2
- return `${h.accession}-${h.sciname.replaceAll(' ', '_')}`
1
+ import type { TaxonomyInfo } from '../../utils/taxonomyNames'
2
+
3
+ export function makeId(
4
+ h: { accession: string; sciname: string; taxid?: number },
5
+ taxonomyInfo?: Map<number, TaxonomyInfo>,
6
+ ) {
7
+ let speciesName = h.sciname.replaceAll(' ', '_')
8
+ if (h.taxid && taxonomyInfo?.has(h.taxid)) {
9
+ const info = taxonomyInfo.get(h.taxid)!
10
+ if (info.commonName) {
11
+ speciesName = info.commonName
12
+ .split(' ')
13
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
14
+ .join('_')
15
+ }
16
+ }
17
+ return `${h.accession}-${speciesName}`
3
18
  }
4
19
 
5
20
  export function strip(s: string) {
@@ -7,7 +7,7 @@ import AddIcon from '@mui/icons-material/Add'
7
7
 
8
8
  import LaunchMsaViewDialog from './components/LaunchMsaViewDialog'
9
9
 
10
- import type { IAnyModelType } from 'mobx-state-tree'
10
+ import type { IAnyModelType } from '@jbrowse/mobx-state-tree'
11
11
 
12
12
  function isDisplay(elt: { name: string }): elt is DisplayType {
13
13
  return elt.name === 'LinearBasicDisplay'
@@ -6,12 +6,21 @@ export function getTranscriptFeatures(feature: Feature) {
6
6
  // check if we are looking at a 'two-level' or 'three-level' feature by
7
7
  // finding exon/CDS subfeatures. we want to select from transcript names
8
8
  const subfeatures = feature.get('subfeatures') ?? []
9
- return subfeatures.some(f => f.get('type') === 'CDS')
10
- ? [feature]
11
- : // filter out non-coding by finding subfeatures with CDS subfeatures
12
- subfeatures.filter(f =>
13
- f.get('subfeatures')?.some(f => f.get('type') === 'CDS'),
14
- )
9
+
10
+ // Check for mRNA/transcript subfeatures (three-level: gene → mRNA → CDS)
11
+ // Filter to only those that have CDS subfeatures (i.e. are coding)
12
+ const transcripts = subfeatures.filter(
13
+ (f: Feature) =>
14
+ (f.get('type') === 'mRNA' || f.get('type') === 'transcript') &&
15
+ f.get('subfeatures')?.some((s: Feature) => s.get('type') === 'CDS'),
16
+ )
17
+ if (transcripts.length > 0) {
18
+ return transcripts
19
+ }
20
+
21
+ // Has direct CDS children, treat feature itself as the transcript
22
+ // (two-level: gene → CDS or mRNA → CDS)
23
+ return [feature]
15
24
  }
16
25
 
17
26
  export function getTranscriptLength(feature: Feature) {
@@ -48,3 +57,13 @@ export function getGeneDisplayName(val?: Feature) {
48
57
  .filter(f => !!f)
49
58
  .join(' ')
50
59
  }
60
+
61
+ export function getSortedTranscriptFeatures(feature: Feature) {
62
+ return getTranscriptFeatures(feature).toSorted(
63
+ (a, b) => getTranscriptLength(b).len - getTranscriptLength(a).len,
64
+ )
65
+ }
66
+
67
+ export function cleanProteinSequence(seq: string) {
68
+ return seq.replaceAll('*', '').replaceAll('&', '')
69
+ }
@@ -0,0 +1,74 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager'
2
+ import type { AbstractSessionModel } from '@jbrowse/core/util'
3
+
4
+ export default function LaunchMsaViewExtensionPointF(
5
+ pluginManager: PluginManager,
6
+ ) {
7
+ pluginManager.addToExtensionPoint(
8
+ 'LaunchView-MsaView',
9
+ // @ts-expect-error
10
+ ({
11
+ session,
12
+ data,
13
+ msaFileLocation,
14
+ treeFileLocation,
15
+ connectedViewId,
16
+ connectedFeature,
17
+ displayName,
18
+ colorSchemeName,
19
+ colWidth,
20
+ rowHeight,
21
+ treeAreaWidth,
22
+ treeWidth,
23
+ drawNodeBubbles,
24
+ labelsAlignRight,
25
+ showBranchLen,
26
+ querySeqName,
27
+ }: {
28
+ session: AbstractSessionModel
29
+ data?: { msa: string; tree?: string }
30
+ msaFileLocation?: { uri: string }
31
+ treeFileLocation?: { uri: string }
32
+ connectedViewId?: string
33
+ connectedFeature?: Record<string, unknown>
34
+ displayName?: string
35
+ colorSchemeName?: string
36
+ colWidth?: number
37
+ rowHeight?: number
38
+ treeAreaWidth?: number
39
+ treeWidth?: number
40
+ drawNodeBubbles?: boolean
41
+ labelsAlignRight?: boolean
42
+ showBranchLen?: boolean
43
+ querySeqName?: string
44
+ }) => {
45
+ if (!data && !msaFileLocation) {
46
+ throw new Error(
47
+ 'No MSA data or file location provided when launching MSA view',
48
+ )
49
+ }
50
+
51
+ session.addView('MsaView', {
52
+ type: 'MsaView',
53
+ displayName,
54
+ connectedViewId,
55
+ connectedFeature,
56
+ colorSchemeName,
57
+ colWidth,
58
+ rowHeight,
59
+ treeAreaWidth,
60
+ treeWidth,
61
+ drawNodeBubbles,
62
+ labelsAlignRight,
63
+ showBranchLen,
64
+ init: {
65
+ msaData: data?.msa,
66
+ treeData: data?.tree,
67
+ msaUrl: msaFileLocation?.uri,
68
+ treeUrl: treeFileLocation?.uri,
69
+ querySeqName,
70
+ },
71
+ })
72
+ },
73
+ )
74
+ }
@@ -0,0 +1,156 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import { Dialog } from '@jbrowse/core/ui'
4
+ import { getSession } from '@jbrowse/core/util'
5
+ import {
6
+ Button,
7
+ DialogActions,
8
+ DialogContent,
9
+ FormControl,
10
+ InputLabel,
11
+ MenuItem,
12
+ Select,
13
+ Typography,
14
+ } from '@mui/material'
15
+ import { observer } from 'mobx-react'
16
+
17
+ import type { JBrowsePluginMsaViewModel } from '../model'
18
+
19
+ const ConnectStructureDialog = observer(function ConnectStructureDialog({
20
+ model,
21
+ handleClose,
22
+ }: {
23
+ model: JBrowsePluginMsaViewModel
24
+ handleClose: () => void
25
+ }) {
26
+ const session = getSession(model)
27
+ const [selectedViewId, setSelectedViewId] = useState('')
28
+ const [selectedStructureIdx, setSelectedStructureIdx] = useState(0)
29
+ const [selectedMsaRow, setSelectedMsaRow] = useState(model.querySeqName)
30
+ const [error, setError] = useState<string>()
31
+
32
+ // Find all ProteinViews in the session
33
+
34
+ const proteinViews = session.views.filter(
35
+ (v: any) => v.type === 'ProteinView',
36
+ ) as any[]
37
+
38
+ // Get structures for the selected view
39
+ const selectedView = proteinViews.find(v => v.id === selectedViewId)
40
+ const structures = selectedView?.structures ?? []
41
+
42
+ // Get MSA row names
43
+ const msaRowNames = model.rows.map(r => r[0])
44
+
45
+ const handleConnect = () => {
46
+ if (!selectedViewId) {
47
+ setError('Please select a protein view')
48
+ return
49
+ }
50
+
51
+ try {
52
+ model.connectToStructure(
53
+ selectedViewId,
54
+ selectedStructureIdx,
55
+ selectedMsaRow,
56
+ )
57
+ handleClose()
58
+ } catch (e) {
59
+ setError(e instanceof Error ? e.message : String(e))
60
+ }
61
+ }
62
+
63
+ return (
64
+ <Dialog
65
+ maxWidth="sm"
66
+ title="Connect to Protein Structure"
67
+ open
68
+ onClose={handleClose}
69
+ >
70
+ <DialogContent>
71
+ {proteinViews.length === 0 ? (
72
+ <Typography color="textSecondary">
73
+ No protein views are currently open. Please open a protein structure
74
+ view first.
75
+ </Typography>
76
+ ) : (
77
+ <>
78
+ <FormControl fullWidth sx={{ mb: 2 }}>
79
+ <InputLabel>Protein View</InputLabel>
80
+ <Select
81
+ value={selectedViewId}
82
+ label="Protein View"
83
+ onChange={e => {
84
+ setSelectedViewId(e.target.value)
85
+ setSelectedStructureIdx(0)
86
+ }}
87
+ >
88
+ {proteinViews.map(view => (
89
+ <MenuItem key={view.id} value={view.id}>
90
+ {view.displayName ?? `ProteinView ${view.id}`}
91
+ </MenuItem>
92
+ ))}
93
+ </Select>
94
+ </FormControl>
95
+
96
+ {structures.length > 1 && (
97
+ <FormControl fullWidth sx={{ mb: 2 }}>
98
+ <InputLabel>Structure</InputLabel>
99
+ <Select
100
+ value={selectedStructureIdx}
101
+ label="Structure"
102
+ onChange={e => {
103
+ setSelectedStructureIdx(e.target.value)
104
+ }}
105
+ >
106
+ {structures.map(
107
+ (structure: { url?: string }, idx: number) => (
108
+ <MenuItem key={idx} value={idx}>
109
+ {structure.url ?? `Structure ${idx + 1}`}
110
+ </MenuItem>
111
+ ),
112
+ )}
113
+ </Select>
114
+ </FormControl>
115
+ )}
116
+
117
+ <FormControl fullWidth sx={{ mb: 2 }}>
118
+ <InputLabel>MSA Row</InputLabel>
119
+ <Select
120
+ value={selectedMsaRow}
121
+ label="MSA Row"
122
+ onChange={e => {
123
+ setSelectedMsaRow(e.target.value)
124
+ }}
125
+ >
126
+ {msaRowNames.map(name => (
127
+ <MenuItem key={name} value={name}>
128
+ {name}
129
+ </MenuItem>
130
+ ))}
131
+ </Select>
132
+ </FormControl>
133
+
134
+ {error && (
135
+ <Typography color="error" sx={{ mt: 1 }}>
136
+ {error}
137
+ </Typography>
138
+ )}
139
+ </>
140
+ )}
141
+ </DialogContent>
142
+ <DialogActions>
143
+ <Button onClick={handleClose}>Cancel</Button>
144
+ <Button
145
+ onClick={handleConnect}
146
+ variant="contained"
147
+ disabled={proteinViews.length === 0 || !selectedViewId}
148
+ >
149
+ Connect
150
+ </Button>
151
+ </DialogActions>
152
+ </Dialog>
153
+ )
154
+ })
155
+
156
+ export default ConnectStructureDialog
@@ -1,5 +1,6 @@
1
1
  import React from 'react'
2
2
 
3
+ import { LoadingEllipses } from '@jbrowse/core/ui'
3
4
  import { observer } from 'mobx-react'
4
5
  import { MSAView } from 'react-msaview'
5
6
 
@@ -11,11 +12,15 @@ const MsaViewPanel = observer(function MsaViewPanel2({
11
12
  }: {
12
13
  model: JBrowsePluginMsaViewModel
13
14
  }) {
14
- const { blastParams } = model
15
+ const { blastParams, loadingStoredData } = model
15
16
  return (
16
17
  <div>
17
18
  {blastParams ? (
18
19
  <LoadingBLAST model={model} baseUrl={blastParams.baseUrl} />
20
+ ) : loadingStoredData ? (
21
+ <div style={{ padding: 20 }}>
22
+ <LoadingEllipses message="Loading MSA data" variant="h6" />
23
+ </div>
19
24
  ) : (
20
25
  <MSAView model={model} />
21
26
  )}