jbrowse-plugin-msaview 2.5.0 → 2.5.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/LaunchMsaViewExtensionPoint/index.js +2 -1
- package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +22 -1
- package/dist/MsaViewPanel/afterCreateAutoruns.js +83 -26
- package/dist/MsaViewPanel/loadProteinDomains.d.ts +16 -0
- package/dist/MsaViewPanel/loadProteinDomains.js +33 -0
- package/dist/MsaViewPanel/model.d.ts +189 -158
- package/dist/MsaViewPanel/model.js +17 -5
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +28 -28
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/domainCache.d.ts +8 -0
- package/dist/utils/domainCache.js +28 -0
- package/dist/utils/eutils.d.ts +3 -0
- package/dist/utils/eutils.js +14 -0
- package/dist/utils/msa.js +18 -15
- package/dist/utils/ncbiBlast.js +22 -24
- package/dist/utils/ncbiDomains.d.ts +39 -0
- package/dist/utils/ncbiDomains.js +156 -0
- package/dist/utils/poll.d.ts +11 -0
- package/dist/utils/poll.js +19 -0
- package/dist/utils/taxonomyNames.js +2 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +22 -19
- package/src/LaunchMsaViewExtensionPoint/index.ts +3 -0
- package/src/MsaViewPanel/afterCreateAutoruns.ts +86 -29
- package/src/MsaViewPanel/loadProteinDomains.ts +57 -0
- package/src/MsaViewPanel/model.ts +22 -4
- package/src/utils/domainCache.ts +43 -0
- package/src/utils/eutils.ts +16 -0
- package/src/utils/msa.ts +18 -16
- package/src/utils/ncbiBlast.ts +27 -31
- package/src/utils/ncbiDomains.ts +173 -0
- package/src/utils/poll.ts +28 -0
- package/src/utils/taxonomyNames.ts +3 -1
- package/src/version.ts +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default function LaunchMsaViewExtensionPointF(pluginManager) {
|
|
2
2
|
pluginManager.addToExtensionPoint('LaunchView-MsaView',
|
|
3
3
|
// @ts-expect-error
|
|
4
|
-
({ session, data, msaFileLocation, treeFileLocation, connectedViewId, connectedFeature, displayName, colorSchemeName, colWidth, rowHeight, treeAreaWidth, treeWidth, drawNodeBubbles, labelsAlignRight, showBranchLen, querySeqName, }) => {
|
|
4
|
+
({ session, data, msaFileLocation, treeFileLocation, connectedViewId, connectedFeature, displayName, colorSchemeName, colWidth, rowHeight, treeAreaWidth, treeWidth, drawNodeBubbles, labelsAlignRight, showBranchLen, querySeqName, highlightColumns, }) => {
|
|
5
5
|
if (!data && !msaFileLocation) {
|
|
6
6
|
throw new Error('No MSA data or file location provided when launching MSA view');
|
|
7
7
|
}
|
|
@@ -18,6 +18,7 @@ export default function LaunchMsaViewExtensionPointF(pluginManager) {
|
|
|
18
18
|
drawNodeBubbles,
|
|
19
19
|
labelsAlignRight,
|
|
20
20
|
showBranchLen,
|
|
21
|
+
highlightColumns,
|
|
21
22
|
init: {
|
|
22
23
|
msaData: data?.msa,
|
|
23
24
|
treeData: data?.tree,
|
|
@@ -2,6 +2,13 @@ import type { JBrowsePluginMsaViewModel } from './model';
|
|
|
2
2
|
export declare function loadStoredData(self: JBrowsePluginMsaViewModel): void;
|
|
3
3
|
export declare function storeDataToIndexedDB(self: JBrowsePluginMsaViewModel): void;
|
|
4
4
|
export declare function launchBlastIfNeeded(self: JBrowsePluginMsaViewModel): void;
|
|
5
|
+
/**
|
|
6
|
+
* Once an accession-bearing alignment is present (fresh from BLAST or restored
|
|
7
|
+
* from cache), fetch NCBI CDD domains for those accessions and overlay them.
|
|
8
|
+
* Runs once per view; the domainsRequested guard prevents refiring when NCBI
|
|
9
|
+
* returns no domains (which leaves interProAnnotations undefined).
|
|
10
|
+
*/
|
|
11
|
+
export declare function autoLoadProteinDomains(self: JBrowsePluginMsaViewModel): void;
|
|
5
12
|
export declare function processInit(self: JBrowsePluginMsaViewModel): void;
|
|
6
13
|
/**
|
|
7
14
|
* Mirror the connected genome view's hover position onto the MSA's hovered
|
|
@@ -13,5 +20,19 @@ export declare function processInit(self: JBrowsePluginMsaViewModel): void;
|
|
|
13
20
|
export declare function syncGenomeHoverToMsaColumn(self: JBrowsePluginMsaViewModel): () => void;
|
|
14
21
|
export declare function highlightConnectedStructures(self: JBrowsePluginMsaViewModel): void;
|
|
15
22
|
export declare function autoConnectStructures(self: JBrowsePluginMsaViewModel): void;
|
|
16
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Mirror a connected 3D protein view's hovered residue onto the MSA's
|
|
25
|
+
* highlighted columns. Returns the autorun body and keeps a flag tracking
|
|
26
|
+
* whether the current highlight was set by THIS sync: when a protein hover ends
|
|
27
|
+
* we restore the declarative highlightColumns seed (or clear) rather than
|
|
28
|
+
* blindly wiping it.
|
|
29
|
+
*
|
|
30
|
+
* Without the flag this autorun fires once on creation — with the view connected
|
|
31
|
+
* to a *genome* LGV but no 3D protein structure attached — computes zero columns,
|
|
32
|
+
* and calls setHighlightedColumns(undefined), clobbering the seed that
|
|
33
|
+
* MSAModelF.afterCreate just set from the declarative `highlightColumns`. That is
|
|
34
|
+
* the bug that made the BRAF/TP53 genome-browser links open with no V600/R248
|
|
35
|
+
* column lit (SRC has no highlightColumns, so nothing was there to wipe).
|
|
36
|
+
*/
|
|
37
|
+
export declare function observeProteinHighlights(self: JBrowsePluginMsaViewModel): () => void;
|
|
17
38
|
export declare function runCleanup(): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getSession } from '@jbrowse/core/util';
|
|
2
2
|
import { doLaunchBlast } from './doLaunchBlast';
|
|
3
3
|
import { genomeToMSA } from './genomeToMSA';
|
|
4
|
+
import { loadProteinDomains } from './loadProteinDomains';
|
|
4
5
|
import { cleanupOldData, generateDataStoreId, retrieveMsaData, storeMsaData, } from './msaDataStore';
|
|
5
6
|
import { gappedToUngappedPosition, getProteinViews, } from './structureConnection';
|
|
6
7
|
import { getUniprotIdFromAlphaFoldUrl } from './util';
|
|
@@ -18,6 +19,9 @@ export function loadStoredData(self) {
|
|
|
18
19
|
if (storedData.tree) {
|
|
19
20
|
self.setTree(storedData.tree);
|
|
20
21
|
}
|
|
22
|
+
if (storedData.treeMetadata) {
|
|
23
|
+
self.setTreeMetadata(storedData.treeMetadata);
|
|
24
|
+
}
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
catch (e) {
|
|
@@ -84,6 +88,33 @@ export function launchBlastIfNeeded(self) {
|
|
|
84
88
|
})();
|
|
85
89
|
}
|
|
86
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Once an accession-bearing alignment is present (fresh from BLAST or restored
|
|
93
|
+
* from cache), fetch NCBI CDD domains for those accessions and overlay them.
|
|
94
|
+
* Runs once per view; the domainsRequested guard prevents refiring when NCBI
|
|
95
|
+
* returns no domains (which leaves interProAnnotations undefined).
|
|
96
|
+
*/
|
|
97
|
+
export function autoLoadProteinDomains(self) {
|
|
98
|
+
const { rows, domainsRequested, interProAnnotations } = self;
|
|
99
|
+
const hasAccessions = self.data.treeMetadata?.includes('"Accession"') ?? false;
|
|
100
|
+
if (rows.length > 0 &&
|
|
101
|
+
hasAccessions &&
|
|
102
|
+
!interProAnnotations &&
|
|
103
|
+
!domainsRequested) {
|
|
104
|
+
self.setDomainsRequested(true);
|
|
105
|
+
void (async () => {
|
|
106
|
+
try {
|
|
107
|
+
await loadProteinDomains(self);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
console.error('[msaview-domains] auto-load failed:', e);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
self.setProgress('');
|
|
114
|
+
}
|
|
115
|
+
})();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
87
118
|
export function processInit(self) {
|
|
88
119
|
const { init } = self;
|
|
89
120
|
if (init) {
|
|
@@ -218,37 +249,63 @@ export function autoConnectStructures(self) {
|
|
|
218
249
|
}
|
|
219
250
|
}
|
|
220
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Mirror a connected 3D protein view's hovered residue onto the MSA's
|
|
254
|
+
* highlighted columns. Returns the autorun body and keeps a flag tracking
|
|
255
|
+
* whether the current highlight was set by THIS sync: when a protein hover ends
|
|
256
|
+
* we restore the declarative highlightColumns seed (or clear) rather than
|
|
257
|
+
* blindly wiping it.
|
|
258
|
+
*
|
|
259
|
+
* Without the flag this autorun fires once on creation — with the view connected
|
|
260
|
+
* to a *genome* LGV but no 3D protein structure attached — computes zero columns,
|
|
261
|
+
* and calls setHighlightedColumns(undefined), clobbering the seed that
|
|
262
|
+
* MSAModelF.afterCreate just set from the declarative `highlightColumns`. That is
|
|
263
|
+
* the bug that made the BRAF/TP53 genome-browser links open with no V600/R248
|
|
264
|
+
* column lit (SRC has no highlightColumns, so nothing was there to wipe).
|
|
265
|
+
*/
|
|
221
266
|
export function observeProteinHighlights(self) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
267
|
+
let proteinDriven = false;
|
|
268
|
+
return () => {
|
|
269
|
+
const { connectedViewId, transcriptToMsaMap, querySeqName } = self;
|
|
270
|
+
if (!connectedViewId || !transcriptToMsaMap) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const columns = new Set();
|
|
274
|
+
for (const view of getProteinViews(getSession(self).views)) {
|
|
275
|
+
for (const structure of view.structures) {
|
|
276
|
+
if (structure.connectedViewId !== connectedViewId) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const highlights = structure.hoverGenomeHighlights;
|
|
280
|
+
if (!highlights || highlights.length === 0) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const { g2p } = transcriptToMsaMap;
|
|
284
|
+
for (const highlight of highlights) {
|
|
285
|
+
for (let coord = highlight.start; coord < highlight.end; coord++) {
|
|
286
|
+
const proteinPos = g2p[coord];
|
|
287
|
+
if (proteinPos !== undefined) {
|
|
288
|
+
const col = self.seqPosToGlobalCol(querySeqName, proteinPos);
|
|
289
|
+
columns.add(col);
|
|
290
|
+
}
|
|
243
291
|
}
|
|
244
292
|
}
|
|
245
293
|
}
|
|
246
294
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
.
|
|
251
|
-
|
|
295
|
+
const visibleColumns = Array.from(columns)
|
|
296
|
+
.map(col => self.globalColToVisibleCol(col))
|
|
297
|
+
.filter((col) => col !== undefined);
|
|
298
|
+
if (visibleColumns.length > 0) {
|
|
299
|
+
self.setHighlightedColumns(visibleColumns);
|
|
300
|
+
proteinDriven = true;
|
|
301
|
+
}
|
|
302
|
+
else if (proteinDriven) {
|
|
303
|
+
// our protein-hover highlight ended — fall back to the declarative seed
|
|
304
|
+
// instead of wiping a column the URL/user asked to keep lit
|
|
305
|
+
self.setHighlightedColumns(self.highlightColumns?.length ? self.highlightColumns : undefined);
|
|
306
|
+
proteinDriven = false;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
252
309
|
}
|
|
253
310
|
export function runCleanup() {
|
|
254
311
|
cleanupOldData().catch((e) => {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { InterProScanResults } from 'react-msaview';
|
|
2
|
+
interface DomainModel {
|
|
3
|
+
data: {
|
|
4
|
+
treeMetadata?: string;
|
|
5
|
+
};
|
|
6
|
+
setProgress: (arg: string) => void;
|
|
7
|
+
setDomains: (data: Record<string, InterProScanResults>) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Overlay protein domains on the alignment using NCBI's pre-computed CDD
|
|
11
|
+
* annotations. The BLAST workflow stores each hit's accession in treeMetadata,
|
|
12
|
+
* so we look those up via efetch and key the results by MSA row name (which is
|
|
13
|
+
* what react-msaview matches domains against).
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadProteinDomains(self: DomainModel): Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fetchProteinDomains } from '../utils/ncbiDomains';
|
|
2
|
+
/**
|
|
3
|
+
* Overlay protein domains on the alignment using NCBI's pre-computed CDD
|
|
4
|
+
* annotations. The BLAST workflow stores each hit's accession in treeMetadata,
|
|
5
|
+
* so we look those up via efetch and key the results by MSA row name (which is
|
|
6
|
+
* what react-msaview matches domains against).
|
|
7
|
+
*/
|
|
8
|
+
export async function loadProteinDomains(self) {
|
|
9
|
+
const metadataJson = self.data.treeMetadata;
|
|
10
|
+
if (!metadataJson) {
|
|
11
|
+
throw new Error('No sequence metadata available to look up domains');
|
|
12
|
+
}
|
|
13
|
+
const metadata = JSON.parse(metadataJson);
|
|
14
|
+
const rowAccessions = Object.entries(metadata)
|
|
15
|
+
.map(([rowName, meta]) => ({ rowName, accession: meta.Accession }))
|
|
16
|
+
.filter((r) => !!r.accession);
|
|
17
|
+
if (rowAccessions.length === 0) {
|
|
18
|
+
throw new Error('No NCBI accessions found in alignment rows');
|
|
19
|
+
}
|
|
20
|
+
self.setProgress(`Fetching protein domains from NCBI for ${rowAccessions.length} sequences...`);
|
|
21
|
+
const byAccession = await fetchProteinDomains(rowAccessions.map(r => r.accession));
|
|
22
|
+
const annotations = {};
|
|
23
|
+
for (const { rowName, accession } of rowAccessions) {
|
|
24
|
+
const matches = byAccession.get(accession);
|
|
25
|
+
if (matches && matches.length > 0) {
|
|
26
|
+
annotations[rowName] = { matches, xref: [{ id: rowName }] };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (Object.keys(annotations).length === 0) {
|
|
30
|
+
throw new Error('No CDD domain annotations found for these proteins');
|
|
31
|
+
}
|
|
32
|
+
self.setDomains(annotations);
|
|
33
|
+
}
|