jbrowse-plugin-protein3d 0.4.9 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.d.ts +5 -3
  2. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +1 -4
  3. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js +8 -13
  4. package/dist/LaunchProteinView/components/FoldseekSearch.d.ts +5 -3
  5. package/dist/LaunchProteinView/components/FoldseekSearch.js +7 -15
  6. package/dist/LaunchProteinView/components/IsoformSequencesToggle.d.ts +10 -0
  7. package/dist/LaunchProteinView/components/IsoformSequencesToggle.js +13 -0
  8. package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js +6 -3
  9. package/dist/LaunchProteinView/components/StructureSourcePicker.d.ts +1 -2
  10. package/dist/LaunchProteinView/components/StructureSourcePicker.js +1 -1
  11. package/dist/LaunchProteinView/components/UserProvidedStructure.d.ts +6 -3
  12. package/dist/LaunchProteinView/components/UserProvidedStructure.js +10 -35
  13. package/dist/LaunchProteinView/hooks/swrOptions.d.ts +5 -0
  14. package/dist/LaunchProteinView/hooks/swrOptions.js +8 -0
  15. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.js +6 -13
  16. package/dist/LaunchProteinView/hooks/useAlphaFoldData.js +2 -4
  17. package/dist/LaunchProteinView/hooks/useAlphaFoldSequenceSearch.js +2 -3
  18. package/dist/LaunchProteinView/hooks/useIsoformProteinSequences.js +8 -4
  19. package/dist/LaunchProteinView/hooks/{useRemoteStructureFileSequence.d.ts → useStructureFileSequence.d.ts} +2 -1
  20. package/dist/LaunchProteinView/hooks/useStructureFileSequence.js +47 -0
  21. package/dist/LaunchProteinView/hooks/useTranscriptIsoformSelection.d.ts +24 -0
  22. package/dist/LaunchProteinView/hooks/useTranscriptIsoformSelection.js +33 -0
  23. package/dist/LaunchProteinView/hooks/useUniProtSearch.js +2 -3
  24. package/dist/LaunchProteinView/utils/calculateProteinSequence.d.ts +4 -5
  25. package/dist/LaunchProteinView/utils/calculateProteinSequence.js +2 -4
  26. package/dist/LaunchProteinView/utils/util.d.ts +0 -1
  27. package/dist/LaunchProteinView/utils/util.js +0 -3
  28. package/dist/LaunchProteinViewExtensionPoint/index.js +44 -7
  29. package/dist/LaunchProteinViewExtensionPoint/resolveShortLaunch.d.ts +26 -0
  30. package/dist/LaunchProteinViewExtensionPoint/resolveShortLaunch.js +91 -0
  31. package/dist/ProteinView/components/FeatureBar.js +2 -2
  32. package/dist/ProteinView/components/FeatureTypeLabel.js +2 -2
  33. package/dist/ProteinView/components/HeaderStructureInfo.d.ts +1 -1
  34. package/dist/ProteinView/components/HeaderStructureInfo.js +12 -6
  35. package/dist/ProteinView/components/ProteinAlignment.js +5 -5
  36. package/dist/ProteinView/components/ProteinFeatureTrack.js +3 -3
  37. package/dist/ProteinView/components/ProteinView.js +7 -2
  38. package/dist/ProteinView/components/ProteinViewHeader.js +69 -18
  39. package/dist/ProteinView/components/ResidueValueTrack.js +4 -4
  40. package/dist/ProteinView/constants.d.ts +4 -2
  41. package/dist/ProteinView/constants.js +4 -2
  42. package/dist/ProteinView/model.d.ts +24 -10
  43. package/dist/ProteinView/model.js +12 -0
  44. package/dist/ProteinView/structureModel.d.ts +26 -1
  45. package/dist/ProteinView/structureModel.js +53 -8
  46. package/dist/ProteinView/useProteinView.js +43 -8
  47. package/dist/jbrowse-plugin-protein3d.umd.production.min.js +15 -15
  48. package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.js +1 -1
  51. package/package.json +2 -1
  52. package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +5 -6
  53. package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +12 -27
  54. package/src/LaunchProteinView/components/FoldseekSearch.tsx +17 -24
  55. package/src/LaunchProteinView/components/IsoformSequencesToggle.tsx +41 -0
  56. package/src/LaunchProteinView/components/LaunchProteinViewDialog.tsx +10 -3
  57. package/src/LaunchProteinView/components/StructureSourcePicker.tsx +0 -2
  58. package/src/LaunchProteinView/components/UserProvidedStructure.tsx +24 -53
  59. package/src/LaunchProteinView/hooks/swrOptions.ts +8 -0
  60. package/src/LaunchProteinView/hooks/useAlphaFoldDBSearch.ts +18 -31
  61. package/src/LaunchProteinView/hooks/useAlphaFoldData.ts +2 -4
  62. package/src/LaunchProteinView/hooks/useAlphaFoldSequenceSearch.ts +2 -3
  63. package/src/LaunchProteinView/hooks/useIsoformProteinSequences.ts +8 -4
  64. package/src/LaunchProteinView/hooks/useStructureFileSequence.ts +64 -0
  65. package/src/LaunchProteinView/hooks/useTranscriptIsoformSelection.ts +47 -0
  66. package/src/LaunchProteinView/hooks/useUniProtSearch.ts +2 -3
  67. package/src/LaunchProteinView/utils/calculateProteinSequence.ts +5 -6
  68. package/src/LaunchProteinView/utils/util.ts +0 -4
  69. package/src/LaunchProteinViewExtensionPoint/index.ts +54 -6
  70. package/src/LaunchProteinViewExtensionPoint/resolveShortLaunch.ts +143 -0
  71. package/src/ProteinView/components/FeatureBar.tsx +2 -7
  72. package/src/ProteinView/components/FeatureTypeLabel.tsx +2 -2
  73. package/src/ProteinView/components/HeaderStructureInfo.tsx +21 -10
  74. package/src/ProteinView/components/ProteinAlignment.tsx +13 -9
  75. package/src/ProteinView/components/ProteinFeatureTrack.tsx +3 -3
  76. package/src/ProteinView/components/ProteinView.tsx +11 -2
  77. package/src/ProteinView/components/ProteinViewHeader.tsx +104 -43
  78. package/src/ProteinView/components/ResidueValueTrack.tsx +4 -4
  79. package/src/ProteinView/constants.ts +4 -2
  80. package/src/ProteinView/model.ts +12 -0
  81. package/src/ProteinView/structureModel.ts +63 -8
  82. package/src/ProteinView/useProteinView.ts +49 -8
  83. package/src/version.ts +1 -1
  84. package/dist/LaunchProteinView/hooks/useLocalStructureFileSequence.d.ts +0 -7
  85. package/dist/LaunchProteinView/hooks/useLocalStructureFileSequence.js +0 -39
  86. package/dist/LaunchProteinView/hooks/useRemoteStructureFileSequence.js +0 -28
  87. package/src/LaunchProteinView/hooks/useLocalStructureFileSequence.ts +0 -60
  88. package/src/LaunchProteinView/hooks/useRemoteStructureFileSequence.ts +0 -41
