jbrowse-plugin-protein3d 0.4.4 → 0.4.5
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/ProteinToGenomeHighlightInner.js +1 -1
- package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js +3 -5
- package/dist/LaunchProteinView/components/FoldseekResultsTable.js +5 -21
- package/dist/LaunchProteinView/components/MSATable.js +3 -3
- package/dist/LaunchProteinView/components/UserProvidedStructure.js +6 -2
- package/dist/LaunchProteinView/components/launchProteinAnnotationView.js +3 -1
- package/dist/LaunchProteinView/hooks/useTranscriptSelection.js +3 -12
- package/dist/LaunchProteinView/services/foldseekApi.d.ts +1 -1
- package/dist/LaunchProteinView/utils/launchViewUtils.js +15 -4
- package/dist/LaunchProteinView/utils/util.js +3 -7
- package/dist/ProteinView/components/AddStructureDialog.js +8 -5
- package/dist/ProteinView/components/HeaderStructureInfo.js +1 -1
- package/dist/ProteinView/components/ProteinFeatureTrack.js +8 -12
- package/dist/ProteinView/components/ProteinViewHeader.js +1 -4
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js +11 -11
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/src/AddHighlightModel/ProteinToGenomeHighlightInner.tsx +1 -1
- package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +6 -4
- package/src/LaunchProteinView/components/FoldseekResultsTable.tsx +7 -24
- package/src/LaunchProteinView/components/MSATable.tsx +3 -3
- package/src/LaunchProteinView/components/UserProvidedStructure.tsx +6 -2
- package/src/LaunchProteinView/components/launchProteinAnnotationView.ts +3 -1
- package/src/LaunchProteinView/hooks/useTranscriptSelection.ts +7 -12
- package/src/LaunchProteinView/services/foldseekApi.ts +1 -1
- package/src/LaunchProteinView/services/lookupMethods.ts +9 -4
- package/src/LaunchProteinView/utils/launchViewUtils.ts +17 -4
- package/src/LaunchProteinView/utils/util.ts +5 -7
- package/src/ProteinView/components/AddStructureDialog.tsx +7 -7
- package/src/ProteinView/components/HeaderStructureInfo.tsx +1 -1
- package/src/ProteinView/components/ProteinFeatureTrack.tsx +12 -13
- package/src/ProteinView/components/ProteinViewHeader.tsx +9 -14
- package/src/version.ts +1 -1
|
@@ -12,6 +12,6 @@ const ProteinToGenomeHighlightInner = observer(function ProteinToGenomeHighlight
|
|
|
12
12
|
const assembly = assemblyName
|
|
13
13
|
? assemblyManager.get(assemblyName)
|
|
14
14
|
: undefined;
|
|
15
|
-
return assembly && assemblyName ? (React.createElement(React.Fragment, null, proteinView?.structures.
|
|
15
|
+
return assembly && assemblyName ? (React.createElement(React.Fragment, null, proteinView?.structures.flatMap((structure, idx) => structure[field].map((r, idx2) => (React.createElement(Highlight, { key: `${r.refName}-${r.start}-${r.end}-${idx}-${idx2}`, start: r.start, end: r.end, refName: r.refName, assemblyName: assemblyName, model: model })))))) : null;
|
|
16
16
|
});
|
|
17
17
|
export default ProteinToGenomeHighlightInner;
|
|
@@ -10,15 +10,13 @@ function NotFound({ uniprotId }) {
|
|
|
10
10
|
React.createElement(ExternalLink, { href: `https://alphafold.ebi.ac.uk/search/text/${uniprotId}` }, "(search for results)")));
|
|
11
11
|
}
|
|
12
12
|
export default function AlphaFoldDBSearchStatus({ uniprotId, selectedTranscript, structureSequence, isoformSequences, url, }) {
|
|
13
|
-
const url2 = uniprotId
|
|
14
|
-
? `https://www.uniprot.org/uniprotkb/${uniprotId}/entry`
|
|
15
|
-
: undefined;
|
|
16
13
|
const [showAllProteinSequences, setShowAllProteinSequences] = useState(false);
|
|
17
14
|
return uniprotId ? (React.createElement(React.Fragment, null,
|
|
18
15
|
React.createElement("div", null,
|
|
19
16
|
React.createElement(Typography, null,
|
|
20
|
-
"UniProt link:
|
|
21
|
-
|
|
17
|
+
"UniProt link:",
|
|
18
|
+
' ',
|
|
19
|
+
React.createElement(ExternalLink, { href: `https://www.uniprot.org/uniprotkb/${uniprotId}/entry` }, uniprotId)),
|
|
22
20
|
React.createElement(Typography, null,
|
|
23
21
|
"AlphaFoldDB link: ",
|
|
24
22
|
React.createElement(ExternalLink, { href: url }, url))),
|
|
@@ -22,27 +22,11 @@ const useStyles = makeStyles()({
|
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
24
|
function flattenResults(results) {
|
|
25
|
-
const hits = []
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
for (const alignmentGroup of dbResult.alignments) {
|
|
31
|
-
if (!alignmentGroup) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
for (const alignment of alignmentGroup) {
|
|
35
|
-
if (!alignment) {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
hits.push({
|
|
39
|
-
...alignment,
|
|
40
|
-
db: dbResult.db,
|
|
41
|
-
structureUrl: getStructureUrlFromTarget(alignment.target, dbResult.db),
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
25
|
+
const hits = results.results.flatMap(dbResult => (dbResult.alignments ?? []).flat().map(alignment => ({
|
|
26
|
+
...alignment,
|
|
27
|
+
db: dbResult.db,
|
|
28
|
+
structureUrl: getStructureUrlFromTarget(alignment.target, dbResult.db),
|
|
29
|
+
})));
|
|
46
30
|
hits.sort((a, b) => (a.eval ?? Infinity) - (b.eval ?? Infinity));
|
|
47
31
|
return hits.slice(0, 100);
|
|
48
32
|
}
|
|
@@ -18,7 +18,7 @@ export default function MSATable({ structureName, structureSequence, isoformSequ
|
|
|
18
18
|
{ ...val, seq: stripStopCodon(val.seq) },
|
|
19
19
|
]));
|
|
20
20
|
const exactMatchIsoformAndStructureSeq = Object.entries(removedStars).find(([_, val]) => structureSequence === val.seq);
|
|
21
|
-
const sname = `${structureName
|
|
21
|
+
const sname = `${structureName} (structure residues)`;
|
|
22
22
|
const maxKeyLen = max([
|
|
23
23
|
sname.length,
|
|
24
24
|
...Object.entries(removedStars).map(([_, val]) => getTranscriptDisplayName(val.feature).length),
|
|
@@ -38,8 +38,8 @@ export default function MSATable({ structureName, structureSequence, isoformSequ
|
|
|
38
38
|
? `${getTranscriptDisplayName(exactMatchIsoformAndStructureSeq[1].feature).padEnd(maxKeyLen)}* ${exactMatchIsoformAndStructureSeq[1].seq}`
|
|
39
39
|
: undefined,
|
|
40
40
|
...Object.entries(removedStars)
|
|
41
|
-
.
|
|
42
|
-
.
|
|
41
|
+
.filter(([k]) => k !== exactMatchIsoformAndStructureSeq?.[0])
|
|
42
|
+
.map(([_, val]) => `${getTranscriptDisplayName(val.feature).padEnd(maxKeyLen)} ${val.seq}`),
|
|
43
43
|
]
|
|
44
44
|
.filter(f => !!f)
|
|
45
45
|
.join('\n'), slotProps: {
|
|
@@ -64,17 +64,21 @@ const UserProvidedStructure = observer(function UserProvidedStructure({ feature,
|
|
|
64
64
|
!!structureSequence &&
|
|
65
65
|
stripStopCodon(protein.seq) !== structureSequence;
|
|
66
66
|
const handleLaunch = async () => {
|
|
67
|
-
if (!protein || !selectedTranscript
|
|
67
|
+
if (!protein || !selectedTranscript) {
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
try {
|
|
71
71
|
const structureData = file ? await file.text() : undefined;
|
|
72
|
+
const url = structureURL ? structureURL : undefined;
|
|
73
|
+
if (!url && !structureData) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
72
76
|
launch3DProteinView({
|
|
73
77
|
session,
|
|
74
78
|
view,
|
|
75
79
|
feature,
|
|
76
80
|
selectedTranscript,
|
|
77
|
-
url
|
|
81
|
+
url,
|
|
78
82
|
data: structureData,
|
|
79
83
|
userProvidedTranscriptSequence: protein.seq,
|
|
80
84
|
alignmentAlgorithm,
|
|
@@ -23,7 +23,9 @@ export async function launchProteinAnnotationView({ session, feature, selectedTr
|
|
|
23
23
|
uniprotId,
|
|
24
24
|
getGeneDisplayName(feature),
|
|
25
25
|
getTranscriptDisplayName(selectedTranscript),
|
|
26
|
-
]
|
|
26
|
+
]
|
|
27
|
+
.filter(s => !!s)
|
|
28
|
+
.join(' - '),
|
|
27
29
|
});
|
|
28
30
|
// Register the 1D view for linked highlighting
|
|
29
31
|
if (connectedViewId && selectedTranscript) {
|
|
@@ -2,18 +2,9 @@ import { useMemo, useState } from 'react';
|
|
|
2
2
|
import { selectBestTranscript } from '../utils/util';
|
|
3
3
|
export default function useTranscriptSelection({ options, isoformSequences, structureSequence, }) {
|
|
4
4
|
const [userSelection, setUserSelection] = useState();
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (isoformSequences !== undefined && userSelection === undefined) {
|
|
9
|
-
return selectBestTranscript({
|
|
10
|
-
options,
|
|
11
|
-
isoformSequences,
|
|
12
|
-
structureSequence,
|
|
13
|
-
})?.id();
|
|
14
|
-
}
|
|
15
|
-
return undefined;
|
|
16
|
-
}, [options, structureSequence, isoformSequences, userSelection]);
|
|
5
|
+
const autoSelection = useMemo(() => isoformSequences !== undefined
|
|
6
|
+
? selectBestTranscript({ options, isoformSequences, structureSequence })?.id()
|
|
7
|
+
: undefined, [options, structureSequence, isoformSequences]);
|
|
17
8
|
const effectiveSelection = userSelection ?? autoSelection;
|
|
18
9
|
return { userSelection: effectiveSelection, setUserSelection };
|
|
19
10
|
}
|
|
@@ -55,7 +55,7 @@ export interface FoldseekAlignment {
|
|
|
55
55
|
}
|
|
56
56
|
export interface FoldseekDatabaseResult {
|
|
57
57
|
db: string;
|
|
58
|
-
alignments?:
|
|
58
|
+
alignments?: FoldseekAlignment[][];
|
|
59
59
|
}
|
|
60
60
|
export interface FoldseekResult {
|
|
61
61
|
query: {
|
|
@@ -54,12 +54,13 @@ function formatViewName(prefix, feature, selectedTranscript, uniprotId) {
|
|
|
54
54
|
getGeneDisplayName(feature),
|
|
55
55
|
getTranscriptDisplayName(selectedTranscript),
|
|
56
56
|
]),
|
|
57
|
-
]
|
|
57
|
+
]
|
|
58
|
+
.filter(s => !!s)
|
|
59
|
+
.join(' - ');
|
|
58
60
|
}
|
|
59
61
|
export function launch3DProteinView({ session, view, feature, selectedTranscript, uniprotId, url, data, userProvidedTranscriptSequence, alignmentAlgorithm, displayName, connectedMsaViewId, }) {
|
|
60
|
-
|
|
62
|
+
const snap = {
|
|
61
63
|
type: 'ProteinView',
|
|
62
|
-
isFloating: true,
|
|
63
64
|
alignmentAlgorithm,
|
|
64
65
|
connectedMsaViewId,
|
|
65
66
|
structures: [
|
|
@@ -73,7 +74,17 @@ export function launch3DProteinView({ session, view, feature, selectedTranscript
|
|
|
73
74
|
],
|
|
74
75
|
displayName: displayName ??
|
|
75
76
|
formatViewName('Protein view', feature, selectedTranscript, uniprotId),
|
|
76
|
-
}
|
|
77
|
+
};
|
|
78
|
+
// eslint-disable-next-line no-console
|
|
79
|
+
console.log('[protein3d debug] addView ProteinView snapshot:', JSON.stringify(snap, (_k, v) => (typeof v === 'function' ? '<fn>' : v), 2));
|
|
80
|
+
try {
|
|
81
|
+
return session.addView('ProteinView', snap);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.log('[protein3d debug] addView threw:', e.message);
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
77
88
|
}
|
|
78
89
|
export async function launch1DProteinView({ session, view, feature, selectedTranscript, uniprotId, confidenceUrl, }) {
|
|
79
90
|
if (!uniprotId || !isSessionWithAddTracks(session)) {
|
|
@@ -64,13 +64,9 @@ export function isRecognizedDatabaseId(id) {
|
|
|
64
64
|
* Get the database type for a recognized ID (used for UniProt xref queries)
|
|
65
65
|
*/
|
|
66
66
|
export function getDatabaseTypeForId(id) {
|
|
67
|
-
if (ensemblGenePattern.test(id)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (ensemblTranscriptPattern.test(id)) {
|
|
71
|
-
return 'ensembl';
|
|
72
|
-
}
|
|
73
|
-
if (ensemblProteinPattern.test(id)) {
|
|
67
|
+
if (ensemblGenePattern.test(id) ||
|
|
68
|
+
ensemblTranscriptPattern.test(id) ||
|
|
69
|
+
ensemblProteinPattern.test(id)) {
|
|
74
70
|
return 'ensembl';
|
|
75
71
|
}
|
|
76
72
|
if (refSeqTranscriptPattern.test(id) || refSeqProteinPattern.test(id)) {
|
|
@@ -21,19 +21,22 @@ const AddStructureDialog = observer(function AddStructureDialog({ model, }) {
|
|
|
21
21
|
};
|
|
22
22
|
const handleAdd = async () => {
|
|
23
23
|
try {
|
|
24
|
-
let url
|
|
24
|
+
let url;
|
|
25
25
|
let data;
|
|
26
26
|
if (choice === 'pdb' && pdbId) {
|
|
27
27
|
url = getPdbStructureUrl(pdbId);
|
|
28
28
|
}
|
|
29
|
-
if (choice === 'uniprot' && uniprotId) {
|
|
29
|
+
else if (choice === 'uniprot' && uniprotId) {
|
|
30
30
|
url = getAlphaFoldStructureUrl(uniprotId.toUpperCase());
|
|
31
31
|
}
|
|
32
|
-
if (choice === '
|
|
32
|
+
else if (choice === 'url' && structureURL) {
|
|
33
|
+
url = structureURL;
|
|
34
|
+
}
|
|
35
|
+
else if (choice === 'file' && file) {
|
|
33
36
|
data = await file.text();
|
|
34
37
|
}
|
|
35
|
-
if (url || data) {
|
|
36
|
-
await model.addStructureAndSuperpose({ url
|
|
38
|
+
if (url !== undefined || data !== undefined) {
|
|
39
|
+
await model.addStructureAndSuperpose({ url, data });
|
|
37
40
|
handleClose();
|
|
38
41
|
}
|
|
39
42
|
}
|
|
@@ -4,7 +4,7 @@ const HeaderStructureInfo = observer(function HeaderStructureInfo({ model, }) {
|
|
|
4
4
|
const { structures } = model;
|
|
5
5
|
return structures.map((structure, idx) => {
|
|
6
6
|
const { hoverString } = structure;
|
|
7
|
-
return (React.createElement("span", { key:
|
|
7
|
+
return (React.createElement("span", { key: idx },
|
|
8
8
|
hoverString ? `Hover: ${hoverString}` : '',
|
|
9
9
|
"\u00A0"));
|
|
10
10
|
});
|
|
@@ -5,6 +5,13 @@ import { CHAR_WIDTH, HIDE_BUTTON_COLOR, HOVERED_BORDER, HOVER_MARKER_COLOR, SELE
|
|
|
5
5
|
import { selectResidueRange } from '../highlightResidueRange';
|
|
6
6
|
import { getFeatureColor } from '../hooks/useUniProtFeatures';
|
|
7
7
|
import { clickProteinToGenome } from '../proteinToGenomeMapping';
|
|
8
|
+
function getFeatureAlignmentRange(feature, structurePositionToAlignmentMap) {
|
|
9
|
+
const startAlignmentPos = structurePositionToAlignmentMap?.[feature.start - 1];
|
|
10
|
+
const endAlignmentPos = structurePositionToAlignmentMap?.[feature.end - 1];
|
|
11
|
+
return startAlignmentPos !== undefined && endAlignmentPos !== undefined
|
|
12
|
+
? { start: startAlignmentPos, end: endAlignmentPos }
|
|
13
|
+
: undefined;
|
|
14
|
+
}
|
|
8
15
|
function getVisibleTypes(featureTypes, hiddenFeatureTypes) {
|
|
9
16
|
return featureTypes.filter(type => !hiddenFeatureTypes.has(type));
|
|
10
17
|
}
|
|
@@ -31,20 +38,9 @@ const FeatureBar = observer(function FeatureBar({ feature, model, }) {
|
|
|
31
38
|
const [isHovered, setIsHovered] = useState(false);
|
|
32
39
|
const { molstarPluginContext, selectedFeatureId, structurePositionToAlignmentMap, } = model;
|
|
33
40
|
const isSelected = selectedFeatureId === feature.uniqueId;
|
|
34
|
-
const getAlignmentRange = () => {
|
|
35
|
-
if (!structurePositionToAlignmentMap) {
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
const startAlignmentPos = structurePositionToAlignmentMap[feature.start - 1];
|
|
39
|
-
const endAlignmentPos = structurePositionToAlignmentMap[feature.end - 1];
|
|
40
|
-
if (startAlignmentPos !== undefined && endAlignmentPos !== undefined) {
|
|
41
|
-
return { start: startAlignmentPos, end: endAlignmentPos };
|
|
42
|
-
}
|
|
43
|
-
return undefined;
|
|
44
|
-
};
|
|
45
41
|
const handleMouseEnter = () => {
|
|
46
42
|
setIsHovered(true);
|
|
47
|
-
const range =
|
|
43
|
+
const range = getFeatureAlignmentRange(feature, structurePositionToAlignmentMap);
|
|
48
44
|
if (range) {
|
|
49
45
|
model.setAlignmentHoverRange(range);
|
|
50
46
|
}
|
|
@@ -31,10 +31,7 @@ const ProteinViewHeader = observer(function ProteinViewHeader({ model, }) {
|
|
|
31
31
|
model.setAutoScrollAlignment(!autoScrollAlignment);
|
|
32
32
|
} }))),
|
|
33
33
|
showAlignment
|
|
34
|
-
? structures.map((structure, idx) => {
|
|
35
|
-
const { pairwiseAlignment } = structure;
|
|
36
|
-
return (React.createElement("div", { key: idx }, pairwiseAlignment ? (React.createElement(ProteinAlignment, { key: idx, model: structure })) : (React.createElement(LoadingEllipses, { message: "Loading pairwise alignment" }))));
|
|
37
|
-
})
|
|
34
|
+
? structures.map((structure, idx) => (React.createElement("div", { key: idx }, structure.pairwiseAlignment ? (React.createElement(ProteinAlignment, { model: structure })) : (React.createElement(LoadingEllipses, { message: "Loading pairwise alignment" })))))
|
|
38
35
|
: null,
|
|
39
36
|
React.createElement(AddStructureDialog, { model: model })));
|
|
40
37
|
});
|