jbrowse-plugin-msaview 2.2.8 → 2.3.2
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/dist/AddHighlightModel/index.d.ts +1 -1
- package/dist/AddHighlightModel/index.js.map +1 -1
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +2 -1
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js +1 -1
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js.map +1 -1
- package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +3 -3
- package/dist/BgzipFastaMsaAdapter/configSchema.js.map +1 -1
- package/dist/BgzipFastaMsaAdapter/index.d.ts +1 -1
- package/dist/BgzipFastaMsaAdapter/index.js.map +1 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.d.ts +1 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js +1 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js.map +1 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeUtils.js.map +1 -1
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +1 -1
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.d.ts +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js.map +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.d.ts +1 -2
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js +21 -14
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.d.ts +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js +32 -35
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastRIDPanel.d.ts +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastRIDPanel.js +2 -2
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastRIDPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.d.ts +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/consts.d.ts +2 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/consts.js +1 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/consts.js.map +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +6 -8
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.d.ts +2 -2
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.js.map +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js.map +1 -1
- package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +3 -3
- package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -4
- package/dist/LaunchMsaView/components/TranscriptSelector.js.map +1 -1
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +1 -1
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +1 -2
- package/dist/LaunchMsaView/components/useTranscriptSelection.js.map +1 -1
- package/dist/LaunchMsaView/components/util.js +1 -1
- package/dist/LaunchMsaView/components/util.js.map +1 -1
- package/dist/LaunchMsaView/index.d.ts +1 -1
- package/dist/LaunchMsaView/index.js.map +1 -1
- package/dist/LaunchMsaView/util.d.ts +1 -0
- package/dist/LaunchMsaView/util.js +10 -0
- package/dist/LaunchMsaView/util.js.map +1 -1
- package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +10 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.js +258 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.js.map +1 -0
- package/dist/MsaViewPanel/blosum62.d.ts +2 -0
- package/dist/MsaViewPanel/blosum62.js +628 -0
- package/dist/MsaViewPanel/blosum62.js.map +1 -0
- package/dist/MsaViewPanel/components/LoadingBLAST.d.ts +1 -1
- package/dist/MsaViewPanel/components/LoadingBLAST.js.map +1 -1
- package/dist/MsaViewPanel/components/MsaViewPanel.d.ts +1 -1
- package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -1
- package/dist/MsaViewPanel/doLaunchBlast.d.ts +1 -1
- package/dist/MsaViewPanel/doLaunchBlast.js.map +1 -1
- package/dist/MsaViewPanel/genomeToMSA.d.ts +1 -1
- package/dist/MsaViewPanel/genomeToMSA.js.map +1 -1
- package/dist/MsaViewPanel/index.d.ts +1 -1
- package/dist/MsaViewPanel/index.js.map +1 -1
- package/dist/MsaViewPanel/model.d.ts +31 -39
- package/dist/MsaViewPanel/model.js +17 -309
- package/dist/MsaViewPanel/model.js.map +1 -1
- package/dist/MsaViewPanel/msaDataStore.d.ts +3 -4
- package/dist/MsaViewPanel/msaDataStore.js +59 -173
- package/dist/MsaViewPanel/msaDataStore.js.map +1 -1
- package/dist/MsaViewPanel/pairwiseAlignment.d.ts +0 -7
- package/dist/MsaViewPanel/pairwiseAlignment.js +1 -636
- package/dist/MsaViewPanel/pairwiseAlignment.js.map +1 -1
- package/dist/MsaViewPanel/structureConnection.js +1 -5
- package/dist/MsaViewPanel/structureConnection.js.map +1 -1
- package/dist/MsaViewPanel/util.d.ts +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +21 -22
- package/dist/index.js.map +1 -1
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +29 -29
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/msa.js +35 -77
- package/dist/utils/msa.js.map +1 -1
- package/dist/utils/ncbiBlast.js +1 -10
- package/dist/utils/ncbiBlast.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +33 -30
- package/src/AddHighlightModel/index.tsx +1 -1
- package/src/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.ts +3 -4
- package/src/BgzipFastaMsaAdapter/configSchema.ts +2 -1
- package/src/BgzipFastaMsaAdapter/index.ts +2 -1
- package/src/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.tsx +2 -6
- package/src/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeUtils.ts +2 -1
- package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +2 -1
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +6 -7
- package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +5 -2
- package/src/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.tsx +22 -17
- package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx +40 -52
- package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastRIDPanel.tsx +6 -13
- package/src/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.ts +2 -1
- package/src/LaunchMsaView/components/NCBIBlastQuery/consts.ts +2 -0
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +7 -19
- package/src/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.ts +2 -2
- package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +1 -2
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +7 -6
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +1 -6
- package/src/LaunchMsaView/components/util.ts +1 -1
- package/src/LaunchMsaView/index.ts +6 -5
- package/src/LaunchMsaView/util.ts +11 -0
- package/src/MsaViewPanel/afterCreateAutoruns.ts +299 -0
- package/src/MsaViewPanel/blosum62.ts +628 -0
- package/src/MsaViewPanel/components/LoadingBLAST.tsx +2 -1
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +2 -1
- package/src/MsaViewPanel/doLaunchBlast.ts +1 -1
- package/src/MsaViewPanel/genomeToMSA.ts +2 -1
- package/src/MsaViewPanel/index.ts +2 -1
- package/src/MsaViewPanel/model.ts +30 -392
- package/src/MsaViewPanel/msaDataStore.ts +56 -196
- package/src/MsaViewPanel/pairwiseAlignment.ts +1 -637
- package/src/MsaViewPanel/structureConnection.ts +1 -5
- package/src/MsaViewPanel/util.ts +1 -1
- package/src/index.ts +5 -4
- package/src/utils/msa.ts +45 -100
- package/src/utils/ncbiBlast.ts +3 -13
- package/src/version.ts +1 -1
- package/CHANGELOG.md +0 -101
|
@@ -7,22 +7,21 @@ import { genomeToTranscriptSeqMapping } from 'g2p_mapper'
|
|
|
7
7
|
import { autorun } from 'mobx'
|
|
8
8
|
import { MSAModelF } from 'react-msaview'
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
autoConnectStructures,
|
|
12
|
+
highlightConnectedStructures,
|
|
13
|
+
launchBlastIfNeeded,
|
|
14
|
+
loadStoredData,
|
|
15
|
+
observeProteinHighlights,
|
|
16
|
+
processInit,
|
|
17
|
+
runCleanup,
|
|
18
|
+
storeDataToIndexedDB,
|
|
19
|
+
updateGenomeHighlights,
|
|
20
|
+
} from './afterCreateAutoruns'
|
|
11
21
|
import { genomeToMSA } from './genomeToMSA'
|
|
12
22
|
import { msaCoordToGenomeCoord } from './msaCoordToGenomeCoord'
|
|
13
|
-
import {
|
|
14
|
-
cleanupOldData,
|
|
15
|
-
generateDataStoreId,
|
|
16
|
-
retrieveMsaData,
|
|
17
|
-
storeMsaData,
|
|
18
|
-
} from './msaDataStore'
|
|
19
23
|
import { buildAlignmentMaps, runPairwiseAlignment } from './pairwiseAlignment'
|
|
20
|
-
import {
|
|
21
|
-
gappedToUngappedPosition,
|
|
22
|
-
mapToRecord,
|
|
23
|
-
ungappedToGappedPosition,
|
|
24
|
-
} from './structureConnection'
|
|
25
|
-
import { getUniprotIdFromAlphaFoldUrl } from './util'
|
|
24
|
+
import { mapToRecord, ungappedToGappedPosition } from './structureConnection'
|
|
26
25
|
|
|
27
26
|
import type { StructureConnection } from './structureConnection'
|
|
28
27
|
import type { MafRegion, MsaViewInitState } from './types'
|
|
@@ -38,49 +37,6 @@ type LGV = LinearGenomeViewModel
|
|
|
38
37
|
|
|
39
38
|
type MaybeLGV = LGV | undefined
|
|
40
39
|
|
|
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
|
-
|
|
84
40
|
export interface IRegion {
|
|
85
41
|
refName: string
|
|
86
42
|
start: number
|
|
@@ -137,7 +93,6 @@ export default function stateModelFactory() {
|
|
|
137
93
|
|
|
138
94
|
/**
|
|
139
95
|
* #property
|
|
140
|
-
* UniProt ID extracted from AlphaFold MSA URL
|
|
141
96
|
*/
|
|
142
97
|
uniprotId: types.maybe(types.string),
|
|
143
98
|
|
|
@@ -148,33 +103,21 @@ export default function stateModelFactory() {
|
|
|
148
103
|
|
|
149
104
|
/**
|
|
150
105
|
* #property
|
|
151
|
-
* used for loading the MSA view via session snapshots, e.g.
|
|
152
|
-
* {
|
|
153
|
-
* "type": "MsaView",
|
|
154
|
-
* "init": {
|
|
155
|
-
* "msaUrl": "https://example.com/alignment.fa",
|
|
156
|
-
* "treeUrl": "https://example.com/tree.nh",
|
|
157
|
-
* "querySeqName": "ENST00000123_hg38"
|
|
158
|
-
* }
|
|
159
|
-
* }
|
|
160
106
|
*/
|
|
161
107
|
init: types.frozen<MsaViewInitState | undefined>(),
|
|
162
108
|
|
|
163
109
|
/**
|
|
164
110
|
* #property
|
|
165
|
-
* connections to protein 3D structure views for synchronized highlighting
|
|
166
111
|
*/
|
|
167
112
|
connectedStructures: types.array(types.frozen<StructureConnection>()),
|
|
168
113
|
|
|
169
114
|
/**
|
|
170
115
|
* #property
|
|
171
|
-
* Reference ID for MSA data stored in IndexedDB (for large datasets)
|
|
172
116
|
*/
|
|
173
117
|
dataStoreId: types.maybe(types.string),
|
|
174
118
|
|
|
175
119
|
/**
|
|
176
120
|
* #property
|
|
177
|
-
* MAF region for coordinate mapping (used when launched from MAF viewer)
|
|
178
121
|
*/
|
|
179
122
|
mafRegion: types.frozen<MafRegion | undefined>(),
|
|
180
123
|
}),
|
|
@@ -195,7 +138,6 @@ export default function stateModelFactory() {
|
|
|
195
138
|
error: undefined as unknown,
|
|
196
139
|
/**
|
|
197
140
|
* #volatile
|
|
198
|
-
* True when loading MSA data from IndexedDB
|
|
199
141
|
*/
|
|
200
142
|
loadingStoredData: false,
|
|
201
143
|
}))
|
|
@@ -203,7 +145,6 @@ export default function stateModelFactory() {
|
|
|
203
145
|
.views(self => ({
|
|
204
146
|
/**
|
|
205
147
|
* #method
|
|
206
|
-
* Get a row by name, returns [name, sequence] or undefined
|
|
207
148
|
*/
|
|
208
149
|
getRowByName(rowName: string) {
|
|
209
150
|
return self.rows.find(r => r[0] === rowName)
|
|
@@ -211,7 +152,6 @@ export default function stateModelFactory() {
|
|
|
211
152
|
|
|
212
153
|
/**
|
|
213
154
|
* #method
|
|
214
|
-
* Get the sequence for a row by name
|
|
215
155
|
*/
|
|
216
156
|
getSequenceByRowName(rowName: string) {
|
|
217
157
|
return self.rows.find(r => r[0] === rowName)?.[1]
|
|
@@ -245,7 +185,6 @@ export default function stateModelFactory() {
|
|
|
245
185
|
|
|
246
186
|
/**
|
|
247
187
|
* #getter
|
|
248
|
-
* Get connected protein views with their full model references
|
|
249
188
|
*/
|
|
250
189
|
get connectedProteinViews() {
|
|
251
190
|
const { views } = getSession(self)
|
|
@@ -263,8 +202,6 @@ export default function stateModelFactory() {
|
|
|
263
202
|
.views(self => ({
|
|
264
203
|
/**
|
|
265
204
|
* #getter
|
|
266
|
-
* Get the MSA column that corresponds to the currently hovered structure position
|
|
267
|
-
* Returns the first match from any connected structure
|
|
268
205
|
*/
|
|
269
206
|
get structureHoverCol(): number | undefined {
|
|
270
207
|
for (const conn of self.connectedProteinViews) {
|
|
@@ -275,7 +212,6 @@ export default function stateModelFactory() {
|
|
|
275
212
|
if (msaUngapped !== undefined) {
|
|
276
213
|
const seq = self.getSequenceByRowName(conn.msaRowName)
|
|
277
214
|
if (seq) {
|
|
278
|
-
// Convert ungapped position to global column, then to visible column
|
|
279
215
|
const globalCol = ungappedToGappedPosition(seq, msaUngapped)
|
|
280
216
|
if (globalCol !== undefined) {
|
|
281
217
|
return self.globalColToVisibleCol(globalCol)
|
|
@@ -291,17 +227,12 @@ export default function stateModelFactory() {
|
|
|
291
227
|
.views(self => ({
|
|
292
228
|
/**
|
|
293
229
|
* #getter
|
|
294
|
-
* Returns a secondary highlight column from either:
|
|
295
|
-
* 1. Structure hover (from connected protein 3D view)
|
|
296
|
-
* 2. Genome hover (from connected linear genome view)
|
|
297
230
|
*/
|
|
298
231
|
get mouseCol2(): number | undefined {
|
|
299
|
-
// Check structure hover first
|
|
300
232
|
const structureCol = self.structureHoverCol
|
|
301
233
|
if (structureCol !== undefined) {
|
|
302
234
|
return structureCol
|
|
303
235
|
}
|
|
304
|
-
// Fall back to genome hover
|
|
305
236
|
return genomeToMSA({ model: self as JBrowsePluginMsaViewModel })
|
|
306
237
|
},
|
|
307
238
|
/**
|
|
@@ -422,7 +353,6 @@ export default function stateModelFactory() {
|
|
|
422
353
|
|
|
423
354
|
/**
|
|
424
355
|
* #action
|
|
425
|
-
* Connect to a protein structure for synchronized highlighting
|
|
426
356
|
*/
|
|
427
357
|
connectToStructure(
|
|
428
358
|
proteinViewId: string,
|
|
@@ -475,7 +405,6 @@ export default function stateModelFactory() {
|
|
|
475
405
|
|
|
476
406
|
/**
|
|
477
407
|
* #action
|
|
478
|
-
* Disconnect from a protein structure
|
|
479
408
|
*/
|
|
480
409
|
disconnectFromStructure(proteinViewId: string, structureIdx: number) {
|
|
481
410
|
const idx = self.connectedStructures.findIndex(
|
|
@@ -490,20 +419,17 @@ export default function stateModelFactory() {
|
|
|
490
419
|
|
|
491
420
|
/**
|
|
492
421
|
* #action
|
|
493
|
-
* Disconnect from all protein structures
|
|
494
422
|
*/
|
|
495
423
|
disconnectAllStructures() {
|
|
496
424
|
self.connectedStructures.clear()
|
|
497
425
|
},
|
|
498
426
|
}))
|
|
499
427
|
.actions(self => {
|
|
500
|
-
// store reference to the original action from react-msaview
|
|
501
428
|
const superSetMouseClickPos = self.setMouseClickPos.bind(self)
|
|
502
429
|
|
|
503
430
|
return {
|
|
504
431
|
/**
|
|
505
432
|
* #action
|
|
506
|
-
* overrides base setMouseClickPos to trigger navigation
|
|
507
433
|
*/
|
|
508
434
|
setMouseClickPos(col?: number, row?: number) {
|
|
509
435
|
superSetMouseClickPos(col, row)
|
|
@@ -517,7 +443,6 @@ export default function stateModelFactory() {
|
|
|
517
443
|
.views(self => ({
|
|
518
444
|
/**
|
|
519
445
|
* #method
|
|
520
|
-
* overrides base
|
|
521
446
|
*/
|
|
522
447
|
extraViewMenuItems() {
|
|
523
448
|
return [
|
|
@@ -557,311 +482,24 @@ export default function stateModelFactory() {
|
|
|
557
482
|
|
|
558
483
|
.actions(self => ({
|
|
559
484
|
afterCreate() {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (storedData.tree) {
|
|
579
|
-
self.setTree(storedData.tree)
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
} catch (e) {
|
|
583
|
-
console.error('Failed to load MSA data from IndexedDB:', e)
|
|
584
|
-
} finally {
|
|
585
|
-
self.setLoadingStoredData(false)
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}),
|
|
589
|
-
)
|
|
590
|
-
|
|
591
|
-
// Store MSA data to IndexedDB when loaded from inline data (no filehandle)
|
|
592
|
-
// This ensures data persists across page refreshes even when
|
|
593
|
-
// react-msaview's postProcessSnapshot would strip it
|
|
594
|
-
addDisposer(
|
|
595
|
-
self,
|
|
596
|
-
autorun(async () => {
|
|
597
|
-
const { rows, dataStoreId } = self
|
|
598
|
-
// Only store if we have data and don't already have a dataStoreId
|
|
599
|
-
if (rows.length > 0 && !dataStoreId) {
|
|
600
|
-
// Only store if there's no filehandle (filehandles can reload from source)
|
|
601
|
-
const hasFilehandle = !!(
|
|
602
|
-
self.msaFilehandle ?? self.treeFilehandle
|
|
603
|
-
)
|
|
604
|
-
if (hasFilehandle) {
|
|
605
|
-
return
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
const msaData = self.data.msa
|
|
609
|
-
const treeData = self.data.tree
|
|
610
|
-
|
|
611
|
-
// Only store if we have actual data
|
|
612
|
-
if (msaData || treeData) {
|
|
613
|
-
try {
|
|
614
|
-
const newId = generateDataStoreId()
|
|
615
|
-
const success = await storeMsaData(newId, {
|
|
616
|
-
msa: msaData,
|
|
617
|
-
tree: treeData,
|
|
618
|
-
treeMetadata: self.data.treeMetadata,
|
|
619
|
-
})
|
|
620
|
-
// Only set dataStoreId if storage was successful
|
|
621
|
-
if (success) {
|
|
622
|
-
self.setDataStoreId(newId)
|
|
623
|
-
}
|
|
624
|
-
} catch (e) {
|
|
625
|
-
console.error('Failed to store MSA data to IndexedDB:', e)
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}),
|
|
630
|
-
)
|
|
631
|
-
|
|
632
|
-
addDisposer(
|
|
633
|
-
self,
|
|
634
|
-
autorun(async () => {
|
|
635
|
-
if (self.blastParams) {
|
|
636
|
-
try {
|
|
637
|
-
self.setProgress('Submitting query')
|
|
638
|
-
self.setError(undefined)
|
|
639
|
-
const data = await doLaunchBlast({
|
|
640
|
-
self,
|
|
641
|
-
})
|
|
642
|
-
self.setData(data)
|
|
643
|
-
self.setBlastParams(undefined)
|
|
644
|
-
} catch (e) {
|
|
645
|
-
self.setError(e)
|
|
646
|
-
console.error(e)
|
|
647
|
-
} finally {
|
|
648
|
-
self.setProgress('')
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}),
|
|
652
|
-
)
|
|
653
|
-
|
|
654
|
-
// process init parameter for loading MSA from session snapshots
|
|
655
|
-
addDisposer(
|
|
656
|
-
self,
|
|
657
|
-
autorun(async () => {
|
|
658
|
-
const { init } = self
|
|
659
|
-
if (init) {
|
|
660
|
-
try {
|
|
661
|
-
self.setError(undefined)
|
|
662
|
-
const { msaData, msaUrl, treeData, treeUrl, querySeqName } =
|
|
663
|
-
init
|
|
664
|
-
|
|
665
|
-
// Extract uniprotId from AlphaFold MSA URL and set querySeqName
|
|
666
|
-
if (msaUrl) {
|
|
667
|
-
const id = getUniprotIdFromAlphaFoldUrl(msaUrl)
|
|
668
|
-
if (id) {
|
|
669
|
-
self.setUniprotId(id)
|
|
670
|
-
// AlphaFold MSA files use 'query' as the row name
|
|
671
|
-
self.setQuerySeqName('query')
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// User-provided querySeqName takes precedence
|
|
676
|
-
if (querySeqName) {
|
|
677
|
-
self.setQuerySeqName(querySeqName)
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if (msaData) {
|
|
681
|
-
self.setMSA(msaData)
|
|
682
|
-
} else if (msaUrl) {
|
|
683
|
-
const response = await fetch(msaUrl)
|
|
684
|
-
if (!response.ok) {
|
|
685
|
-
throw new Error(`Failed to fetch MSA: ${response.status}`)
|
|
686
|
-
}
|
|
687
|
-
const data = await response.text()
|
|
688
|
-
self.setMSA(data)
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
if (treeData) {
|
|
692
|
-
self.setTree(treeData)
|
|
693
|
-
} else if (treeUrl) {
|
|
694
|
-
const response = await fetch(treeUrl)
|
|
695
|
-
if (!response.ok) {
|
|
696
|
-
throw new Error(`Failed to fetch tree: ${response.status}`)
|
|
697
|
-
}
|
|
698
|
-
const data = await response.text()
|
|
699
|
-
self.setTree(data)
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
self.setInit(undefined)
|
|
703
|
-
} catch (e) {
|
|
704
|
-
self.setError(e)
|
|
705
|
-
console.error(e)
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}),
|
|
709
|
-
)
|
|
710
|
-
|
|
711
|
-
// this adds highlights to the genome view when mouse-ing over the MSA
|
|
712
|
-
addDisposer(
|
|
713
|
-
self,
|
|
714
|
-
autorun(() => {
|
|
715
|
-
const { mouseCol, mouseClickCol } = self
|
|
716
|
-
const r1 =
|
|
717
|
-
mouseCol === undefined
|
|
718
|
-
? undefined
|
|
719
|
-
: msaCoordToGenomeCoord({ model: self, coord: mouseCol })
|
|
720
|
-
const r2 =
|
|
721
|
-
mouseClickCol === undefined
|
|
722
|
-
? undefined
|
|
723
|
-
: msaCoordToGenomeCoord({ model: self, coord: mouseClickCol })
|
|
724
|
-
|
|
725
|
-
self.setConnectedHighlights([r1, r2].filter(f => !!f))
|
|
726
|
-
}),
|
|
727
|
-
)
|
|
728
|
-
|
|
729
|
-
// this highlights residues in connected protein structures when mousing over the MSA
|
|
730
|
-
addDisposer(
|
|
731
|
-
self,
|
|
732
|
-
autorun(() => {
|
|
733
|
-
highlightConnectedStructures(self)
|
|
734
|
-
}),
|
|
735
|
-
)
|
|
736
|
-
|
|
737
|
-
// auto-connect to compatible ProteinViews
|
|
738
|
-
addDisposer(
|
|
739
|
-
self,
|
|
740
|
-
autorun(() => {
|
|
741
|
-
const { views } = getSession(self)
|
|
742
|
-
const { connectedViewId, uniprotId, rows, connectedStructures } =
|
|
743
|
-
self
|
|
744
|
-
|
|
745
|
-
// Need MSA loaded and a uniprotId to auto-connect
|
|
746
|
-
if (!uniprotId || rows.length === 0) {
|
|
747
|
-
return
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// Find ProteinViews that share the same connectedViewId
|
|
751
|
-
for (const view of views) {
|
|
752
|
-
const v = view as any
|
|
753
|
-
if (v.type !== 'ProteinView' || !v.structures) {
|
|
754
|
-
continue
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
for (
|
|
758
|
-
let structureIdx = 0;
|
|
759
|
-
structureIdx < v.structures.length;
|
|
760
|
-
structureIdx++
|
|
761
|
-
) {
|
|
762
|
-
const structure = v.structures[structureIdx]
|
|
763
|
-
|
|
764
|
-
// Check if structure shares the same genome view connection
|
|
765
|
-
if (structure.connectedViewId !== connectedViewId) {
|
|
766
|
-
continue
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// Check if structure has matching uniprotId
|
|
770
|
-
if (structure.uniprotId !== uniprotId) {
|
|
771
|
-
continue
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Check if already connected
|
|
775
|
-
const alreadyConnected = connectedStructures.some(
|
|
776
|
-
c =>
|
|
777
|
-
c.proteinViewId === v.id && c.structureIdx === structureIdx,
|
|
778
|
-
)
|
|
779
|
-
if (alreadyConnected) {
|
|
780
|
-
continue
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
// Check if structure sequence is available
|
|
784
|
-
if (!structure.structureSequences?.[0]) {
|
|
785
|
-
continue
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Auto-connect
|
|
789
|
-
try {
|
|
790
|
-
self.connectToStructure(v.id, structureIdx)
|
|
791
|
-
} catch (e) {
|
|
792
|
-
console.error('Failed to auto-connect to ProteinView:', e)
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}),
|
|
797
|
-
)
|
|
798
|
-
|
|
799
|
-
// Observe protein3d genome highlights and update MSA highlighted columns
|
|
800
|
-
// This enables communication via the linear genome view coordinates
|
|
801
|
-
addDisposer(
|
|
802
|
-
self,
|
|
803
|
-
autorun(() => {
|
|
804
|
-
const { views } = getSession(self)
|
|
805
|
-
const { connectedViewId, transcriptToMsaMap, querySeqName } = self
|
|
806
|
-
|
|
807
|
-
if (!connectedViewId || !transcriptToMsaMap) {
|
|
808
|
-
return
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
const columns: number[] = []
|
|
812
|
-
|
|
813
|
-
// Find ProteinViews that share the same connected genome view
|
|
814
|
-
for (const view of views) {
|
|
815
|
-
const v = view as any
|
|
816
|
-
if (v.type !== 'ProteinView' || !v.structures) {
|
|
817
|
-
continue
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
for (const structure of v.structures) {
|
|
821
|
-
// Check if structure is connected to same genome view
|
|
822
|
-
if (structure.connectedViewId !== connectedViewId) {
|
|
823
|
-
continue
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
// Check if structure has hover genome highlights
|
|
827
|
-
const highlights = structure.hoverGenomeHighlights
|
|
828
|
-
if (!highlights || highlights.length === 0) {
|
|
829
|
-
continue
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// Map genome coordinates to MSA columns
|
|
833
|
-
const { g2p } = transcriptToMsaMap
|
|
834
|
-
for (const highlight of highlights) {
|
|
835
|
-
for (
|
|
836
|
-
let coord = highlight.start;
|
|
837
|
-
coord < highlight.end;
|
|
838
|
-
coord++
|
|
839
|
-
) {
|
|
840
|
-
const proteinPos = g2p[coord]
|
|
841
|
-
if (proteinPos !== undefined) {
|
|
842
|
-
const col = self.seqPosToGlobalCol(
|
|
843
|
-
querySeqName,
|
|
844
|
-
proteinPos,
|
|
845
|
-
)
|
|
846
|
-
if (!columns.includes(col)) {
|
|
847
|
-
columns.push(col)
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// Convert global column indices to visible column indices
|
|
856
|
-
const visibleColumns = columns
|
|
857
|
-
.map(col => self.globalColToVisibleCol(col))
|
|
858
|
-
.filter((col): col is number => col !== undefined)
|
|
859
|
-
|
|
860
|
-
self.setHighlightedColumns(
|
|
861
|
-
visibleColumns.length > 0 ? visibleColumns : undefined,
|
|
862
|
-
)
|
|
863
|
-
}),
|
|
864
|
-
)
|
|
485
|
+
runCleanup()
|
|
486
|
+
for (const fn of [
|
|
487
|
+
loadStoredData,
|
|
488
|
+
storeDataToIndexedDB,
|
|
489
|
+
launchBlastIfNeeded,
|
|
490
|
+
processInit,
|
|
491
|
+
updateGenomeHighlights,
|
|
492
|
+
highlightConnectedStructures,
|
|
493
|
+
autoConnectStructures,
|
|
494
|
+
observeProteinHighlights,
|
|
495
|
+
]) {
|
|
496
|
+
addDisposer(
|
|
497
|
+
self,
|
|
498
|
+
autorun(() => {
|
|
499
|
+
fn(self)
|
|
500
|
+
}),
|
|
501
|
+
)
|
|
502
|
+
}
|
|
865
503
|
},
|
|
866
504
|
}))
|
|
867
505
|
}
|