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.
- package/CHANGELOG.md +1 -1
- package/README.md +229 -0
- package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +23 -18
- package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -1
- package/dist/AddHighlightModel/MsaToGenomeHighlight.js +23 -13
- package/dist/AddHighlightModel/MsaToGenomeHighlight.js.map +1 -1
- package/dist/AddHighlightModel/index.js +8 -1
- package/dist/AddHighlightModel/index.js.map +1 -1
- package/dist/AddHighlightModel/util.d.ts +2 -2
- package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +2 -2
- package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js +5 -11
- package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js.map +1 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js +5 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js.map +1 -1
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +16 -16
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +38 -46
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js.map +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +4 -3
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +4 -3
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.d.ts +9 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js +76 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js.map +1 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js +35 -13
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js +6 -12
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.d.ts +6 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js +15 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js.map +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +12 -34
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/consts.d.ts +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js.map +1 -1
- package/dist/LaunchMsaView/components/TabPanel.d.ts +2 -2
- package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +2 -2
- package/dist/LaunchMsaView/components/TranscriptSelector.js +3 -6
- package/dist/LaunchMsaView/components/TranscriptSelector.js.map +1 -1
- package/dist/LaunchMsaView/components/useSWRFeatureSequence.js +6 -4
- package/dist/LaunchMsaView/components/useSWRFeatureSequence.js.map +1 -1
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +16 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +31 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.js.map +1 -0
- package/dist/LaunchMsaView/components/util.d.ts +3 -1
- package/dist/LaunchMsaView/components/util.js +12 -2
- package/dist/LaunchMsaView/components/util.js.map +1 -1
- package/dist/LaunchMsaView/util.d.ts +2 -0
- package/dist/LaunchMsaView/util.js +16 -4
- package/dist/LaunchMsaView/util.js.map +1 -1
- package/dist/LaunchMsaViewExtensionPoint/index.d.ts +2 -0
- package/dist/LaunchMsaViewExtensionPoint/index.js +31 -0
- package/dist/LaunchMsaViewExtensionPoint/index.js.map +1 -0
- package/dist/MsaViewPanel/components/ConnectStructureDialog.d.ts +7 -0
- package/dist/MsaViewPanel/components/ConnectStructureDialog.js +56 -0
- package/dist/MsaViewPanel/components/ConnectStructureDialog.js.map +1 -0
- package/dist/MsaViewPanel/components/MsaViewPanel.js +4 -2
- package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -1
- package/dist/MsaViewPanel/doLaunchBlast.d.ts +1 -0
- package/dist/MsaViewPanel/doLaunchBlast.js +65 -19
- package/dist/MsaViewPanel/doLaunchBlast.js.map +1 -1
- package/dist/MsaViewPanel/genomeToMSA.d.ts +6 -0
- package/dist/MsaViewPanel/genomeToMSA.js +38 -8
- package/dist/MsaViewPanel/genomeToMSA.js.map +1 -1
- package/dist/MsaViewPanel/genomeToMSA.test.d.ts +1 -0
- package/dist/MsaViewPanel/genomeToMSA.test.js +244 -0
- package/dist/MsaViewPanel/genomeToMSA.test.js.map +1 -0
- package/dist/MsaViewPanel/model.d.ts +727 -226
- package/dist/MsaViewPanel/model.js +496 -52
- package/dist/MsaViewPanel/model.js.map +1 -1
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +10 -2
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +26 -27
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -1
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.d.ts +1 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js +240 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js.map +1 -0
- package/dist/MsaViewPanel/msaDataStore.d.ts +14 -0
- package/dist/MsaViewPanel/msaDataStore.js +197 -0
- package/dist/MsaViewPanel/msaDataStore.js.map +1 -0
- package/dist/MsaViewPanel/pairwiseAlignment.d.ts +27 -0
- package/dist/MsaViewPanel/pairwiseAlignment.js +776 -0
- package/dist/MsaViewPanel/pairwiseAlignment.js.map +1 -0
- package/dist/MsaViewPanel/pairwiseAlignment.test.d.ts +1 -0
- package/dist/MsaViewPanel/pairwiseAlignment.test.js +112 -0
- package/dist/MsaViewPanel/pairwiseAlignment.test.js.map +1 -0
- package/dist/MsaViewPanel/structureConnection.d.ts +27 -0
- package/dist/MsaViewPanel/structureConnection.js +46 -0
- package/dist/MsaViewPanel/structureConnection.js.map +1 -0
- package/dist/MsaViewPanel/structureConnection.test.d.ts +1 -0
- package/dist/MsaViewPanel/structureConnection.test.js +122 -0
- package/dist/MsaViewPanel/structureConnection.test.js.map +1 -0
- package/dist/MsaViewPanel/types.d.ts +13 -0
- package/dist/MsaViewPanel/types.js +2 -0
- package/dist/MsaViewPanel/types.js.map +1 -0
- package/dist/MsaViewPanel/util.d.ts +7 -0
- package/dist/MsaViewPanel/util.js +10 -0
- package/dist/MsaViewPanel/util.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +39 -90
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/blastCache.d.ts +34 -0
- package/dist/utils/blastCache.js +58 -0
- package/dist/utils/blastCache.js.map +1 -0
- package/dist/utils/fetch.d.ts +1 -1
- package/dist/utils/fetch.js +1 -1
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/ncbiBlast.d.ts +1 -5
- package/dist/utils/taxonomyNames.d.ts +5 -0
- package/dist/utils/taxonomyNames.js +79 -0
- package/dist/utils/taxonomyNames.js.map +1 -0
- package/dist/utils/types.d.ts +8 -5
- package/package.json +50 -54
- package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +37 -21
- package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +38 -17
- package/src/AddHighlightModel/index.tsx +9 -4
- package/src/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.tsx +13 -13
- package/src/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.ts +6 -0
- package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +30 -23
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +64 -51
- package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +9 -6
- package/src/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.tsx +146 -0
- package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx +53 -22
- package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.tsx +8 -13
- package/src/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.ts +25 -0
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +27 -47
- package/src/LaunchMsaView/components/PreLoadedMSA/consts.ts +1 -0
- package/src/LaunchMsaView/components/TabPanel.tsx +2 -2
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +13 -20
- package/src/LaunchMsaView/components/useSWRFeatureSequence.ts +5 -5
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +48 -0
- package/src/LaunchMsaView/components/util.ts +17 -2
- package/src/LaunchMsaView/index.ts +1 -1
- package/src/LaunchMsaView/util.ts +25 -6
- package/src/LaunchMsaViewExtensionPoint/index.ts +74 -0
- package/src/MsaViewPanel/components/ConnectStructureDialog.tsx +156 -0
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +6 -1
- package/src/MsaViewPanel/doLaunchBlast.ts +83 -23
- package/src/MsaViewPanel/genomeToMSA.test.ts +281 -0
- package/src/MsaViewPanel/genomeToMSA.ts +43 -10
- package/src/MsaViewPanel/model.ts +617 -58
- package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +256 -0
- package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +42 -30
- package/src/MsaViewPanel/msaDataStore.ts +236 -0
- package/src/MsaViewPanel/pairwiseAlignment.test.ts +140 -0
- package/src/MsaViewPanel/pairwiseAlignment.ts +818 -0
- package/src/MsaViewPanel/structureConnection.test.ts +143 -0
- package/src/MsaViewPanel/structureConnection.ts +72 -0
- package/src/MsaViewPanel/types.ts +14 -0
- package/src/MsaViewPanel/util.ts +11 -0
- package/src/index.ts +3 -1
- package/src/utils/blastCache.ts +114 -0
- package/src/utils/fetch.ts +1 -1
- package/src/utils/taxonomyNames.ts +111 -0
- package/src/utils/types.ts +9 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.d.ts +0 -5
- package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js +0 -16
- package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js.map +0 -1
- package/dist/out.js +0 -55367
- package/src/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.ts +0 -25
|
@@ -1,22 +1,86 @@
|
|
|
1
|
+
import { lazy } from 'react'
|
|
2
|
+
|
|
1
3
|
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes'
|
|
2
4
|
import { getSession } from '@jbrowse/core/util'
|
|
5
|
+
import { addDisposer, cast, types } from '@jbrowse/mobx-state-tree'
|
|
3
6
|
import { genomeToTranscriptSeqMapping } from 'g2p_mapper'
|
|
4
7
|
import { autorun } from 'mobx'
|
|
5
|
-
import { addDisposer, cast, types } from 'mobx-state-tree'
|
|
6
8
|
import { MSAModelF } from 'react-msaview'
|
|
7
9
|
|
|
8
10
|
import { doLaunchBlast } from './doLaunchBlast'
|
|
9
11
|
import { genomeToMSA } from './genomeToMSA'
|
|
10
12
|
import { msaCoordToGenomeCoord } from './msaCoordToGenomeCoord'
|
|
13
|
+
import {
|
|
14
|
+
cleanupOldData,
|
|
15
|
+
generateDataStoreId,
|
|
16
|
+
retrieveMsaData,
|
|
17
|
+
storeMsaData,
|
|
18
|
+
} from './msaDataStore'
|
|
19
|
+
import { buildAlignmentMaps, runPairwiseAlignment } from './pairwiseAlignment'
|
|
20
|
+
import {
|
|
21
|
+
gappedToUngappedPosition,
|
|
22
|
+
mapToRecord,
|
|
23
|
+
ungappedToGappedPosition,
|
|
24
|
+
} from './structureConnection'
|
|
25
|
+
import { getUniprotIdFromAlphaFoldUrl } from './util'
|
|
11
26
|
|
|
27
|
+
import type { StructureConnection } from './structureConnection'
|
|
28
|
+
import type { MafRegion, MsaViewInitState } from './types'
|
|
12
29
|
import type { Feature } from '@jbrowse/core/util'
|
|
30
|
+
import type { Instance } from '@jbrowse/mobx-state-tree'
|
|
13
31
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
14
|
-
|
|
32
|
+
|
|
33
|
+
const ConnectStructureDialog = lazy(
|
|
34
|
+
() => import('./components/ConnectStructureDialog'),
|
|
35
|
+
)
|
|
15
36
|
|
|
16
37
|
type LGV = LinearGenomeViewModel
|
|
17
38
|
|
|
18
39
|
type MaybeLGV = LGV | undefined
|
|
19
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Highlights residues in connected protein structures based on current MSA hover position
|
|
43
|
+
*/
|
|
44
|
+
function highlightConnectedStructures(self: JBrowsePluginMsaViewModel) {
|
|
45
|
+
const { mouseCol, connectedProteinViews } = self
|
|
46
|
+
if (connectedProteinViews.length === 0) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const conn of connectedProteinViews) {
|
|
51
|
+
const structure = conn.proteinView?.structures?.[conn.structureIdx]
|
|
52
|
+
if (!structure) {
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Clear highlight if mouse left MSA
|
|
57
|
+
if (mouseCol === undefined) {
|
|
58
|
+
structure.clearHighlightFromExternal?.()
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const seq = self.getSequenceByRowName(conn.msaRowName)
|
|
63
|
+
if (!seq) {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Convert gapped MSA column to ungapped position
|
|
68
|
+
const msaUngapped = gappedToUngappedPosition(seq, mouseCol)
|
|
69
|
+
if (msaUngapped === undefined) {
|
|
70
|
+
structure.clearHighlightFromExternal?.()
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Map to structure position and highlight
|
|
75
|
+
const structurePos = conn.msaToStructure[msaUngapped]
|
|
76
|
+
if (structurePos === undefined) {
|
|
77
|
+
structure.clearHighlightFromExternal?.()
|
|
78
|
+
} else {
|
|
79
|
+
structure.highlightFromExternal?.(structurePos)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
20
84
|
export interface IRegion {
|
|
21
85
|
refName: string
|
|
22
86
|
start: number
|
|
@@ -28,7 +92,7 @@ export interface BlastParams {
|
|
|
28
92
|
blastDatabase: string
|
|
29
93
|
msaAlgorithm: string
|
|
30
94
|
blastProgram: string
|
|
31
|
-
selectedTranscript
|
|
95
|
+
selectedTranscript?: Feature
|
|
32
96
|
proteinSequence: string
|
|
33
97
|
}
|
|
34
98
|
|
|
@@ -70,10 +134,48 @@ export default function stateModelFactory() {
|
|
|
70
134
|
*/
|
|
71
135
|
querySeqName: 'QUERY',
|
|
72
136
|
|
|
137
|
+
/**
|
|
138
|
+
* #property
|
|
139
|
+
* UniProt ID extracted from AlphaFold MSA URL
|
|
140
|
+
*/
|
|
141
|
+
uniprotId: types.maybe(types.string),
|
|
142
|
+
|
|
73
143
|
/**
|
|
74
144
|
* #property
|
|
75
145
|
*/
|
|
76
146
|
zoomToBaseLevel: false,
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* #property
|
|
150
|
+
* used for loading the MSA view via session snapshots, e.g.
|
|
151
|
+
* {
|
|
152
|
+
* "type": "MsaView",
|
|
153
|
+
* "init": {
|
|
154
|
+
* "msaUrl": "https://example.com/alignment.fa",
|
|
155
|
+
* "treeUrl": "https://example.com/tree.nh",
|
|
156
|
+
* "querySeqName": "ENST00000123_hg38"
|
|
157
|
+
* }
|
|
158
|
+
* }
|
|
159
|
+
*/
|
|
160
|
+
init: types.frozen<MsaViewInitState | undefined>(),
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* #property
|
|
164
|
+
* connections to protein 3D structure views for synchronized highlighting
|
|
165
|
+
*/
|
|
166
|
+
connectedStructures: types.array(types.frozen<StructureConnection>()),
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* #property
|
|
170
|
+
* Reference ID for MSA data stored in IndexedDB (for large datasets)
|
|
171
|
+
*/
|
|
172
|
+
dataStoreId: types.maybe(types.string),
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* #property
|
|
176
|
+
* MAF region for coordinate mapping (used when launched from MAF viewer)
|
|
177
|
+
*/
|
|
178
|
+
mafRegion: types.frozen<MafRegion | undefined>(),
|
|
77
179
|
}),
|
|
78
180
|
)
|
|
79
181
|
|
|
@@ -90,28 +192,31 @@ export default function stateModelFactory() {
|
|
|
90
192
|
* #volatile
|
|
91
193
|
*/
|
|
92
194
|
error: undefined as unknown,
|
|
195
|
+
/**
|
|
196
|
+
* #volatile
|
|
197
|
+
* True when loading MSA data from IndexedDB
|
|
198
|
+
*/
|
|
199
|
+
loadingStoredData: false,
|
|
93
200
|
}))
|
|
94
201
|
|
|
95
202
|
.views(self => ({
|
|
96
203
|
/**
|
|
97
204
|
* #method
|
|
205
|
+
* Get a row by name, returns [name, sequence] or undefined
|
|
98
206
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
return i
|
|
111
|
-
}
|
|
112
|
-
return undefined
|
|
207
|
+
getRowByName(rowName: string) {
|
|
208
|
+
return self.rows.find(r => r[0] === rowName)
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* #method
|
|
213
|
+
* Get the sequence for a row by name
|
|
214
|
+
*/
|
|
215
|
+
getSequenceByRowName(rowName: string) {
|
|
216
|
+
return self.rows.find(r => r[0] === rowName)?.[1]
|
|
113
217
|
},
|
|
114
218
|
}))
|
|
219
|
+
|
|
115
220
|
.views(self => ({
|
|
116
221
|
/**
|
|
117
222
|
* #getter
|
|
@@ -121,38 +226,91 @@ export default function stateModelFactory() {
|
|
|
121
226
|
? genomeToTranscriptSeqMapping(self.connectedFeature)
|
|
122
227
|
: undefined
|
|
123
228
|
},
|
|
124
|
-
|
|
125
|
-
.views(self => ({
|
|
229
|
+
|
|
126
230
|
/**
|
|
127
231
|
* #getter
|
|
128
232
|
*/
|
|
129
|
-
get
|
|
130
|
-
return
|
|
233
|
+
get processing() {
|
|
234
|
+
return !!self.progress
|
|
131
235
|
},
|
|
236
|
+
|
|
132
237
|
/**
|
|
133
238
|
* #getter
|
|
134
239
|
*/
|
|
135
|
-
get
|
|
136
|
-
|
|
240
|
+
get connectedView() {
|
|
241
|
+
const { views } = getSession(self)
|
|
242
|
+
return views.find(f => f.id === self.connectedViewId) as MaybeLGV
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* #getter
|
|
247
|
+
* Get connected protein views with their full model references
|
|
248
|
+
*/
|
|
249
|
+
get connectedProteinViews() {
|
|
250
|
+
const { views } = getSession(self)
|
|
251
|
+
return self.connectedStructures
|
|
252
|
+
.map(conn => {
|
|
253
|
+
const proteinView = views.find(
|
|
254
|
+
(v: any) => v.id === conn.proteinViewId,
|
|
255
|
+
) as any
|
|
256
|
+
return proteinView ? { ...conn, proteinView } : undefined
|
|
257
|
+
})
|
|
258
|
+
.filter((c): c is StructureConnection & { proteinView: any } => !!c)
|
|
137
259
|
},
|
|
138
260
|
}))
|
|
139
261
|
|
|
140
262
|
.views(self => ({
|
|
141
263
|
/**
|
|
142
264
|
* #getter
|
|
265
|
+
* Get the MSA column that corresponds to the currently hovered structure position
|
|
266
|
+
* Returns the first match from any connected structure
|
|
143
267
|
*/
|
|
144
|
-
get
|
|
145
|
-
|
|
268
|
+
get structureHoverCol(): number | undefined {
|
|
269
|
+
for (const conn of self.connectedProteinViews) {
|
|
270
|
+
const structure = conn.proteinView?.structures?.[conn.structureIdx]
|
|
271
|
+
const structurePos = structure?.hoverPosition?.structureSeqPos
|
|
272
|
+
if (structurePos !== undefined) {
|
|
273
|
+
const msaUngapped = conn.structureToMsa[structurePos]
|
|
274
|
+
if (msaUngapped !== undefined) {
|
|
275
|
+
const seq = self.getSequenceByRowName(conn.msaRowName)
|
|
276
|
+
if (seq) {
|
|
277
|
+
// Convert ungapped position to global column, then to visible column
|
|
278
|
+
const globalCol = ungappedToGappedPosition(seq, msaUngapped)
|
|
279
|
+
if (globalCol !== undefined) {
|
|
280
|
+
return self.globalColToVisibleCol(globalCol)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return undefined
|
|
146
287
|
},
|
|
288
|
+
}))
|
|
147
289
|
|
|
290
|
+
.views(self => ({
|
|
148
291
|
/**
|
|
149
292
|
* #getter
|
|
293
|
+
* Returns a secondary highlight column from either:
|
|
294
|
+
* 1. Structure hover (from connected protein 3D view)
|
|
295
|
+
* 2. Genome hover (from connected linear genome view)
|
|
150
296
|
*/
|
|
151
|
-
get
|
|
152
|
-
|
|
153
|
-
|
|
297
|
+
get mouseCol2(): number | undefined {
|
|
298
|
+
// Check structure hover first
|
|
299
|
+
const structureCol = self.structureHoverCol
|
|
300
|
+
if (structureCol !== undefined) {
|
|
301
|
+
return structureCol
|
|
302
|
+
}
|
|
303
|
+
// Fall back to genome hover
|
|
304
|
+
return genomeToMSA({ model: self as JBrowsePluginMsaViewModel })
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* #getter
|
|
308
|
+
*/
|
|
309
|
+
get clickCol2() {
|
|
310
|
+
return undefined
|
|
154
311
|
},
|
|
155
312
|
}))
|
|
313
|
+
|
|
156
314
|
.actions(self => ({
|
|
157
315
|
/**
|
|
158
316
|
* #action
|
|
@@ -202,7 +360,158 @@ export default function stateModelFactory() {
|
|
|
202
360
|
setBlastParams(args?: BlastParams) {
|
|
203
361
|
self.blastParams = args
|
|
204
362
|
},
|
|
363
|
+
/**
|
|
364
|
+
* #action
|
|
365
|
+
*/
|
|
366
|
+
setInit(arg?: MsaViewInitState) {
|
|
367
|
+
self.init = arg
|
|
368
|
+
},
|
|
369
|
+
/**
|
|
370
|
+
* #action
|
|
371
|
+
*/
|
|
372
|
+
setQuerySeqName(arg: string) {
|
|
373
|
+
self.querySeqName = arg
|
|
374
|
+
},
|
|
375
|
+
/**
|
|
376
|
+
* #action
|
|
377
|
+
*/
|
|
378
|
+
setUniprotId(arg?: string) {
|
|
379
|
+
self.uniprotId = arg
|
|
380
|
+
},
|
|
381
|
+
/**
|
|
382
|
+
* #action
|
|
383
|
+
*/
|
|
384
|
+
setDataStoreId(arg?: string) {
|
|
385
|
+
self.dataStoreId = arg
|
|
386
|
+
},
|
|
387
|
+
/**
|
|
388
|
+
* #action
|
|
389
|
+
*/
|
|
390
|
+
setMafRegion(arg?: MafRegion) {
|
|
391
|
+
self.mafRegion = arg
|
|
392
|
+
},
|
|
393
|
+
/**
|
|
394
|
+
* #action
|
|
395
|
+
*/
|
|
396
|
+
setLoadingStoredData(arg: boolean) {
|
|
397
|
+
self.loadingStoredData = arg
|
|
398
|
+
},
|
|
399
|
+
/**
|
|
400
|
+
* #action
|
|
401
|
+
*/
|
|
402
|
+
handleMsaClick(coord: number) {
|
|
403
|
+
const { connectedView, zoomToBaseLevel } = self
|
|
404
|
+
const { assemblyManager } = getSession(self)
|
|
405
|
+
const r2 = msaCoordToGenomeCoord({ model: self, coord })
|
|
406
|
+
|
|
407
|
+
if (!r2 || !connectedView) {
|
|
408
|
+
return
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (zoomToBaseLevel) {
|
|
412
|
+
connectedView.navTo(r2)
|
|
413
|
+
} else {
|
|
414
|
+
const r =
|
|
415
|
+
assemblyManager
|
|
416
|
+
.get(connectedView.assemblyNames[0]!)
|
|
417
|
+
?.getCanonicalRefName(r2.refName) ?? r2.refName
|
|
418
|
+
connectedView.centerAt(r2.start, r)
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* #action
|
|
424
|
+
* Connect to a protein structure for synchronized highlighting
|
|
425
|
+
*/
|
|
426
|
+
connectToStructure(
|
|
427
|
+
proteinViewId: string,
|
|
428
|
+
structureIdx: number,
|
|
429
|
+
msaRowName?: string,
|
|
430
|
+
) {
|
|
431
|
+
const rowName = msaRowName ?? self.querySeqName
|
|
432
|
+
const msaSequence = self.getSequenceByRowName(rowName)
|
|
433
|
+
if (!msaSequence) {
|
|
434
|
+
throw new Error(`MSA row "${rowName}" not found`)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const ungappedMsaSequence = msaSequence.replaceAll('-', '')
|
|
438
|
+
|
|
439
|
+
const { views } = getSession(self)
|
|
440
|
+
|
|
441
|
+
const proteinView = views.find(
|
|
442
|
+
(v: any) => v.id === proteinViewId,
|
|
443
|
+
) as any
|
|
444
|
+
if (!proteinView) {
|
|
445
|
+
throw new Error(`ProteinView "${proteinViewId}" not found`)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const structure = proteinView.structures?.[structureIdx]
|
|
449
|
+
if (!structure) {
|
|
450
|
+
throw new Error(`Structure at index ${structureIdx} not found`)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const structureSequence = structure.structureSequences?.[0]
|
|
454
|
+
if (!structureSequence) {
|
|
455
|
+
throw new Error('Structure sequence not available')
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const alignment = runPairwiseAlignment(
|
|
459
|
+
ungappedMsaSequence,
|
|
460
|
+
structureSequence,
|
|
461
|
+
)
|
|
462
|
+
const { seq1ToSeq2, seq2ToSeq1 } = buildAlignmentMaps(alignment)
|
|
463
|
+
|
|
464
|
+
const connection: StructureConnection = {
|
|
465
|
+
proteinViewId,
|
|
466
|
+
structureIdx,
|
|
467
|
+
msaRowName: rowName,
|
|
468
|
+
msaToStructure: mapToRecord(seq1ToSeq2),
|
|
469
|
+
structureToMsa: mapToRecord(seq2ToSeq1),
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
self.connectedStructures.push(connection)
|
|
473
|
+
},
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* #action
|
|
477
|
+
* Disconnect from a protein structure
|
|
478
|
+
*/
|
|
479
|
+
disconnectFromStructure(proteinViewId: string, structureIdx: number) {
|
|
480
|
+
const idx = self.connectedStructures.findIndex(
|
|
481
|
+
c =>
|
|
482
|
+
c.proteinViewId === proteinViewId &&
|
|
483
|
+
c.structureIdx === structureIdx,
|
|
484
|
+
)
|
|
485
|
+
if (idx !== -1) {
|
|
486
|
+
self.connectedStructures.splice(idx, 1)
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* #action
|
|
492
|
+
* Disconnect from all protein structures
|
|
493
|
+
*/
|
|
494
|
+
disconnectAllStructures() {
|
|
495
|
+
self.connectedStructures.clear()
|
|
496
|
+
},
|
|
205
497
|
}))
|
|
498
|
+
.actions(self => {
|
|
499
|
+
// store reference to the original action from react-msaview
|
|
500
|
+
const superSetMouseClickPos = self.setMouseClickPos.bind(self)
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
/**
|
|
504
|
+
* #action
|
|
505
|
+
* overrides base setMouseClickPos to trigger navigation
|
|
506
|
+
*/
|
|
507
|
+
setMouseClickPos(col?: number, row?: number) {
|
|
508
|
+
superSetMouseClickPos(col, row)
|
|
509
|
+
if (col !== undefined) {
|
|
510
|
+
self.handleMsaClick(col)
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
}
|
|
514
|
+
})
|
|
206
515
|
|
|
207
516
|
.views(self => ({
|
|
208
517
|
/**
|
|
@@ -219,26 +528,106 @@ export default function stateModelFactory() {
|
|
|
219
528
|
self.setZoomToBaseLevel(!self.zoomToBaseLevel)
|
|
220
529
|
},
|
|
221
530
|
},
|
|
531
|
+
{
|
|
532
|
+
label: 'Connect to protein structure...',
|
|
533
|
+
onClick: () => {
|
|
534
|
+
getSession(self).queueDialog(handleClose => [
|
|
535
|
+
ConnectStructureDialog,
|
|
536
|
+
{
|
|
537
|
+
model: self,
|
|
538
|
+
handleClose,
|
|
539
|
+
},
|
|
540
|
+
])
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
...(self.connectedStructures.length > 0
|
|
544
|
+
? [
|
|
545
|
+
{
|
|
546
|
+
label: 'Disconnect from protein structures',
|
|
547
|
+
onClick: () => {
|
|
548
|
+
self.disconnectAllStructures()
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
]
|
|
552
|
+
: []),
|
|
222
553
|
]
|
|
223
554
|
},
|
|
224
|
-
/**
|
|
225
|
-
* #getter
|
|
226
|
-
*/
|
|
227
|
-
get processing() {
|
|
228
|
-
return !!self.progress
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* #getter
|
|
233
|
-
*/
|
|
234
|
-
get connectedView() {
|
|
235
|
-
const { views } = getSession(self)
|
|
236
|
-
return views.find(f => f.id === self.connectedViewId) as MaybeLGV
|
|
237
|
-
},
|
|
238
555
|
}))
|
|
239
556
|
|
|
240
557
|
.actions(self => ({
|
|
241
558
|
afterCreate() {
|
|
559
|
+
// Clean up old IndexedDB entries on startup
|
|
560
|
+
cleanupOldData().catch((e: unknown) => {
|
|
561
|
+
console.error('Failed to cleanup old MSA data:', e)
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
// Load MSA data from IndexedDB if dataStoreId exists and no data loaded
|
|
565
|
+
addDisposer(
|
|
566
|
+
self,
|
|
567
|
+
autorun(async () => {
|
|
568
|
+
const { dataStoreId, rows } = self
|
|
569
|
+
if (dataStoreId && rows.length === 0) {
|
|
570
|
+
try {
|
|
571
|
+
self.setLoadingStoredData(true)
|
|
572
|
+
const storedData = await retrieveMsaData(dataStoreId)
|
|
573
|
+
if (storedData) {
|
|
574
|
+
if (storedData.msa) {
|
|
575
|
+
self.setMSA(storedData.msa)
|
|
576
|
+
}
|
|
577
|
+
if (storedData.tree) {
|
|
578
|
+
self.setTree(storedData.tree)
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
} catch (e) {
|
|
582
|
+
console.error('Failed to load MSA data from IndexedDB:', e)
|
|
583
|
+
} finally {
|
|
584
|
+
self.setLoadingStoredData(false)
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}),
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
// Store MSA data to IndexedDB when loaded from inline data (no filehandle)
|
|
591
|
+
// This ensures data persists across page refreshes even when
|
|
592
|
+
// react-msaview's postProcessSnapshot would strip it
|
|
593
|
+
addDisposer(
|
|
594
|
+
self,
|
|
595
|
+
autorun(async () => {
|
|
596
|
+
const { rows, dataStoreId } = self
|
|
597
|
+
// Only store if we have data and don't already have a dataStoreId
|
|
598
|
+
if (rows.length > 0 && !dataStoreId) {
|
|
599
|
+
// Only store if there's no filehandle (filehandles can reload from source)
|
|
600
|
+
const hasFilehandle = !!(
|
|
601
|
+
self.msaFilehandle ?? self.treeFilehandle
|
|
602
|
+
)
|
|
603
|
+
if (hasFilehandle) {
|
|
604
|
+
return
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const msaData = self.data.msa
|
|
608
|
+
const treeData = self.data.tree
|
|
609
|
+
|
|
610
|
+
// Only store if we have actual data
|
|
611
|
+
if (msaData || treeData) {
|
|
612
|
+
try {
|
|
613
|
+
const newId = generateDataStoreId()
|
|
614
|
+
const success = await storeMsaData(newId, {
|
|
615
|
+
msa: msaData,
|
|
616
|
+
tree: treeData,
|
|
617
|
+
treeMetadata: self.data.treeMetadata,
|
|
618
|
+
})
|
|
619
|
+
// Only set dataStoreId if storage was successful
|
|
620
|
+
if (success) {
|
|
621
|
+
self.setDataStoreId(newId)
|
|
622
|
+
}
|
|
623
|
+
} catch (e) {
|
|
624
|
+
console.error('Failed to store MSA data to IndexedDB:', e)
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}),
|
|
629
|
+
)
|
|
630
|
+
|
|
242
631
|
addDisposer(
|
|
243
632
|
self,
|
|
244
633
|
autorun(async () => {
|
|
@@ -261,6 +650,63 @@ export default function stateModelFactory() {
|
|
|
261
650
|
}),
|
|
262
651
|
)
|
|
263
652
|
|
|
653
|
+
// process init parameter for loading MSA from session snapshots
|
|
654
|
+
addDisposer(
|
|
655
|
+
self,
|
|
656
|
+
autorun(async () => {
|
|
657
|
+
const { init } = self
|
|
658
|
+
if (init) {
|
|
659
|
+
try {
|
|
660
|
+
self.setError(undefined)
|
|
661
|
+
const { msaData, msaUrl, treeData, treeUrl, querySeqName } =
|
|
662
|
+
init
|
|
663
|
+
|
|
664
|
+
// Extract uniprotId from AlphaFold MSA URL and set querySeqName
|
|
665
|
+
if (msaUrl) {
|
|
666
|
+
const id = getUniprotIdFromAlphaFoldUrl(msaUrl)
|
|
667
|
+
if (id) {
|
|
668
|
+
self.setUniprotId(id)
|
|
669
|
+
// AlphaFold MSA files use 'query' as the row name
|
|
670
|
+
self.setQuerySeqName('query')
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// User-provided querySeqName takes precedence
|
|
675
|
+
if (querySeqName) {
|
|
676
|
+
self.setQuerySeqName(querySeqName)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (msaData) {
|
|
680
|
+
self.setMSA(msaData)
|
|
681
|
+
} else if (msaUrl) {
|
|
682
|
+
const response = await fetch(msaUrl)
|
|
683
|
+
if (!response.ok) {
|
|
684
|
+
throw new Error(`Failed to fetch MSA: ${response.status}`)
|
|
685
|
+
}
|
|
686
|
+
const data = await response.text()
|
|
687
|
+
self.setMSA(data)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (treeData) {
|
|
691
|
+
self.setTree(treeData)
|
|
692
|
+
} else if (treeUrl) {
|
|
693
|
+
const response = await fetch(treeUrl)
|
|
694
|
+
if (!response.ok) {
|
|
695
|
+
throw new Error(`Failed to fetch tree: ${response.status}`)
|
|
696
|
+
}
|
|
697
|
+
const data = await response.text()
|
|
698
|
+
self.setTree(data)
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
self.setInit(undefined)
|
|
702
|
+
} catch (e) {
|
|
703
|
+
self.setError(e)
|
|
704
|
+
console.error(e)
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}),
|
|
708
|
+
)
|
|
709
|
+
|
|
264
710
|
// this adds highlights to the genome view when mouse-ing over the MSA
|
|
265
711
|
addDisposer(
|
|
266
712
|
self,
|
|
@@ -274,36 +720,147 @@ export default function stateModelFactory() {
|
|
|
274
720
|
mouseClickCol === undefined
|
|
275
721
|
? undefined
|
|
276
722
|
: msaCoordToGenomeCoord({ model: self, coord: mouseClickCol })
|
|
723
|
+
|
|
277
724
|
self.setConnectedHighlights([r1, r2].filter(f => !!f))
|
|
278
725
|
}),
|
|
279
726
|
)
|
|
280
727
|
|
|
281
|
-
//
|
|
728
|
+
// this highlights residues in connected protein structures when mousing over the MSA
|
|
282
729
|
addDisposer(
|
|
283
730
|
self,
|
|
284
731
|
autorun(() => {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
732
|
+
highlightConnectedStructures(self)
|
|
733
|
+
}),
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
// auto-connect to compatible ProteinViews
|
|
737
|
+
addDisposer(
|
|
738
|
+
self,
|
|
739
|
+
autorun(() => {
|
|
740
|
+
const { views } = getSession(self)
|
|
741
|
+
const { connectedViewId, uniprotId, rows, connectedStructures } =
|
|
742
|
+
self
|
|
291
743
|
|
|
292
|
-
|
|
744
|
+
// Need MSA loaded and a uniprotId to auto-connect
|
|
745
|
+
if (!uniprotId || rows.length === 0) {
|
|
293
746
|
return
|
|
294
747
|
}
|
|
295
748
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
749
|
+
// Find ProteinViews that share the same connectedViewId
|
|
750
|
+
for (const view of views) {
|
|
751
|
+
const v = view as any
|
|
752
|
+
if (v.type !== 'ProteinView' || !v.structures) {
|
|
753
|
+
continue
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
for (
|
|
757
|
+
let structureIdx = 0;
|
|
758
|
+
structureIdx < v.structures.length;
|
|
759
|
+
structureIdx++
|
|
760
|
+
) {
|
|
761
|
+
const structure = v.structures[structureIdx]
|
|
762
|
+
|
|
763
|
+
// Check if structure shares the same genome view connection
|
|
764
|
+
if (structure.connectedViewId !== connectedViewId) {
|
|
765
|
+
continue
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Check if structure has matching uniprotId
|
|
769
|
+
if (structure.uniprotId !== uniprotId) {
|
|
770
|
+
continue
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Check if already connected
|
|
774
|
+
const alreadyConnected = connectedStructures.some(
|
|
775
|
+
c =>
|
|
776
|
+
c.proteinViewId === v.id && c.structureIdx === structureIdx,
|
|
777
|
+
)
|
|
778
|
+
if (alreadyConnected) {
|
|
779
|
+
continue
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Check if structure sequence is available
|
|
783
|
+
if (!structure.structureSequences?.[0]) {
|
|
784
|
+
continue
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Auto-connect
|
|
788
|
+
try {
|
|
789
|
+
self.connectToStructure(v.id, structureIdx)
|
|
790
|
+
} catch (e) {
|
|
791
|
+
console.error('Failed to auto-connect to ProteinView:', e)
|
|
792
|
+
}
|
|
793
|
+
}
|
|
304
794
|
}
|
|
305
795
|
}),
|
|
306
796
|
)
|
|
797
|
+
|
|
798
|
+
// Observe protein3d genome highlights and update MSA highlighted columns
|
|
799
|
+
// This enables communication via the linear genome view coordinates
|
|
800
|
+
addDisposer(
|
|
801
|
+
self,
|
|
802
|
+
autorun(() => {
|
|
803
|
+
const { views } = getSession(self)
|
|
804
|
+
const { connectedViewId, transcriptToMsaMap, querySeqName } = self
|
|
805
|
+
|
|
806
|
+
if (!connectedViewId || !transcriptToMsaMap) {
|
|
807
|
+
return
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
const columns: number[] = []
|
|
811
|
+
|
|
812
|
+
// Find ProteinViews that share the same connected genome view
|
|
813
|
+
for (const view of views) {
|
|
814
|
+
const v = view as any
|
|
815
|
+
if (v.type !== 'ProteinView' || !v.structures) {
|
|
816
|
+
continue
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
for (const structure of v.structures) {
|
|
820
|
+
// Check if structure is connected to same genome view
|
|
821
|
+
if (structure.connectedViewId !== connectedViewId) {
|
|
822
|
+
continue
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Check if structure has hover genome highlights
|
|
826
|
+
const highlights = structure.hoverGenomeHighlights
|
|
827
|
+
if (!highlights || highlights.length === 0) {
|
|
828
|
+
continue
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Map genome coordinates to MSA columns
|
|
832
|
+
const { g2p } = transcriptToMsaMap
|
|
833
|
+
for (const highlight of highlights) {
|
|
834
|
+
for (
|
|
835
|
+
let coord = highlight.start;
|
|
836
|
+
coord < highlight.end;
|
|
837
|
+
coord++
|
|
838
|
+
) {
|
|
839
|
+
const proteinPos = g2p[coord]
|
|
840
|
+
if (proteinPos !== undefined) {
|
|
841
|
+
const col = self.seqPosToGlobalCol(
|
|
842
|
+
querySeqName,
|
|
843
|
+
proteinPos,
|
|
844
|
+
)
|
|
845
|
+
if (!columns.includes(col)) {
|
|
846
|
+
columns.push(col)
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Convert global column indices to visible column indices
|
|
855
|
+
const visibleColumns = columns
|
|
856
|
+
.map(col => self.globalColToVisibleCol(col))
|
|
857
|
+
.filter((col): col is number => col !== undefined)
|
|
858
|
+
|
|
859
|
+
self.setHighlightedColumns(
|
|
860
|
+
visibleColumns.length > 0 ? visibleColumns : undefined,
|
|
861
|
+
)
|
|
862
|
+
}),
|
|
863
|
+
)
|
|
307
864
|
},
|
|
308
865
|
}))
|
|
309
866
|
}
|
|
@@ -312,3 +869,5 @@ export type JBrowsePluginMsaViewStateModel = ReturnType<
|
|
|
312
869
|
typeof stateModelFactory
|
|
313
870
|
>
|
|
314
871
|
export type JBrowsePluginMsaViewModel = Instance<JBrowsePluginMsaViewStateModel>
|
|
872
|
+
|
|
873
|
+
export { type MafRegion, type MsaViewInitState } from './types'
|