jbrowse-plugin-protein3d 0.4.5 → 0.4.7

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 (49) hide show
  1. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +1 -3
  2. package/dist/LaunchProteinView/components/FoldseekSearch.js +6 -2
  3. package/dist/LaunchProteinView/components/IdentifierSelector.d.ts +1 -2
  4. package/dist/LaunchProteinView/components/IdentifierSelector.js +2 -8
  5. package/dist/LaunchProteinView/components/TranscriptSelector.js +3 -6
  6. package/dist/LaunchProteinView/components/UserProvidedStructure.js +9 -7
  7. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.d.ts +1 -5
  8. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.js +11 -22
  9. package/dist/LaunchProteinView/hooks/useAlphaFoldData.d.ts +1 -6
  10. package/dist/LaunchProteinView/hooks/useAlphaFoldData.js +7 -23
  11. package/dist/LaunchProteinView/hooks/useTranscriptSelection.d.ts +2 -1
  12. package/dist/LaunchProteinView/hooks/useTranscriptSelection.js +12 -4
  13. package/dist/LaunchProteinView/hooks/useUniProtSearch.d.ts +1 -2
  14. package/dist/LaunchProteinView/hooks/useUniProtSearch.js +5 -25
  15. package/dist/LaunchProteinView/services/foldseekApi.js +1 -2
  16. package/dist/LaunchProteinView/services/lookupMethods.js +1 -1
  17. package/dist/LaunchProteinView/utils/calculateProteinSequence.js +9 -9
  18. package/dist/LaunchProteinView/utils/launchViewUtils.js +1 -10
  19. package/dist/ProteinView/applyLociInteractivity.js +20 -28
  20. package/dist/fetchUtils.d.ts +1 -1
  21. package/dist/jbrowse-plugin-protein3d.umd.production.min.js +16 -16
  22. package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
  23. package/dist/version.d.ts +1 -1
  24. package/dist/version.js +1 -1
  25. package/package.json +1 -1
  26. package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +0 -8
  27. package/src/LaunchProteinView/components/FoldseekSearch.tsx +13 -2
  28. package/src/LaunchProteinView/components/IdentifierSelector.tsx +1 -10
  29. package/src/LaunchProteinView/components/TranscriptSelector.tsx +4 -12
  30. package/src/LaunchProteinView/components/UserProvidedStructure.tsx +12 -9
  31. package/src/LaunchProteinView/hooks/useAlphaFoldDBSearch.ts +7 -23
  32. package/src/LaunchProteinView/hooks/useAlphaFoldData.ts +7 -45
  33. package/src/LaunchProteinView/hooks/useTranscriptSelection.ts +16 -4
  34. package/src/LaunchProteinView/hooks/useUniProtSearch.ts +4 -28
  35. package/src/LaunchProteinView/services/foldseekApi.ts +1 -2
  36. package/src/LaunchProteinView/services/lookupMethods.ts +1 -1
  37. package/src/LaunchProteinView/utils/calculateProteinSequence.ts +9 -15
  38. package/src/LaunchProteinView/utils/launchViewUtils.ts +1 -12
  39. package/src/ProteinView/applyLociInteractivity.ts +29 -29
  40. package/src/ProteinView/components/ProteinFeatureTrack.tsx +4 -1
  41. package/src/ProteinView/components/ProteinViewHeader.tsx +11 -9
  42. package/src/fetchUtils.ts +4 -1
  43. package/src/version.ts +1 -1
  44. package/dist/LaunchProteinView/components/AlphaFoldEntrySelector.d.ts +0 -12
  45. package/dist/LaunchProteinView/components/AlphaFoldEntrySelector.js +0 -17
  46. package/dist/LaunchProteinView/hooks/useAlphaFoldUrl.d.ts +0 -13
  47. package/dist/LaunchProteinView/hooks/useAlphaFoldUrl.js +0 -17
  48. package/src/LaunchProteinView/components/AlphaFoldEntrySelector.tsx +0 -47
  49. package/src/LaunchProteinView/hooks/useAlphaFoldUrl.ts +0 -31
@@ -5,7 +5,6 @@ import { DialogActions, DialogContent, Typography } from '@mui/material';
5
5
  import { observer } from 'mobx-react';
6
6
  import { makeStyles } from 'tss-react/mui';
7
7
  import AlphaFoldDBSearchStatus from './AlphaFoldDBSearchStatus';
8
- import AlphaFoldEntrySelector from './AlphaFoldEntrySelector';
9
8
  import IdentifierSelector from './IdentifierSelector';
10
9
  import ProteinViewActions from './ProteinViewActions';
11
10
  import SequenceSearchStatus from './SequenceSearchStatus';