@@ -1,9 +1,11 @@
1
1
  import React from 'react';
2
2
  import type { AlignmentAlgorithm } from '../../ProteinView/types';
3
- import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
4
- declare const AlphaFoldDBSearch: ({ feature, model, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }: {
3
+ import type { AbstractSessionModel, Feature } from '@jbrowse/core/util';
4
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
5
+ declare const AlphaFoldDBSearch: ({ feature, session, view, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }: {
5
6
  feature: Feature;
6
- model: AbstractTrackModel;
7
+ session: AbstractSessionModel;
8
+ view: LinearGenomeViewModel;
7
9
  handleClose: () => void;
8
10
  alignmentAlgorithm: AlignmentAlgorithm;
9
11
  onAlignmentAlgorithmChange: (algorithm: AlignmentAlgorithm) => void;
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
2
  import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
3
- import { getContainingView, getSession } from '@jbrowse/core/util';
4
3
  import { DialogActions, DialogContent, Typography } from '@mui/material';
5
4
  import { observer } from 'mobx-react';
6
5
  import { makeStyles } from 'tss-react/mui';
@@ -30,10 +29,8 @@ const useStyles = makeStyles()({
30
29
  alignItems: 'flex-start',
31
30
  },
32
31
  });
33
- const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, model, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }) {
32
+ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session, view, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }) {
34
33
  const { classes } = useStyles();
35
- const session = getSession(model);
36
- const view = getContainingView(model);
37
34
  const state = useAlphaFoldDBSearch({ feature, view });
38
35
  return (React.createElement(React.Fragment, null,
39
36
  React.createElement(DialogContent, { className: classes.dialogContent },
@@ -1,8 +1,8 @@
1
- import React, { useState } from 'react';
2
- import { Button, Typography } from '@mui/material';
3
- import MSATable from './MSATable';
1
+ import React from 'react';
2
+ import { Typography } from '@mui/material';
3
+ import IsoformSequencesToggle from './IsoformSequencesToggle';
4
4
  import ExternalLink from '../../components/ExternalLink';
5
- import { getDisplayName } from '../utils/util';
5
+ import { getTranscriptDisplayName } from '../utils/util';
6
6
  function NotFound({ uniprotId }) {
7
7
  return (React.createElement(Typography, null,
8
8
  "No structure found for this UniProtID in AlphaFoldDB",
@@ -10,7 +10,6 @@ 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 [showAllProteinSequences, setShowAllProteinSequences] = useState(false);
14
13
  return uniprotId ? (React.createElement(React.Fragment, null,
15
14
  React.createElement("div", null,
16
15
  React.createElement(Typography, null,
@@ -20,16 +19,12 @@ export default function AlphaFoldDBSearchStatus({ uniprotId, selectedTranscript,
20
19
  React.createElement(Typography, null,
21
20
  "AlphaFoldDB link: ",
22
21
  React.createElement(ExternalLink, { href: url }, url))),
23
- structureSequence ? (React.createElement("div", { style: { margin: 20 } },
24
- React.createElement(Button, { variant: "contained", color: "primary", onClick: () => {
25
- setShowAllProteinSequences(!showAllProteinSequences);
26
- } }, showAllProteinSequences
27
- ? 'Hide all isoform protein sequences'
28
- : 'Show all isoform protein sequences'),
29
- showAllProteinSequences ? (React.createElement(MSATable, { structureSequence: structureSequence, structureName: uniprotId, isoformSequences: isoformSequences })) : null)) : (React.createElement(NotFound, { uniprotId: uniprotId })))) : (React.createElement(Typography, null,
22
+ structureSequence ? (React.createElement(IsoformSequencesToggle, { structureSequence: structureSequence, structureName: uniprotId, isoformSequences: isoformSequences })) : (React.createElement(NotFound, { uniprotId: uniprotId })))) : (React.createElement(Typography, null,
30
23
  "Searching",
31
24
  ' ',
32
- selectedTranscript ? getDisplayName(selectedTranscript) : 'transcript',
25
+ selectedTranscript
26
+ ? getTranscriptDisplayName(selectedTranscript)
27
+ : 'transcript',
33
28
  ' ',
34
29
  "for UniProt ID"));
35
30
  }
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
- import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
- declare const FoldseekSearch: ({ feature, model, handleClose, }: {
2
+ import type { AbstractSessionModel, Feature } from '@jbrowse/core/util';
3
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
4
+ declare const FoldseekSearch: ({ feature, session, view, handleClose, }: {
4
5
  feature: Feature;
5
- model: AbstractTrackModel;
6
+ session: AbstractSessionModel;
7
+ view: LinearGenomeViewModel;
6
8
  handleClose: () => void;
7
9
  }) => React.JSX.Element;
8
10
  export default FoldseekSearch;
@@ -1,6 +1,5 @@
1
1
  import React, { useState } from 'react';
2
2
  import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
3
- import { getContainingView, getSession } from '@jbrowse/core/util';
4
3
  import { Button, DialogActions, DialogContent, TextField, Typography, } from '@mui/material';
5
4
  import { observer } from 'mobx-react';
6
5
  import { makeStyles } from 'tss-react/mui';
@@ -8,10 +7,9 @@ import FoldseekDatabaseSelector from './FoldseekDatabaseSelector';
8
7
  import FoldseekResultsTable from './FoldseekResultsTable';
9
8
  import TranscriptSelector from './TranscriptSelector';
10
9
  import useFoldseekSearch from '../hooks/useFoldseekSearch';
11
- import useIsoformProteinSequences from '../hooks/useIsoformProteinSequences';
12
- import useTranscriptSelection from '../hooks/useTranscriptSelection';
10
+ import useTranscriptIsoformSelection from '../hooks/useTranscriptIsoformSelection';
13
11
  import { DEFAULT_DATABASES } from '../services/foldseekApi';
14
- import { getTranscriptFeatures } from '../utils/util';
12
+ import { stripStopCodon } from '../utils/util';
15
13
  const useStyles = makeStyles()({
16
14
  dialogContent: {
17
15
  width: '80em',
@@ -28,21 +26,15 @@ const useStyles = makeStyles()({
28
26
  gap: 8,
29
27
  },
30
28
  });
31
- const FoldseekSearch = observer(function FoldseekSearch({ feature, model, handleClose, }) {
29
+ const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view, handleClose, }) {
32
30
  const { classes } = useStyles();
33
- const session = getSession(model);
34
- const view = getContainingView(model);
35
31
  const [userEditedSequence, setUserEditedSequence] = useState();
36
32
  const [selectedDatabases, setSelectedDatabases] = useState(DEFAULT_DATABASES);
37
33
  const { results, cleanedAaSequence, di3Sequence, isLoading, isPredicting, error, statusMessage, predictStructure, search, reset, } = useFoldseekSearch();
38
- const transcripts = getTranscriptFeatures(feature);
39
- const { isoformSequences, isLoading: isLoadingIsoforms, error: isoformError, } = useIsoformProteinSequences({ feature, view });
40
- const { userSelection: effectiveSelectedTranscriptId, setUserSelection } = useTranscriptSelection({ options: transcripts, isoformSequences });
41
- const selectedTranscript = transcripts.find(t => t.id() === effectiveSelectedTranscriptId);
42
- const selectedIsoformData = effectiveSelectedTranscriptId
43
- ? isoformSequences?.[effectiveSelectedTranscriptId]
44
- : undefined;
45
- const cleanedSequence = selectedIsoformData?.seq.replace(/\*/g, '') ?? '';
34
+ const { transcripts, isoformSequences, isLoading: isLoadingIsoforms, error: isoformError, selectedTranscriptId: effectiveSelectedTranscriptId, setSelectedTranscriptId: setUserSelection, selectedTranscript, selectedIsoform: selectedIsoformData, } = useTranscriptIsoformSelection({ feature, view });
35
+ const cleanedSequence = selectedIsoformData
36
+ ? stripStopCodon(selectedIsoformData.seq)
37
+ : '';
46
38
  const sequence = userEditedSequence ?? cleanedSequence;
47
39
  const setUserSelectionWithReset = (id) => {
48
40
  setUserSelection(id);
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type { Feature } from '@jbrowse/core/util';
3
+ export default function IsoformSequencesToggle({ structureSequence, structureName, isoformSequences, }: {
4
+ structureSequence: string;
5
+ structureName: string;
6
+ isoformSequences: Record<string, {
7
+ feature: Feature;
8
+ seq: string;
9
+ }>;
10
+ }): React.JSX.Element;
@@ -0,0 +1,13 @@
1
+ import React, { useState } from 'react';
2
+ import { Button } from '@mui/material';
3
+ import MSATable from './MSATable';
4
+ export default function IsoformSequencesToggle({ structureSequence, structureName, isoformSequences, }) {
5
+ const [show, setShow] = useState(false);
6
+ return (React.createElement("div", { style: { margin: 10 } },
7
+ React.createElement(Button, { variant: "contained", color: "primary", onClick: () => {
8
+ setShow(!show);
9
+ } }, show
10
+ ? 'Hide all isoform protein sequences'
11
+ : 'Show all isoform protein sequences'),
12
+ show ? (React.createElement(MSATable, { structureSequence: structureSequence, structureName: structureName, isoformSequences: isoformSequences })) : null));
13
+ }
@@ -1,5 +1,6 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Dialog } from '@jbrowse/core/ui';
3
+ import { getContainingView, getSession } from '@jbrowse/core/util';
3
4
  import { Tab, Tabs } from '@mui/material';
4
5
  import AlphaFoldDBSearch from './AlphaFoldDBSearch';
5
6
  import FoldseekSearch from './FoldseekSearch';
@@ -11,6 +12,8 @@ import { useLocalStorage } from '../hooks/useLocalStorage';
11
12
  export default function LaunchProteinViewDialog({ handleClose, feature, model, }) {
12
13
  const [choice, setChoice] = useState(0);
13
14
  const [alignmentAlgorithm, setAlignmentAlgorithm] = useLocalStorage('jbrowse-protein3d-alignment-algorithm', DEFAULT_ALIGNMENT_ALGORITHM);
15
+ const session = getSession(model);
16
+ const view = getContainingView(model);
14
17
  return (React.createElement(Dialog, { maxWidth: "xl", title: "Launch protein view", titleNode: React.createElement(React.Fragment, null,
15
18
  "Launch protein view ",
16
19
  React.createElement(HelpButton, null)), open: true, onClose: handleClose },
@@ -21,9 +24,9 @@ export default function LaunchProteinViewDialog({ handleClose, feature, model, }
21
24
  React.createElement(Tab, { value: 1, label: "Foldseek search" }),
22
25
  React.createElement(Tab, { value: 2, label: "Open file manually" })),
23
26
  React.createElement(TabPanel, { value: choice, index: 0 },
24
- React.createElement(AlphaFoldDBSearch, { model: model, feature: feature, handleClose: handleClose, alignmentAlgorithm: alignmentAlgorithm, onAlignmentAlgorithmChange: setAlignmentAlgorithm })),
27
+ React.createElement(AlphaFoldDBSearch, { session: session, view: view, feature: feature, handleClose: handleClose, alignmentAlgorithm: alignmentAlgorithm, onAlignmentAlgorithmChange: setAlignmentAlgorithm })),
25
28
  React.createElement(TabPanel, { value: choice, index: 1 },
26
- React.createElement(FoldseekSearch, { model: model, feature: feature, handleClose: handleClose })),
29
+ React.createElement(FoldseekSearch, { session: session, view: view, feature: feature, handleClose: handleClose })),
27
30
  React.createElement(TabPanel, { value: choice, index: 2 },
28
- React.createElement(UserProvidedStructure, { model: model, feature: feature, handleClose: handleClose, alignmentAlgorithm: alignmentAlgorithm, onAlignmentAlgorithmChange: setAlignmentAlgorithm }))));
31
+ React.createElement(UserProvidedStructure, { session: session, view: view, feature: feature, handleClose: handleClose, alignmentAlgorithm: alignmentAlgorithm, onAlignmentAlgorithmChange: setAlignmentAlgorithm }))));
29
32
  }
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
- export default function StructureSourcePicker({ choice, setChoice, structureURL, setStructureURL, file: _file, setFile, pdbId, setPdbId, }: {
2
+ export default function StructureSourcePicker({ choice, setChoice, structureURL, setStructureURL, setFile, pdbId, setPdbId, }: {
3
3
  choice: string;
4
4
  setChoice: (c: string) => void;
5
5
  structureURL: string;
6
6
  setStructureURL: (url: string) => void;
7
- file: File | undefined;
8
7
  setFile: (f: File) => void;
9
8
  pdbId: string;
10
9
  setPdbId: (id: string) => void;
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { Button, FormControl, FormControlLabel, Radio, RadioGroup, TextField, Typography, } from '@mui/material';
3
3
  import HelpButton from './HelpButton';
4
4
  import { getPdbStructureUrl } from '../utils/launchViewUtils';
5
- export default function StructureSourcePicker({ choice, setChoice, structureURL, setStructureURL, file: _file, setFile, pdbId, setPdbId, }) {
5
+ export default function StructureSourcePicker({ choice, setChoice, structureURL, setStructureURL, setFile, pdbId, setPdbId, }) {
6
6
  return (React.createElement("div", { style: { display: 'flex', margin: 30 } },
7
7
  React.createElement(Typography, null,
8
8
  "Open your structure file ",
@@ -1,9 +1,12 @@
1
1
  import React from 'react';
2
2
  import type { AlignmentAlgorithm } from '../../ProteinView/types';
3
- import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
4
- declare const UserProvidedStructure: ({ feature, model, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }: {
3
+ import type { AbstractSessionModel, Feature } from '@jbrowse/core/util';
4
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
5
+ type LGV = LinearGenomeViewModel;
6
+ declare const UserProvidedStructure: ({ feature, session, view, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }: {
5
7
  feature: Feature;
6
- model: AbstractTrackModel;
8
+ session: AbstractSessionModel;
9
+ view: LGV;
7
10
  handleClose: () => void;
8
11
  alignmentAlgorithm: AlignmentAlgorithm;
9
12
  onAlignmentAlgorithmChange: (algorithm: AlignmentAlgorithm) => void;
@@ -1,20 +1,17 @@
1
1
  import React, { useState } from 'react';
2
2
  import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
3
- import { getContainingView, getSession } from '@jbrowse/core/util';
4
3
  import { Button, DialogActions, DialogContent } from '@mui/material';
5
4
  import { observer } from 'mobx-react';
6
5
  import { makeStyles } from 'tss-react/mui';
7
- import MSATable from './MSATable';
6
+ import IsoformSequencesToggle from './IsoformSequencesToggle';
8
7
  import SequenceMismatchNotice from './SequenceMismatchNotice';
9
8
  import StructureSourcePicker from './StructureSourcePicker';
10
9
  import TranscriptSelector from './TranscriptSelector';
11
10
  import ExternalLink from '../../components/ExternalLink';
12
- import useIsoformProteinSequences from '../hooks/useIsoformProteinSequences';
13
- import useLocalStructureFileSequence from '../hooks/useLocalStructureFileSequence';
14
- import useRemoteStructureFileSequence from '../hooks/useRemoteStructureFileSequence';
15
- import useTranscriptSelection from '../hooks/useTranscriptSelection';
11
+ import useStructureFileSequence from '../hooks/useStructureFileSequence';
12
+ import useTranscriptIsoformSelection from '../hooks/useTranscriptIsoformSelection';
16
13
  import { launch3DProteinView } from '../utils/launchViewUtils';
17
- import { getGeneDisplayName, getId, getTranscriptDisplayName, getTranscriptFeatures, stripStopCodon, } from '../utils/util';
14
+ import { getGeneDisplayName, getTranscriptDisplayName, stripStopCodon, } from '../utils/util';
18
15
  const useStyles = makeStyles()(theme => ({
19
16
  dialogContent: {
20
17
  marginTop: theme.spacing(6),
@@ -31,36 +28,20 @@ function HelpText() {
31
28
  React.createElement(ExternalLink, { href: "https://github.com/sokrypton/ColabFold" }, "ColabFold"),
32
29
  ". This plugin will align the protein sequence calculated from the genome to the protein sequence embedded in the structure file which allows for slight differences in these two representations."));
33
30
  }
34
- const UserProvidedStructure = observer(function UserProvidedStructure({ feature, model, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }) {
31
+ const UserProvidedStructure = observer(function UserProvidedStructure({ feature, session, view, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }) {
35
32
  const { classes } = useStyles();
36
- const session = getSession(model);
37
33
  const [file, setFile] = useState();
38
34
  const [pdbId, setPdbId] = useState('');
39
35
  const [choice, setChoice] = useState('file');
40
36
  const [submitError, setSubmitError] = useState();
41
37
  const [structureURL, setStructureURL] = useState('');
42
- const [showAllProteinSequences, setShowAllProteinSequences] = useState(false);
43
38
  const activeFile = choice === 'file' ? file : undefined;
44
39
  const activeURL = choice === 'file' ? '' : structureURL;
45
- const options = getTranscriptFeatures(feature);
46
- const view = getContainingView(model);
47
- const { isoformSequences, error: isoformError } = useIsoformProteinSequences({
48
- feature,
49
- view,
50
- });
51
- const { sequences: localSequences, error: localFileError } = useLocalStructureFileSequence({ file: activeFile });
52
- const { sequences: remoteSequences, error: remoteFileError } = useRemoteStructureFileSequence({ url: activeURL });
40
+ const { sequences: structureSequences, error: fileError } = useStructureFileSequence({ file: activeFile, url: activeURL });
53
41
  const structureName = activeFile?.name ?? activeURL.slice(activeURL.lastIndexOf('/') + 1);
54
- const structureSequences = activeFile ? localSequences : remoteSequences;
55
42
  const structureSequence = structureSequences?.[0];
56
- const { userSelection, setUserSelection } = useTranscriptSelection({
57
- options,
58
- isoformSequences,
59
- structureSequence,
60
- });
61
- const selectedTranscript = options.find(val => getId(val) === userSelection);
62
- const protein = userSelection ? isoformSequences?.[userSelection] : undefined;
63
- const error = isoformError ?? submitError ?? localFileError ?? remoteFileError;
43
+ const { transcripts: options, isoformSequences, selectedTranscriptId: userSelection, setSelectedTranscriptId: setUserSelection, selectedTranscript, selectedIsoform: protein, error: isoformError, } = useTranscriptIsoformSelection({ feature, view, structureSequence });
44
+ const error = isoformError ?? submitError ?? fileError;
64
45
  const canLaunch = !!(activeURL || activeFile) && !!protein && !!selectedTranscript;
65
46
  const sequencesDiffer = !!protein?.seq &&
66
47
  !!structureSequence &&
@@ -94,16 +75,10 @@ const UserProvidedStructure = observer(function UserProvidedStructure({ feature,
94
75
  React.createElement(DialogContent, { className: classes.dialogContent },
95
76
  error ? React.createElement(ErrorMessage, { error: error }) : null,
96
77
  React.createElement(HelpText, null),
97
- React.createElement(StructureSourcePicker, { choice: choice, setChoice: setChoice, structureURL: structureURL, setStructureURL: setStructureURL, file: file, setFile: setFile, pdbId: pdbId, setPdbId: setPdbId }),
78
+ React.createElement(StructureSourcePicker, { choice: choice, setChoice: setChoice, structureURL: structureURL, setStructureURL: setStructureURL, setFile: setFile, pdbId: pdbId, setPdbId: setPdbId }),
98
79
  React.createElement("div", { style: { margin: 20 } }, isoformSequences ? (structureSequence ? (React.createElement(React.Fragment, null,
99
80
  React.createElement(TranscriptSelector, { val: userSelection, setVal: setUserSelection, structureSequence: structureSequence, isoforms: options, feature: feature, isoformSequences: isoformSequences }),
100
- React.createElement("div", { style: { margin: 10 } },
101
- React.createElement(Button, { variant: "contained", color: "primary", onClick: () => {
102
- setShowAllProteinSequences(!showAllProteinSequences);
103
- } }, showAllProteinSequences
104
- ? 'Hide all isoform protein sequences'
105
- : 'Show all isoform protein sequences'),
106
- showAllProteinSequences ? (React.createElement(MSATable, { structureSequence: structureSequence, structureName: structureName, isoformSequences: isoformSequences })) : null))) : null) : (React.createElement(LoadingEllipses, { title: "Loading protein sequences", variant: "h6" })))),
81
+ React.createElement(IsoformSequencesToggle, { structureSequence: structureSequence, structureName: structureName, isoformSequences: isoformSequences }))) : null) : (React.createElement(LoadingEllipses, { title: "Loading protein sequences", variant: "h6" })))),
107
82
  React.createElement(DialogActions, null,
108
83
  sequencesDiffer ? (React.createElement(SequenceMismatchNotice, { alignmentAlgorithm: alignmentAlgorithm, onAlignmentAlgorithmChange: onAlignmentAlgorithmChange })) : null,
109
84
  React.createElement(Button, { variant: "contained", color: "secondary", onClick: () => {
@@ -0,0 +1,5 @@
1
+ export declare const STATIC_SWR_OPTIONS: {
2
+ readonly revalidateOnFocus: false;
3
+ readonly revalidateOnReconnect: false;
4
+ readonly revalidateIfStale: false;
5
+ };
@@ -0,0 +1,8 @@
1
+ // Shared SWR config for one-shot fetches that should never auto-revalidate
2
+ // (structure files, computed protein sequences). keepPreviousData is opt-in
3
+ // per-hook since it avoids result flicker when the key changes.
4
+ export const STATIC_SWR_OPTIONS = {
5
+ revalidateOnFocus: false,
6
+ revalidateOnReconnect: false,
7
+ revalidateIfStale: false,
8
+ };
@@ -2,24 +2,21 @@ import { useState } from 'react';
2
2
  import useAlphaFoldData from './useAlphaFoldData';
3
3
  import useAlphaFoldSequenceSearch from './useAlphaFoldSequenceSearch';
4
4
  import useDebouncedValue from './useDebouncedValue';
5
- import useIsoformProteinSequences from './useIsoformProteinSequences';
6
- import useTranscriptSelection from './useTranscriptSelection';
5
+ import useTranscriptIsoformSelection from './useTranscriptIsoformSelection';
7
6
  import useUniProtSearch from './useUniProtSearch';
8
7
  import getSearchDescription from '../utils/getSearchDescription';
9
- import { extractFeatureIdentifiers, getId, getTranscriptFeatures, stripStopCodon, } from '../utils/util';
8
+ import { extractFeatureIdentifiers, stripStopCodon } from '../utils/util';
10
9
  export default function useAlphaFoldDBSearch({ feature, view, }) {
11
10
  const [lookupMode, setLookupMode] = useState('auto');
12
11
  const [manualUniprotId, setManualUniprotId] = useState('');
13
12
  const geneIds = extractFeatureIdentifiers(feature);
14
- const [selectedQueryId, setSelectedQueryId] = useState(geneIds.recognizedIds[0] ?? 'auto');
13
+ const [selectedQueryId, setSelectedQueryId] = useState('auto');
15
14
  const [sequenceSearchType, setSequenceSearchType] = useState('md5');
16
15
  const [selectedUniprotId, setSelectedUniprotId] = useState();
17
- const transcriptOptions = getTranscriptFeatures(feature);
18
16
  const featureUniprotId = geneIds.uniprotId;
19
17
  const effectiveLookupMode = lookupMode === 'auto' && featureUniprotId ? 'feature' : lookupMode;
20
18
  const isSequenceMode = effectiveLookupMode === 'sequence';
21
19
  const isAutoMode = effectiveLookupMode === 'auto';
22
- const { isoformSequences, isLoading: isIsoformLoading, error: isoformError, } = useIsoformProteinSequences({ feature, view });
23
20
  const { entries: uniprotEntries, isLoading: isLookupLoading, error: lookupError, } = useUniProtSearch({
24
21
  recognizedIds: geneIds.recognizedIds,
25
22
  geneId: geneIds.geneId,
@@ -41,16 +38,12 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
41
38
  const { isLoading: isAlphaFoldLoading, error: alphaFoldError, url: alphaFoldUrl, confidenceUrl: alphaFoldConfidenceUrl, structureSequence: alphaFoldStructureSequence, } = useAlphaFoldData({
42
39
  uniprotId: isSequenceMode ? undefined : uniprotId,
43
40
  });
44
- const { userSelection: effectiveTranscriptId, setUserSelection } = useTranscriptSelection({
45
- options: transcriptOptions,
46
- isoformSequences,
41
+ const { transcripts: transcriptOptions, isoformSequences, isLoading: isIsoformLoading, error: isoformError, selectedTranscriptId: effectiveTranscriptId, setSelectedTranscriptId: setUserSelection, selectedTranscript, selectedIsoform: userSelectedProteinSequence, } = useTranscriptIsoformSelection({
42
+ feature,
43
+ view,
47
44
  structureSequence: alphaFoldStructureSequence,
48
45
  resetKey: uniprotId,
49
46
  });
50
- const selectedTranscript = transcriptOptions.find(f => getId(f) === effectiveTranscriptId);
51
- const userSelectedProteinSequence = effectiveTranscriptId
52
- ? isoformSequences?.[effectiveTranscriptId]
53
- : undefined;
54
47
  const { uniprotId: seqSearchUniprotId, cifUrl: seqSearchUrl, plddtDocUrl: seqSearchConfidenceUrl, structureSequence: seqSearchStructureSequence, isLoading: isSequenceSearchLoading, error: sequenceSearchError, } = useAlphaFoldSequenceSearch({
55
48
  sequence: userSelectedProteinSequence?.seq,
56
49
  searchType: sequenceSearchType,
@@ -1,13 +1,11 @@
1
- import useRemoteStructureFileSequence from './useRemoteStructureFileSequence';
1
+ import useStructureFileSequence from './useStructureFileSequence';
2
2
  import { getAlphaFoldConfidenceUrl, getAlphaFoldStructureUrl, } from '../utils/launchViewUtils';
3
3
  export default function useAlphaFoldData({ uniprotId, }) {
4
4
  const url = uniprotId ? getAlphaFoldStructureUrl(uniprotId) : undefined;
5
5
  const confidenceUrl = uniprotId
6
6
  ? getAlphaFoldConfidenceUrl(uniprotId)
7
7
  : undefined;
8
- const { sequences, isLoading, error } = useRemoteStructureFileSequence({
9
- url,
10
- });
8
+ const { sequences, isLoading, error } = useStructureFileSequence({ url });
11
9
  return {
12
10
  isLoading,
13
11
  error,
@@ -1,5 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  import useSWR from 'swr';
3
+ import { STATIC_SWR_OPTIONS } from './swrOptions';
3
4
  import { jsonfetch } from '../../fetchUtils';
4
5
  import { md5 } from '../utils/md5';
5
6
  import { stripStopCodon } from '../utils/util';
@@ -14,9 +15,7 @@ export default function useAlphaFoldSequenceSearch({ sequence, searchType, enabl
14
15
  const { data, error, isLoading } = useSWR(enabled && searchValue
15
16
  ? `https://alphafold.ebi.ac.uk/api/sequence/summary?id=${encodeURIComponent(searchValue)}&type=${searchType}`
16
17
  : null, jsonfetch, {
17
- revalidateOnFocus: false,
18
- revalidateOnReconnect: false,
19
- revalidateIfStale: false,
18
+ ...STATIC_SWR_OPTIONS,
20
19
  keepPreviousData: true,
21
20
  });
22
21
  return {
@@ -1,4 +1,6 @@
1
+ import { getSession } from '@jbrowse/core/util';
1
2
  import useSWR from 'swr';
3
+ import { STATIC_SWR_OPTIONS } from './swrOptions';
2
4
  import { fetchProteinSeq } from '../utils/calculateProteinSequence';
3
5
  import { getTranscriptFeatures } from '../utils/util';
4
6
  export default function useIsoformProteinSequences({ feature, view, }) {
@@ -7,7 +9,11 @@ export default function useIsoformProteinSequences({ feature, view, }) {
7
9
  const errors = [];
8
10
  const results = await Promise.all(transcripts.map(async (f) => {
9
11
  try {
10
- const seq = await fetchProteinSeq({ view, feature: f });
12
+ const seq = await fetchProteinSeq({
13
+ session: getSession(view),
14
+ assemblyName: view?.assemblyNames?.[0],
15
+ feature: f,
16
+ });
11
17
  return seq ? [f.id(), { feature: f, seq }] : undefined;
12
18
  }
13
19
  catch (e) {
@@ -27,9 +33,7 @@ export default function useIsoformProteinSequences({ feature, view, }) {
27
33
  }
28
34
  return Object.fromEntries(entries);
29
35
  }, {
30
- revalidateOnFocus: false,
31
- revalidateOnReconnect: false,
32
- revalidateIfStale: false,
36
+ ...STATIC_SWR_OPTIONS,
33
37
  keepPreviousData: true,
34
38
  });
35
39
  return { isLoading, isoformSequences: data, error };
@@ -1,4 +1,5 @@
1
- export default function useRemoteStructureFileSequence({ url, }: {
1
+ export default function useStructureFileSequence({ file, url, }: {
2
+ file?: File;
2
3
  url?: string;
3
4
  }): {
4
5
  error: any;
@@ -0,0 +1,47 @@
1
+ import useSWR from 'swr';
2
+ import { STATIC_SWR_OPTIONS } from './swrOptions';
3
+ import { addStructureFromData } from '../../ProteinView/addStructureFromData';
4
+ import { addStructureFromURL } from '../../ProteinView/addStructureFromURL';
5
+ import { extractStructureSequences } from '../../ProteinView/extractStructureSequences';
6
+ import { withTemporaryMolstarPlugin } from '../../ProteinView/withTemporaryMolstarPlugin';
7
+ function detectStructureFormat(fileName) {
8
+ const dot = fileName.lastIndexOf('.');
9
+ const ext = dot >= 0 ? fileName.slice(dot + 1).toLowerCase() : '';
10
+ if (ext === 'cif' || ext === 'mmcif' || ext === 'bcif') {
11
+ return 'mmcif';
12
+ }
13
+ return 'pdb';
14
+ }
15
+ async function fetchSequences({ file, url }) {
16
+ return withTemporaryMolstarPlugin(async (plugin) => {
17
+ const { model } = file
18
+ ? await addStructureFromData({
19
+ data: await file.text(),
20
+ plugin,
21
+ format: detectStructureFormat(file.name),
22
+ })
23
+ : await addStructureFromURL({ url: url, plugin });
24
+ return extractStructureSequences(model);
25
+ });
26
+ }
27
+ // Extract protein sequences from a structure given either a local File or a
28
+ // remote URL (exactly one is expected). Used directly for user-provided
29
+ // structures and wrapped by useAlphaFoldData for AlphaFoldDB URLs.
30
+ export default function useStructureFileSequence({ file, url, }) {
31
+ const key = file
32
+ ? ['structure-file', file.name, file.size, file.lastModified]
33
+ : url
34
+ ? ['structure-url', url]
35
+ : null;
36
+ const { data, error, isLoading } = useSWR(key, async () => {
37
+ const seq = await fetchSequences({ file, url });
38
+ if (!seq) {
39
+ throw new Error('no sequences detected in file');
40
+ }
41
+ return seq;
42
+ }, {
43
+ ...STATIC_SWR_OPTIONS,
44
+ keepPreviousData: true,
45
+ });
46
+ return { error, isLoading, sequences: data };
47
+ }
@@ -0,0 +1,24 @@
1
+ import type { Feature } from '@jbrowse/core/util';
2
+ export default function useTranscriptIsoformSelection({ feature, view, structureSequence, resetKey, }: {
3
+ feature: Feature;
4
+ view?: {
5
+ assemblyNames?: string[];
6
+ };
7
+ structureSequence?: string;
8
+ resetKey?: string;
9
+ }): {
10
+ transcripts: Feature[];
11
+ isoformSequences: Record<string, {
12
+ feature: Feature;
13
+ seq: string;
14
+ }> | undefined;
15
+ isLoading: boolean;
16
+ error: any;
17
+ selectedTranscriptId: string | undefined;
18
+ setSelectedTranscriptId: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
19
+ selectedTranscript: Feature | undefined;
20
+ selectedIsoform: {
21
+ feature: Feature;
22
+ seq: string;
23
+ } | undefined;
24
+ };
@@ -0,0 +1,33 @@
1
+ import useIsoformProteinSequences from './useIsoformProteinSequences';
2
+ import useTranscriptSelection from './useTranscriptSelection';
3
+ import { getId, getTranscriptFeatures } from '../utils/util';
4
+ // Bundles the transcript-isoform wiring shared by all three launch tabs:
5
+ // list transcripts, fetch their protein sequences, auto/manually select one,
6
+ // and resolve the selection back to its feature + sequence.
7
+ export default function useTranscriptIsoformSelection({ feature, view, structureSequence, resetKey, }) {
8
+ const transcripts = getTranscriptFeatures(feature);
9
+ const { isoformSequences, isLoading, error } = useIsoformProteinSequences({
10
+ feature,
11
+ view,
12
+ });
13
+ const { userSelection, setUserSelection } = useTranscriptSelection({
14
+ options: transcripts,
15
+ isoformSequences,
16
+ structureSequence,
17
+ resetKey,
18
+ });
19
+ const selectedTranscript = transcripts.find(f => getId(f) === userSelection);
20
+ const selectedIsoform = userSelection
21
+ ? isoformSequences?.[userSelection]
22
+ : undefined;
23
+ return {
24
+ transcripts,
25
+ isoformSequences,
26
+ isLoading,
27
+ error,
28
+ selectedTranscriptId: userSelection,
29
+ setSelectedTranscriptId: setUserSelection,
30
+ selectedTranscript,
31
+ selectedIsoform,
32
+ };
33
+ }
@@ -1,4 +1,5 @@
1
1
  import useSWR from 'swr';
2
+ import { STATIC_SWR_OPTIONS } from './swrOptions';
2
3
  import { searchUniProtEntries } from '../services/lookupMethods';
3
4
  import { isRecognizedDatabaseId } from '../utils/util';
4
5
  export default function useUniProtSearch({ recognizedIds = [], geneId, geneName, selectedQueryId = 'auto', enabled = true, }) {
@@ -29,9 +30,7 @@ export default function useUniProtSearch({ recognizedIds = [], geneId, geneName,
29
30
  geneId,
30
31
  geneName: geneNameToSearch,
31
32
  }), {
32
- revalidateOnFocus: false,
33
- revalidateOnReconnect: false,
34
- revalidateIfStale: false,
33
+ ...STATIC_SWR_OPTIONS,
35
34
  keepPreviousData: true,
36
35
  });
37
36
  return {
@@ -1,4 +1,4 @@
1
- import type { Feature } from '@jbrowse/core/util';
1
+ import type { AbstractSessionModel, Feature } from '@jbrowse/core/util';
2
2
  export interface Feat {
3
3
  start: number;
4
4
  end: number;
@@ -20,9 +20,8 @@ export declare function getProteinSequence({ feature, seq, }: {
20
20
  seq: string;
21
21
  feature: Feature;
22
22
  }): string;
23
- export declare function fetchProteinSeq({ feature, view, }: {
23
+ export declare function fetchProteinSeq({ feature, session, assemblyName, }: {
24
24
  feature: Feature;
25
- view: {
26
- assemblyNames?: string[];
27
- } | undefined;
25
+ session: AbstractSessionModel;
26
+ assemblyName: string | undefined;
28
27
  }): Promise<string | undefined>;