@@ -62,8 +61,7 @@ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, model,
62
61
  "directly and use \"Enter manually\" above, or use \"Search sequence against AlphaFoldDB API\" if available.")),
63
62
  state.showStructureSelectors && (React.createElement(React.Fragment, null,
64
63
  React.createElement("div", { className: classes.selectorsRow },
65
- React.createElement(TranscriptSelector, { val: state.userSelection, setVal: state.setUserSelection, structureSequence: state.structureSequence, feature: feature, isoforms: state.transcriptOptions, isoformSequences: state.isoformSequences }),
66
- state.showAlphaFoldEntrySelector && (React.createElement(AlphaFoldEntrySelector, { predictions: state.predictions, selectedEntryIndex: state.selectedEntryIndex, onSelectionChange: state.setSelectedEntryIndex }))),
64
+ React.createElement(TranscriptSelector, { val: state.userSelection, setVal: state.setUserSelection, structureSequence: state.structureSequence, feature: feature, isoforms: state.transcriptOptions, isoformSequences: state.isoformSequences })),
67
65
  state.showSequenceSearchStatus && (React.createElement(SequenceSearchStatus, { isLoading: state.isSequenceSearchLoading, uniprotId: state.uniprotId, url: state.url, hasProteinSequence: !!state.userSelectedProteinSequence, sequenceSearchType: state.sequenceSearchType })),
68
66
  state.showAlphaFoldDBSearchStatus && (React.createElement(AlphaFoldDBSearchStatus, { uniprotId: state.uniprotId, selectedTranscript: state.selectedTranscript, structureSequence: state.structureSequence, isoformSequences: state.isoformSequences, url: state.url }))))),
69
67
  React.createElement(DialogActions, null,
@@ -76,8 +76,12 @@ const FoldseekSearch = observer(function FoldseekSearch({ feature, model, handle
76
76
  statusMessage ? (React.createElement(LoadingEllipses, { variant: "subtitle2", message: statusMessage })) : null,
77
77
  results ? (React.createElement(FoldseekResultsTable, { results: results, session: session, view: view, feature: feature, selectedTranscript: selectedTranscript, userProvidedTranscriptSequence: sequence, onClose: handleClose })) : null),
78
78
  React.createElement(DialogActions, null,
79
- React.createElement(Button, { variant: "contained", color: "secondary", onClick: handleClose }, "Cancel"),
80
- results ? (React.createElement(Button, { variant: "outlined", onClick: reset }, "New search")) : null,
79
+ React.createElement(Button, { variant: "contained", color: "secondary", onClick: () => {
80
+ handleClose();
81
+ } }, "Cancel"),
82
+ results ? (React.createElement(Button, { variant: "outlined", onClick: () => {
83
+ reset();
84
+ } }, "New search")) : null,
81
85
  !di3Sequence ? (React.createElement(Button, { variant: "contained", color: "primary", disabled: !canPredict, onClick: () => {
82
86
  if (sequence.trim()) {
83
87
  void predictStructure(sequence.trim());
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
2
  interface IdentifierSelectorProps {
3
3
  recognizedIds: string[];
4
- uniprotId?: string;
5
4
  geneName?: string;
6
5
  selectedId: string;
7
6
  onSelectedIdChange: (id: string) => void;
8
7
  }
9
- export default function IdentifierSelector({ recognizedIds, uniprotId, geneName, selectedId, onSelectedIdChange, }: IdentifierSelectorProps): React.JSX.Element | null;
8
+ export default function IdentifierSelector({ recognizedIds, geneName, selectedId, onSelectedIdChange, }: IdentifierSelectorProps): React.JSX.Element | null;
10
9
  export {};
@@ -35,26 +35,20 @@ function getIdLabel(id) {
35
35
  }
36
36
  return id;
37
37
  }
38
- export default function IdentifierSelector({ recognizedIds, uniprotId, geneName, selectedId, onSelectedIdChange, }) {
38
+ export default function IdentifierSelector({ recognizedIds, geneName, selectedId, onSelectedIdChange, }) {
39
39
  const [expanded, setExpanded] = useState(false);
40
40
  // Build list of selectable options
41
41
  const options = [
42
42
  { value: 'auto', label: 'Auto (try all)' },
43
43
  ...recognizedIds.map(id => ({ value: id, label: getIdLabel(id) })),
44
44
  ];
45
- if (uniprotId) {
46
- options.push({
47
- value: `uniprot:${uniprotId}`,
48
- label: `${uniprotId} (UniProt)`,
49
- });
50
- }
51
45
  if (geneName) {
52
46
  options.push({
53
47
  value: `gene:${geneName}`,
54
48
  label: `${geneName} (gene name)`,
55
49
  });
56
50
  }
57
- if (recognizedIds.length === 0 && !uniprotId && !geneName) {
51
+ if (recognizedIds.length === 0 && !geneName) {
58
52
  return null;
59
53
  }
60
54
  if (!expanded) {
@@ -19,21 +19,18 @@ export default function TranscriptSelector({ val, setVal, isoforms, isoformSeque
19
19
  nonMatches.push(f);
20
20
  }
21
21
  }
22
- matches.sort((a, b) => isoformSequences[b.id()].seq.length -
23
- isoformSequences[a.id()].seq.length);
24
- nonMatches.sort((a, b) => isoformSequences[b.id()].seq.length -
25
- isoformSequences[a.id()].seq.length);
22
+ const byLengthDesc = (a, b) => isoformSequences[b.id()].seq.length - isoformSequences[a.id()].seq.length;
26
23
  return (React.createElement(TextField, { value: val ?? '', onChange: event => {
27
24
  setVal(event.target.value);
28
25
  }, label: "Choose transcript isoform", select: true, disabled: disabled },
29
- matches.map(f => (React.createElement(MenuItem, { value: f.id(), key: f.id() },
26
+ matches.toSorted(byLengthDesc).map(f => (React.createElement(MenuItem, { value: f.id(), key: f.id() },
30
27
  geneName,
31
28
  " - ",
32
29
  getTranscriptDisplayName(f),
33
30
  " (",
34
31
  isoformSequences[f.id()].seq.length,
35
32
  "aa) (matches structure residues)"))),
36
- nonMatches.map(f => (React.createElement(MenuItem, { value: f.id(), key: f.id() },
33
+ nonMatches.toSorted(byLengthDesc).map(f => (React.createElement(MenuItem, { value: f.id(), key: f.id() },
37
34
  geneName,
38
35
  " - ",
39
36
  getTranscriptDisplayName(f),
@@ -40,16 +40,18 @@ const UserProvidedStructure = observer(function UserProvidedStructure({ feature,
40
40
  const [submitError, setSubmitError] = useState();
41
41
  const [structureURL, setStructureURL] = useState('');
42
42
  const [showAllProteinSequences, setShowAllProteinSequences] = useState(false);
43
+ const activeFile = choice === 'file' ? file : undefined;
44
+ const activeURL = choice === 'file' ? '' : structureURL;
43
45
  const options = getTranscriptFeatures(feature);
44
46
  const view = getContainingView(model);
45
47
  const { isoformSequences, error: isoformError } = useIsoformProteinSequences({
46
48
  feature,
47
49
  view,
48
50
  });
49
- const { sequences: structureSequences1, error: localFileError } = useLocalStructureFileSequence({ file });
50
- const { sequences: structureSequences2, error: remoteFileError } = useRemoteStructureFileSequence({ url: structureURL });
51
- const structureName = file?.name ?? structureURL.slice(structureURL.lastIndexOf('/') + 1);
52
- const structureSequences = structureSequences1 ?? structureSequences2;
51
+ const { sequences: localSequences, error: localFileError } = useLocalStructureFileSequence({ file: activeFile });
52
+ const { sequences: remoteSequences, error: remoteFileError } = useRemoteStructureFileSequence({ url: activeURL });
53
+ const structureName = activeFile?.name ?? activeURL.slice(activeURL.lastIndexOf('/') + 1);
54
+ const structureSequences = activeFile ? localSequences : remoteSequences;
53
55
  const structureSequence = structureSequences?.[0];
54
56
  const { userSelection, setUserSelection } = useTranscriptSelection({
55
57
  options,
@@ -59,7 +61,7 @@ const UserProvidedStructure = observer(function UserProvidedStructure({ feature,
59
61
  const selectedTranscript = options.find(val => getId(val) === userSelection);
60
62
  const protein = userSelection ? isoformSequences?.[userSelection] : undefined;
61
63
  const error = isoformError ?? submitError ?? localFileError ?? remoteFileError;
62
- const canLaunch = !!(structureURL || file) && !!protein && !!selectedTranscript;
64
+ const canLaunch = !!(activeURL || activeFile) && !!protein && !!selectedTranscript;
63
65
  const sequencesDiffer = !!protein?.seq &&
64
66
  !!structureSequence &&
65
67
  stripStopCodon(protein.seq) !== structureSequence;
@@ -68,8 +70,8 @@ const UserProvidedStructure = observer(function UserProvidedStructure({ feature,
68
70
  return;
69
71
  }
70
72
  try {
71
- const structureData = file ? await file.text() : undefined;
72
- const url = structureURL ? structureURL : undefined;
73
+ const structureData = activeFile ? await activeFile.text() : undefined;
74
+ const url = activeURL ? activeURL : undefined;
73
75
  if (!url && !structureData) {
74
76
  return;
75
77
  }
@@ -15,7 +15,7 @@ export default function useAlphaFoldDBSearch({ feature, view, }: {
15
15
  sequenceSearchType: SequenceSearchType;
16
16
  setSequenceSearchType: import("react").Dispatch<import("react").SetStateAction<SequenceSearchType>>;
17
17
  selectedUniprotId: string | undefined;
18
- setSelectedUniprotId: (id: string | undefined) => void;
18
+ setSelectedUniprotId: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
19
19
  userSelection: string | undefined;
20
20
  setUserSelection: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
21
21
  transcriptOptions: Feature[];
@@ -29,9 +29,6 @@ export default function useAlphaFoldDBSearch({ feature, view, }: {
29
29
  seq: string;
30
30
  } | undefined;
31
31
  uniprotEntries: import("../services/lookupMethods").UniProtEntry[];
32
- predictions: import("./useAlphaFoldUrl").AlphaFoldPrediction[] | undefined;
33
- selectedEntryIndex: number;
34
- setSelectedEntryIndex: import("react").Dispatch<import("react").SetStateAction<number>>;
35
32
  recognizedIds: string[];
36
33
  geneName: string | undefined;
37
34
  featureUniprotId: string | undefined;
@@ -50,7 +47,6 @@ export default function useAlphaFoldDBSearch({ feature, view, }: {
50
47
  selectedTableAccession: string | undefined;
51
48
  showUniprotResults: boolean;
52
49
  showNoResults: boolean;
53
- showAlphaFoldEntrySelector: boolean;
54
50
  showSequenceSearchStatus: boolean;
55
51
  showAlphaFoldDBSearchStatus: boolean;
56
52
  isLoading: boolean;
@@ -3,9 +3,10 @@ import useAlphaFoldData from './useAlphaFoldData';
3
3
  import useAlphaFoldSequenceSearch from './useAlphaFoldSequenceSearch';
4
4
  import useDebouncedValue from './useDebouncedValue';
5
5
  import useIsoformProteinSequences from './useIsoformProteinSequences';
6
+ import useTranscriptSelection from './useTranscriptSelection';
6
7
  import useUniProtSearch from './useUniProtSearch';
7
8
  import getSearchDescription from '../utils/getSearchDescription';
8
- import { extractFeatureIdentifiers, getId, getTranscriptFeatures, selectBestTranscript, stripStopCodon, } from '../utils/util';
9
+ import { extractFeatureIdentifiers, getId, getTranscriptFeatures, stripStopCodon, } from '../utils/util';
9
10
  export default function useAlphaFoldDBSearch({ feature, view, }) {
10
11
  const [lookupMode, setLookupMode] = useState('auto');
11
12
  const [manualUniprotId, setManualUniprotId] = useState('');
@@ -13,7 +14,6 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
13
14
  const [selectedQueryId, setSelectedQueryId] = useState(geneIds.recognizedIds[0] ?? 'auto');
14
15
  const [sequenceSearchType, setSequenceSearchType] = useState('md5');
15
16
  const [selectedUniprotId, setSelectedUniprotId] = useState();
16
- const [userTranscriptId, setUserTranscriptId] = useState();
17
17
  const transcriptOptions = getTranscriptFeatures(feature);
18
18
  const featureUniprotId = geneIds.uniprotId;
19
19
  const effectiveLookupMode = lookupMode === 'auto' && featureUniprotId ? 'feature' : lookupMode;
@@ -38,18 +38,15 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
38
38
  : effectiveLookupMode === 'manual'
39
39
  ? debouncedManualUniprotId
40
40
  : undefined;
41
- const { predictions, isLoading: isAlphaFoldLoading, error: alphaFoldError, selectedEntryIndex, setSelectedEntryIndex, url: alphaFoldUrl, confidenceUrl: alphaFoldConfidenceUrl, structureSequence: alphaFoldStructureSequence, } = useAlphaFoldData({
41
+ const { isLoading: isAlphaFoldLoading, error: alphaFoldError, url: alphaFoldUrl, confidenceUrl: alphaFoldConfidenceUrl, structureSequence: alphaFoldStructureSequence, } = useAlphaFoldData({
42
42
  uniprotId: isSequenceMode ? undefined : uniprotId,
43
43
  });
44
- let autoTranscriptId;
45
- if (isoformSequences) {
46
- autoTranscriptId = selectBestTranscript({
47
- options: transcriptOptions,
48
- isoformSequences,
49
- structureSequence: alphaFoldStructureSequence,
50
- })?.id();
51
- }
52
- const effectiveTranscriptId = userTranscriptId ?? autoTranscriptId;
44
+ const { userSelection: effectiveTranscriptId, setUserSelection } = useTranscriptSelection({
45
+ options: transcriptOptions,
46
+ isoformSequences,
47
+ structureSequence: alphaFoldStructureSequence,
48
+ resetKey: uniprotId,
49
+ });
53
50
  const selectedTranscript = transcriptOptions.find(f => getId(f) === effectiveTranscriptId);
54
51
  const userSelectedProteinSequence = effectiveTranscriptId
55
52
  ? isoformSequences?.[effectiveTranscriptId]
@@ -68,10 +65,6 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
68
65
  ? seqSearchStructureSequence
69
66
  : alphaFoldStructureSequence;
70
67
  const finalUniprotId = isSequenceMode ? seqSearchUniprotId : uniprotId;
71
- const setSelectedUniprotIdWithReset = (id) => {
72
- setSelectedUniprotId(id);
73
- setUserTranscriptId(undefined);
74
- };
75
68
  const loadingStatuses = [
76
69
  isLookupLoading && 'Looking up UniProt ID',
77
70
  isIsoformLoading && 'Loading protein sequences from transcript isoforms',
@@ -96,17 +89,14 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
96
89
  sequenceSearchType,
97
90
  setSequenceSearchType,
98
91
  selectedUniprotId,
99
- setSelectedUniprotId: setSelectedUniprotIdWithReset,
92
+ setSelectedUniprotId,
100
93
  userSelection: effectiveTranscriptId,
101
- setUserSelection: setUserTranscriptId,
94
+ setUserSelection,
102
95
  transcriptOptions,
103
96
  selectedTranscript,
104
97
  isoformSequences,
105
98
  userSelectedProteinSequence,
106
99
  uniprotEntries,
107
- predictions,
108
- selectedEntryIndex,
109
- setSelectedEntryIndex,
110
100
  recognizedIds: geneIds.recognizedIds,
111
101
  geneName: geneIds.geneName,
112
102
  featureUniprotId,
@@ -144,7 +134,6 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
144
134
  isAutoMode &&
145
135
  !isLookupLoading &&
146
136
  uniprotEntries.length === 0,
147
- showAlphaFoldEntrySelector: !!predictions && !isSequenceMode,
148
137
  showSequenceSearchStatus: isSequenceMode,
149
138
  showAlphaFoldDBSearchStatus: !!finalStructureSequence && !!finalUniprotId && !isSequenceMode,
150
139
  isLoading,
@@ -1,13 +1,8 @@
1
- export default function useAlphaFoldData({ uniprotId, useApiSearch, }: {
1
+ export default function useAlphaFoldData({ uniprotId }: {
2
2
  uniprotId?: string;
3
- useApiSearch?: boolean;
4
3
  }): {
5
- predictions: import("./useAlphaFoldUrl").AlphaFoldPrediction[] | undefined;
6
4
  isLoading: boolean;
7
5
  error: any;
8
- selectedEntryIndex: number;
9
- setSelectedEntryIndex: import("react").Dispatch<import("react").SetStateAction<number>>;
10
- selectedPrediction: import("./useAlphaFoldUrl").AlphaFoldPrediction | undefined;
11
6
  url: string | undefined;
12
7
  confidenceUrl: string | undefined;
13
8
  structureSequence: string | undefined;
@@ -1,34 +1,18 @@
1
- import { useState } from 'react';
2
- import useAlphaFoldUrl from './useAlphaFoldUrl';
3
1
  import useRemoteStructureFileSequence from './useRemoteStructureFileSequence';
4
2
  import { getAlphaFoldConfidenceUrl, getAlphaFoldStructureUrl, } from '../utils/launchViewUtils';
5
- export default function useAlphaFoldData({ uniprotId, useApiSearch = false, }) {
6
- const [selectedEntryIndex, setSelectedEntryIndex] = useState(0);
7
- const hardcodedUrl = uniprotId
8
- ? getAlphaFoldStructureUrl(uniprotId)
9
- : undefined;
10
- const hardcodedConfidenceUrl = uniprotId
3
+ export default function useAlphaFoldData({ uniprotId }) {
4
+ const url = uniprotId ? getAlphaFoldStructureUrl(uniprotId) : undefined;
5
+ const confidenceUrl = uniprotId
11
6
  ? getAlphaFoldConfidenceUrl(uniprotId)
12
7
  : undefined;
13
- // Optionally fetch from API for isoform search
14
- const { predictions, isLoading: isApiLoading, error: apiError, } = useAlphaFoldUrl({ uniprotId: useApiSearch ? uniprotId : undefined });
15
- const selectedPrediction = predictions?.[selectedEntryIndex];
16
- const url = useApiSearch ? selectedPrediction?.cifUrl : hardcodedUrl;
17
- const confidenceUrl = selectedPrediction?.plddtDocUrl ?? hardcodedConfidenceUrl;
18
- // Always fetch sequence from structure file
19
- const { sequences, isLoading: isSequenceLoading, error: sequenceError, } = useRemoteStructureFileSequence({ url });
20
- const structureSequence = sequences?.[0];
21
- const isLoading = isApiLoading || isSequenceLoading;
22
- const error = apiError ?? sequenceError;
8
+ const { sequences, isLoading, error } = useRemoteStructureFileSequence({
9
+ url,
10
+ });
23
11
  return {
24
- predictions,
25
12
  isLoading,
26
13
  error,
27
- selectedEntryIndex,
28
- setSelectedEntryIndex,
29
- selectedPrediction,
30
14
  url,
31
15
  confidenceUrl,
32
- structureSequence,
16
+ structureSequence: sequences?.[0],
33
17
  };
34
18
  }
@@ -1,11 +1,12 @@
1
1
  import type { Feature } from '@jbrowse/core/util';
2
- export default function useTranscriptSelection({ options, isoformSequences, structureSequence, }: {
2
+ export default function useTranscriptSelection({ options, isoformSequences, structureSequence, resetKey, }: {
3
3
  options: Feature[];
4
4
  isoformSequences?: Record<string, {
5
5
  feature: Feature;
6
6
  seq: string;
7
7
  }>;
8
8
  structureSequence?: string;
9
+ resetKey?: string;
9
10
  }): {
10
11
  userSelection: string | undefined;
11
12
  setUserSelection: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
@@ -1,10 +1,18 @@
1
1
  import { useMemo, useState } from 'react';
2
2
  import { selectBestTranscript } from '../utils/util';
3
- export default function useTranscriptSelection({ options, isoformSequences, structureSequence, }) {
3
+ export default function useTranscriptSelection({ options, isoformSequences, structureSequence, resetKey, }) {
4
4
  const [userSelection, setUserSelection] = useState();
5
+ const [prevResetKey, setPrevResetKey] = useState(resetKey);
6
+ if (resetKey !== prevResetKey) {
7
+ setPrevResetKey(resetKey);
8
+ setUserSelection(undefined);
9
+ }
5
10
  const autoSelection = useMemo(() => isoformSequences !== undefined
6
- ? selectBestTranscript({ options, isoformSequences, structureSequence })?.id()
11
+ ? selectBestTranscript({
12
+ options,
13
+ isoformSequences,
14
+ structureSequence,
15
+ })?.id()
7
16
  : undefined, [options, structureSequence, isoformSequences]);
8
- const effectiveSelection = userSelection ?? autoSelection;
9
- return { userSelection: effectiveSelection, setUserSelection };
17
+ return { userSelection: userSelection ?? autoSelection, setUserSelection };
10
18
  }
@@ -1,7 +1,6 @@
1
1
  import type { UniProtEntry } from '../services/lookupMethods';
2
- export default function useUniProtSearch({ recognizedIds, uniprotId, geneId, geneName, selectedQueryId, enabled, }: {
2
+ export default function useUniProtSearch({ recognizedIds, geneId, geneName, selectedQueryId, enabled, }: {
3
3
  recognizedIds?: string[];
4
- uniprotId?: string;
5
4
  geneId?: string;
6
5
  geneName?: string;
7
6
  selectedQueryId?: string;
@@ -1,12 +1,7 @@
1
1
  import useSWR from 'swr';
2
2
  import { searchUniProtEntries } from '../services/lookupMethods';
3
3
  import { isRecognizedDatabaseId } from '../utils/util';
4
- export default function useUniProtSearch({ recognizedIds = [], uniprotId, geneId, geneName, selectedQueryId = 'auto', enabled = true, }) {
5
- // If selected ID is a UniProt accession (prefixed with uniprot:), return it directly
6
- const isDirectUniProt = selectedQueryId.startsWith('uniprot:');
7
- const directUniProtId = isDirectUniProt
8
- ? selectedQueryId.replace('uniprot:', '')
9
- : undefined;
4
+ export default function useUniProtSearch({ recognizedIds = [], geneId, geneName, selectedQueryId = 'auto', enabled = true, }) {
10
5
  // Determine what to search based on selectedQueryId
11
6
  let idsToSearch = [];
12
7
  let geneNameToSearch;
@@ -20,16 +15,10 @@ export default function useUniProtSearch({ recognizedIds = [], uniprotId, geneId
20
15
  else if (isRecognizedDatabaseId(selectedQueryId)) {
21
16
  idsToSearch = [selectedQueryId];
22
17
  }
23
- // Has valid ID if we have any recognized database IDs or a gene name
24
- const hasRecognizedId = idsToSearch.some(id => isRecognizedDatabaseId(id));
25
- const hasValidId = hasRecognizedId || Boolean(geneNameToSearch) || Boolean(uniprotId);
26
- const { data, error, isLoading } = useSWR(enabled && hasValidId && !isDirectUniProt
27
- ? [
28
- 'uniprotSearch',
29
- selectedQueryId,
30
- idsToSearch.join(','),
31
- geneNameToSearch,
32
- ]
18
+ const hasValidId = idsToSearch.some(id => isRecognizedDatabaseId(id)) ||
19
+ Boolean(geneNameToSearch);
20
+ const { data, error, isLoading } = useSWR(enabled && hasValidId
21
+ ? ['uniprotSearch', selectedQueryId, idsToSearch.join(','), geneNameToSearch]
33
22
  : null, async () => searchUniProtEntries({
34
23
  recognizedIds: idsToSearch,
35
24
  geneId,
@@ -40,15 +29,6 @@ export default function useUniProtSearch({ recognizedIds = [], uniprotId, geneId
40
29
  revalidateIfStale: false,
41
30
  keepPreviousData: true,
42
31
  });
43
- // If direct UniProt accession selected, return it as a synthetic entry
44
- if (isDirectUniProt && directUniProtId) {
45
- return {
46
- entries: [{ accession: directUniProtId, isReviewed: true }],
47
- isLoading: false,
48
- error: undefined,
49
- hasValidId: true,
50
- };
51
- }
52
32
  return {
53
33
  entries: data ?? [],
54
34
  isLoading,
@@ -81,8 +81,7 @@ export async function pollFoldseekStatus(ticketId) {
81
81
  return result;
82
82
  }
83
83
  export async function getFoldseekResults(ticketId) {
84
- const result = await jsonfetch(`https://search.foldseek.com/api/result/${ticketId}/0`);
85
- return result;
84
+ return jsonfetch(`https://search.foldseek.com/api/result/${ticketId}/0`);
86
85
  }
87
86
  export async function waitForFoldseekResults(ticketId, onStatusChange) {
88
87
  const maxAttempts = 180;
@@ -32,7 +32,7 @@ function buildXrefQuery(id) {
32
32
  }
33
33
  async function searchUniProt(query, size = 10) {
34
34
  const url = `https://rest.uniprot.org/uniprotkb/search?query=${encodeURIComponent(query)}&fields=${UNIPROT_FIELDS}&size=${size}`;
35
- const data = (await jsonfetch(url));
35
+ const data = await jsonfetch(url);
36
36
  return data.results.map(mapApiResultToEntry);
37
37
  }
38
38
  async function searchByXref(id) {
@@ -30,20 +30,20 @@ export function dedupe(list) {
30
30
  return list.filter((item, pos, ary) => !pos || getItemId(item) !== getItemId(ary[pos - 1]));
31
31
  }
32
32
  export function getProteinSequence({ feature, seq, }) {
33
- // @ts-expect-error
34
- const f = feature.toJSON();
35
- const subfeatures = f.subfeatures;
33
+ const featureStart = feature.get('start');
34
+ const strand = feature.get('strand');
35
+ const subfeatures = feature.get('subfeatures') ?? [];
36
36
  const cds = dedupe(subfeatures
37
- .toSorted((a, b) => a.start - b.start)
37
+ .toSorted((a, b) => a.get('start') - b.get('start'))
38
38
  .map(sub => ({
39
- ...sub,
40
- start: sub.start - f.start,
41
- end: sub.end - f.start,
39
+ start: sub.get('start') - featureStart,
40
+ end: sub.get('end') - featureStart,
41
+ type: sub.get('type'),
42
42
  }))
43
43
  .filter(f => f.type === 'CDS'));
44
44
  return calculateProteinSequence({
45
- cds: f.strand === -1 ? revlist(cds, seq.length) : cds,
46
- sequence: f.strand === -1 ? revcom(seq) : seq,
45
+ cds: strand === -1 ? revlist(cds, seq.length) : cds,
46
+ sequence: strand === -1 ? revcom(seq) : seq,
47
47
  codonTable: generateCodonTable(defaultCodonTable),
48
48
  });
49
49
  }
@@ -75,16 +75,7 @@ export function launch3DProteinView({ session, view, feature, selectedTranscript
75
75
  displayName: displayName ??
76
76
  formatViewName('Protein view', feature, selectedTranscript, uniprotId),
77
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
- }
78
+ return session.addView('ProteinView', snap);
88
79
  }
89
80
  export async function launch1DProteinView({ session, view, feature, selectedTranscript, uniprotId, confidenceUrl, }) {
90
81
  if (!uniprotId || !isSessionWithAddTracks(session)) {
@@ -1,9 +1,22 @@
1
1
  import loadMolstar from './loadMolstar';
2
2
  import { getMolstarStructureSelection } from './util';
3
- export async function applyLociInteractivityMultiple({ structure, residues, plugin, mode, }) {
4
- if (mode === 'clear' || residues.length === 0) {
3
+ function clearLoci(plugin) {
4
+ plugin.managers.interactivity.lociHighlights.clearHighlights();
5
+ plugin.managers.interactivity.lociSelects.deselectAll();
6
+ }
7
+ function applyLoci(plugin, loci, mode) {
8
+ if (mode === 'highlight') {
5
9
  plugin.managers.interactivity.lociHighlights.clearHighlights();
10
+ plugin.managers.interactivity.lociHighlights.highlight({ loci });
11
+ }
12
+ else {
6
13
  plugin.managers.interactivity.lociSelects.deselectAll();
14
+ plugin.managers.interactivity.lociSelects.select({ loci });
15
+ }
16
+ }
17
+ export async function applyLociInteractivityMultiple({ structure, residues, plugin, mode, }) {
18
+ if (mode === 'clear' || residues.length === 0) {
19
+ clearLoci(plugin);
7
20
  return;
8
21
  }
9
22
  const { StructureSelection, Script } = await loadMolstar();
@@ -15,18 +28,11 @@ export async function applyLociInteractivityMultiple({ structure, residues, plug
15
28
  'group-by': Q.struct.atomProperty.macromolecular.residueKey(),
16
29
  }), structure);
17
30
  const loci = StructureSelection.toLociWithSourceUnits(sel);
18
- if (mode === 'highlight') {
19
- plugin.managers.interactivity.lociHighlights.clearHighlights();
20
- plugin.managers.interactivity.lociHighlights.highlight({ loci });
21
- }
22
- else {
23
- plugin.managers.interactivity.lociSelects.deselectAll();
24
- plugin.managers.interactivity.lociSelects.select({ loci });
25
- }
31
+ applyLoci(plugin, loci, mode);
26
32
  }
27
33
  export async function applyLociInteractivity({ structure, startResidue, endResidue, plugin, mode, }) {
28
34
  if (mode === 'clear') {
29
- plugin.managers.interactivity.lociHighlights.clearHighlights();
35
+ clearLoci(plugin);
30
36
  return;
31
37
  }
32
38
  const { StructureSelection, Script } = await loadMolstar();
@@ -44,18 +50,11 @@ export async function applyLociInteractivity({ structure, startResidue, endResid
44
50
  'group-by': Q.struct.atomProperty.macromolecular.residueKey(),
45
51
  }), structure);
46
52
  const loci = StructureSelection.toLociWithSourceUnits(sel);
47
- if (mode === 'highlight') {
48
- plugin.managers.interactivity.lociHighlights.clearHighlights();
49
- plugin.managers.interactivity.lociHighlights.highlight({ loci });
50
- }
51
- else {
52
- plugin.managers.interactivity.lociSelects.deselectAll();
53
- plugin.managers.interactivity.lociSelects.select({ loci });
54
- }
53
+ applyLoci(plugin, loci, mode);
55
54
  }
56
55
  export async function applyLociInteractivitySingle({ structure, selectedResidue, plugin, mode, }) {
57
56
  if (mode === 'clear') {
58
- plugin.managers.interactivity.lociHighlights.clearHighlights();
57
+ clearLoci(plugin);
59
58
  return;
60
59
  }
61
60
  const { StructureSelection } = await loadMolstar();
@@ -64,12 +63,5 @@ export async function applyLociInteractivitySingle({ structure, selectedResidue,
64
63
  selectedResidue: selectedResidue + 1,
65
64
  });
66
65
  const loci = StructureSelection.toLociWithSourceUnits(sel);
67
- if (mode === 'highlight') {
68
- plugin.managers.interactivity.lociHighlights.clearHighlights();
69
- plugin.managers.interactivity.lociHighlights.highlight({ loci });
70
- }
71
- else {
72
- plugin.managers.interactivity.lociSelects.deselectAll();
73
- plugin.managers.interactivity.lociSelects.select({ loci });
74
- }
66
+ applyLoci(plugin, loci, mode);
75
67
  }
@@ -1,3 +1,3 @@
1
1
  export declare function myfetch(url: string, args?: RequestInit): Promise<Response>;
2
- export declare function jsonfetch(url: string, args?: RequestInit): Promise<any>;
2
+ export declare function jsonfetch<T = unknown>(url: string, args?: RequestInit): Promise<T>;
3
3
  export declare function timeout(time: number): Promise<unknown>;