jbrowse-plugin-protein3d 0.7.0 → 0.8.0

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 (29) hide show
  1. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +10 -2
  2. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.d.ts +3 -0
  3. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.js +25 -1
  4. package/dist/LaunchProteinView/hooks/useUniProtSearch.d.ts +2 -1
  5. package/dist/LaunchProteinView/hooks/useUniProtSearch.js +3 -1
  6. package/dist/LaunchProteinView/utils/launchViewUtils.d.ts +2 -9
  7. package/dist/LaunchProteinView/utils/launchViewUtils.js +3 -45
  8. package/dist/LaunchProteinView/utils/structureUrls.d.ts +8 -0
  9. package/dist/LaunchProteinView/utils/structureUrls.js +48 -0
  10. package/dist/LaunchProteinView/utils/util.d.ts +8 -0
  11. package/dist/LaunchProteinView/utils/util.js +18 -0
  12. package/dist/ProteinView/proteinViewSpec.d.ts +4 -0
  13. package/dist/ProteinView/structureModel.js +19 -1
  14. package/dist/jbrowse-plugin-protein3d.umd.production.min.js +15 -15
  15. package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
  16. package/dist/version.d.ts +1 -1
  17. package/dist/version.js +1 -1
  18. package/package.json +1 -1
  19. package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +30 -7
  20. package/src/LaunchProteinView/hooks/useAlphaFoldDBSearch.ts +33 -1
  21. package/src/LaunchProteinView/hooks/useUniProtSearch.ts +6 -0
  22. package/src/LaunchProteinView/utils/extractTaxonId.test.ts +28 -0
  23. package/src/LaunchProteinView/utils/launchViewUtils.ts +22 -63
  24. package/src/LaunchProteinView/utils/structureUrls.ts +66 -0
  25. package/src/LaunchProteinView/utils/util.ts +20 -0
  26. package/src/ProteinView/proteinViewSpec.ts +4 -0
  27. package/src/ProteinView/structureModel.test.ts +48 -0
  28. package/src/ProteinView/structureModel.ts +24 -1
  29. package/src/version.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
3
- import { DialogActions, DialogContent, Typography } from '@mui/material';
3
+ import { DialogActions, DialogContent, TextField, Typography, } from '@mui/material';
4
4
  import { observer } from 'mobx-react';
5
5
  import { makeStyles } from 'tss-react/mui';
6
6
  import AlphaFoldDBSearchStatus from './AlphaFoldDBSearchStatus';
@@ -28,6 +28,12 @@ const useStyles = makeStyles()({
28
28
  gap: 20,
29
29
  alignItems: 'flex-start',
30
30
  },
31
+ endRow: {
32
+ display: 'flex',
33
+ flexDirection: 'row',
34
+ gap: 12,
35
+ alignItems: 'flex-start',
36
+ },
31
37
  });
32
38
  const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session, view, handleClose, alignmentAlgorithm, onAlignmentAlgorithmChange, }) {
33
39
  const { classes } = useStyles();
@@ -35,7 +41,9 @@ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session
35
41
  return (React.createElement(React.Fragment, null,
36
42
  React.createElement(DialogContent, { className: classes.dialogContent },
37
43
  state.error ? React.createElement(ErrorMessage, { error: state.error }) : null,
38
- React.createElement(UniProtIdInput, { lookupMode: state.lookupMode, onLookupModeChange: state.setLookupMode, manualUniprotId: state.manualUniprotId, onManualUniprotIdChange: state.setManualUniprotId, featureUniprotId: state.featureUniprotId, hasProteinSequence: !!state.userSelectedProteinSequence?.seq, sequenceSearchType: state.sequenceSearchType, onSequenceSearchTypeChange: state.setSequenceSearchType, endContent: state.showIdentifierSelector ? (React.createElement(IdentifierSelector, { recognizedIds: state.recognizedIds, geneName: state.geneName, selectedId: state.selectedQueryId, onSelectedIdChange: state.setSelectedQueryId })) : null }),
44
+ React.createElement(UniProtIdInput, { lookupMode: state.lookupMode, onLookupModeChange: state.setLookupMode, manualUniprotId: state.manualUniprotId, onManualUniprotIdChange: state.setManualUniprotId, featureUniprotId: state.featureUniprotId, hasProteinSequence: !!state.userSelectedProteinSequence?.seq, sequenceSearchType: state.sequenceSearchType, onSequenceSearchTypeChange: state.setSequenceSearchType, endContent: state.showIdentifierSelector ? (React.createElement("div", { className: classes.endRow },
45
+ React.createElement(IdentifierSelector, { recognizedIds: state.recognizedIds, geneName: state.geneName, selectedId: state.selectedQueryId, onSelectedIdChange: state.setSelectedQueryId }),
46
+ React.createElement(TextField, { size: "small", label: "Organism (NCBI taxon)", helperText: "Scopes the gene-name search", value: state.taxonId, onChange: event => { state.setTaxonId(event.target.value); }, placeholder: String(state.effectiveTaxonId), slotProps: { inputLabel: { shrink: true } }, sx: { width: 180 } }))) : null }),
39
47
  state.loadingStatuses.map(status => (React.createElement(LoadingEllipses, { key: status, variant: "subtitle2", message: status }))),
40
48
  state.showUniprotResults && (React.createElement(React.Fragment, null,
41
49
  React.createElement(Typography, { variant: "body2", color: "textSecondary" },
@@ -10,6 +10,9 @@ export default function useAlphaFoldDBSearch({ feature, view, }: {
10
10
  setLookupMode: import("react").Dispatch<import("react").SetStateAction<LookupMode>>;
11
11
  manualUniprotId: string;
12
12
  setManualUniprotId: import("react").Dispatch<import("react").SetStateAction<string>>;
13
+ taxonId: string;
14
+ setTaxonId: import("react").Dispatch<import("react").SetStateAction<string>>;
15
+ effectiveTaxonId: number;
13
16
  selectedQueryId: string;
14
17
  setSelectedQueryId: import("react").Dispatch<import("react").SetStateAction<string>>;
15
18
  sequenceSearchType: SequenceSearchType;
@@ -1,15 +1,34 @@
1
1
  import { useState } from 'react';
2
+ import { getConf } from '@jbrowse/core/configuration';
3
+ import { getSession } from '@jbrowse/core/util';
2
4
  import useAlphaFoldData from './useAlphaFoldData';
3
5
  import useAlphaFoldSequenceSearch from './useAlphaFoldSequenceSearch';
4
6
  import useDebouncedValue from './useDebouncedValue';
5
7
  import useTranscriptIsoformSelection from './useTranscriptIsoformSelection';
6
8
  import useUniProtSearch from './useUniProtSearch';
7
9
  import getSearchDescription from '../utils/getSearchDescription';
8
- import { extractFeatureIdentifiers, stripStopCodon } from '../utils/util';
10
+ import { extractFeatureIdentifiers, extractTaxonId, stripStopCodon, } from '../utils/util';
9
11
  export default function useAlphaFoldDBSearch({ feature, view, }) {
10
12
  const [lookupMode, setLookupMode] = useState('auto');
11
13
  const [manualUniprotId, setManualUniprotId] = useState('');
14
+ const [taxonIdInput, setTaxonIdInput] = useState('');
12
15
  const geneIds = extractFeatureIdentifiers(feature);
16
+ // The gene-name UniProt search is ambiguous across species, so scope it to
17
+ // the assembly's organism. jb2hubs assemblies carry the NCBI taxon in the
18
+ // reference-sequence track metadata (UCSC: metadata.taxId, GenArk:
19
+ // metadata.ucsc.taxId). Falls back to human via searchUniProtEntries when
20
+ // absent; a user override (taxonIdInput) always wins.
21
+ const assemblyName = view.assemblyNames[0];
22
+ const assembly = assemblyName
23
+ ? getSession(view).assemblyManager.get(assemblyName)
24
+ : undefined;
25
+ const assemblyTaxonId = assembly
26
+ ? extractTaxonId(getConf(assembly, ['sequence', 'metadata']))
27
+ : undefined;
28
+ const overrideTaxon = Number(taxonIdInput.trim());
29
+ const effectiveTaxonId = taxonIdInput.trim() !== '' && Number.isFinite(overrideTaxon) && overrideTaxon > 0
30
+ ? overrideTaxon
31
+ : assemblyTaxonId;
13
32
  const [selectedQueryId, setSelectedQueryId] = useState('auto');
14
33
  const [sequenceSearchType, setSequenceSearchType] = useState('md5');
15
34
  const [selectedUniprotId, setSelectedUniprotId] = useState();
@@ -21,6 +40,7 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
21
40
  recognizedIds: geneIds.recognizedIds,
22
41
  geneId: geneIds.geneId,
23
42
  geneName: geneIds.geneName,
43
+ organismId: effectiveTaxonId,
24
44
  selectedQueryId,
25
45
  enabled: isAutoMode,
26
46
  });
@@ -84,6 +104,10 @@ export default function useAlphaFoldDBSearch({ feature, view, }) {
84
104
  setLookupMode,
85
105
  manualUniprotId,
86
106
  setManualUniprotId,
107
+ taxonId: taxonIdInput,
108
+ setTaxonId: setTaxonIdInput,
109
+ // shown as the field placeholder so the user sees the organism in effect
110
+ effectiveTaxonId: effectiveTaxonId ?? 9606,
87
111
  selectedQueryId,
88
112
  setSelectedQueryId,
89
113
  sequenceSearchType,
@@ -1,8 +1,9 @@
1
1
  import type { UniProtEntry } from '../services/lookupMethods';
2
- export default function useUniProtSearch({ recognizedIds, geneId, geneName, selectedQueryId, enabled, }: {
2
+ export default function useUniProtSearch({ recognizedIds, geneId, geneName, organismId, selectedQueryId, enabled, }: {
3
3
  recognizedIds?: string[];
4
4
  geneId?: string;
5
5
  geneName?: string;
6
+ organismId?: number;
6
7
  selectedQueryId?: string;
7
8
  enabled?: boolean;
8
9
  }): {
@@ -2,7 +2,7 @@ import useSWR from 'swr';
2
2
  import { STATIC_SWR_OPTIONS } from './swrOptions';
3
3
  import { searchUniProtEntries } from '../services/lookupMethods';
4
4
  import { isRecognizedDatabaseId } from '../utils/util';
5
- export default function useUniProtSearch({ recognizedIds = [], geneId, geneName, selectedQueryId = 'auto', enabled = true, }) {
5
+ export default function useUniProtSearch({ recognizedIds = [], geneId, geneName, organismId, selectedQueryId = 'auto', enabled = true, }) {
6
6
  // Determine what to search based on selectedQueryId
7
7
  let idsToSearch = [];
8
8
  let geneNameToSearch;
@@ -25,11 +25,13 @@ export default function useUniProtSearch({ recognizedIds = [], geneId, geneName,
25
25
  idsToSearch.join(','),
26
26
  geneNameToSearch,
27
27
  geneId,
28
+ organismId,
28
29
  ]
29
30
  : null, async () => searchUniProtEntries({
30
31
  recognizedIds: idsToSearch,
31
32
  geneId,
32
33
  geneName: geneNameToSearch,
34
+ organismId,
33
35
  }), {
34
36
  ...STATIC_SWR_OPTIONS,
35
37
  keepPreviousData: true,
@@ -1,3 +1,5 @@
1
+ import { ALPHAFOLD_VERSION, getAlphaFoldConfidenceUrl, getAlphaFoldMsaUrl, getAlphaFoldStructureUrl, getConfidenceUrlFromTarget, getPdbStructureUrl, getStructureUrlFromTarget, getUniprotIdFromAlphaFoldTarget } from './structureUrls';
2
+ export { ALPHAFOLD_VERSION, getAlphaFoldConfidenceUrl, getAlphaFoldMsaUrl, getAlphaFoldStructureUrl, getConfidenceUrlFromTarget, getPdbStructureUrl, getStructureUrlFromTarget, getUniprotIdFromAlphaFoldTarget, };
1
3
  import type { AbstractSessionModel, Feature, SessionWithAddTracks } from '@jbrowse/core/util';
2
4
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
3
5
  declare global {
@@ -5,14 +7,6 @@ declare global {
5
7
  JBrowsePluginMsaView?: unknown;
6
8
  }
7
9
  }
8
- export declare const ALPHAFOLD_VERSION = "v6";
9
- export declare function getAlphaFoldStructureUrl(uniprotId: string, version?: string): string;
10
- export declare function getAlphaFoldConfidenceUrl(uniprotId: string, version?: string): string;
11
- export declare function getAlphaFoldMsaUrl(uniprotId: string, version?: string): string;
12
- export declare function getPdbStructureUrl(pdbId: string): string;
13
- export declare function getUniprotIdFromAlphaFoldTarget(target: string): string | undefined;
14
- export declare function getStructureUrlFromTarget(target: string, db: string): string | undefined;
15
- export declare function getConfidenceUrlFromTarget(target: string): string | undefined;
16
10
  interface LaunchViewParams {
17
11
  session: AbstractSessionModel;
18
12
  view: LinearGenomeViewModel;
@@ -48,4 +42,3 @@ export declare function getConditionalProteinLaunches({ session, view, feature,
48
42
  launchMsa: (() => import("@jbrowse/core/util").AbstractViewModel | undefined) | undefined;
49
43
  };
50
44
  export declare function launch3DProteinViewWithMsa(params: LaunchViewParams & Launch3DExtraParams): import("@jbrowse/core/util").AbstractViewModel | undefined;
51
- export {};
@@ -1,53 +1,11 @@
1
1
  import { isSessionWithAddTracks } from '@jbrowse/core/util';
2
2
  import { maybeLaunchSideBySide } from './sideBySide';
3
+ import { ALPHAFOLD_VERSION, getAlphaFoldConfidenceUrl, getAlphaFoldMsaUrl, getAlphaFoldStructureUrl, getConfidenceUrlFromTarget, getPdbStructureUrl, getStructureUrlFromTarget, getUniprotIdFromAlphaFoldTarget, } from './structureUrls';
3
4
  import { getGeneDisplayName, getTranscriptDisplayName } from './util';
4
5
  import { proteinViewSnapshot } from '../../ProteinView/proteinViewSpec';
5
6
  import { launchProteinAnnotationView } from '../components/launchProteinAnnotationView';
6
- export const ALPHAFOLD_VERSION = 'v6';
7
- export function getAlphaFoldStructureUrl(uniprotId, version = ALPHAFOLD_VERSION) {
8
- return `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-model_${version}.cif`;
9
- }
10
- export function getAlphaFoldConfidenceUrl(uniprotId, version = ALPHAFOLD_VERSION) {
11
- return `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-confidence_${version}.json`;
12
- }
13
- export function getAlphaFoldMsaUrl(uniprotId, version = ALPHAFOLD_VERSION) {
14
- return `https://alphafold.ebi.ac.uk/files/msa/AF-${uniprotId}-F1-msa_${version}.a3m`;
15
- }
16
- export function getPdbStructureUrl(pdbId) {
17
- return `https://files.rcsb.org/download/${pdbId}.cif`;
18
- }
19
- // Foldseek targets may contain a description after the ID separated by a
20
- // space, e.g. "AF-P16442-F1-model_v6 Histo-blood group ABO transferase".
21
- function extractTargetId(target) {
22
- return target.split(' ')[0];
23
- }
24
- export function getUniprotIdFromAlphaFoldTarget(target) {
25
- // Handles both "AF-P16442-F1-model_v6" and full URLs like
26
- // "https://alphafold.ebi.ac.uk/files/AF-P16442-F1-model_v6.cif"
27
- const match = /AF-([A-Z0-9]+)-F\d+/.exec(extractTargetId(target));
28
- return match?.[1];
29
- }
30
- export function getStructureUrlFromTarget(target, db) {
31
- const targetId = extractTargetId(target);
32
- if (targetId.startsWith('AF-')) {
33
- return `https://alphafold.ebi.ac.uk/files/${targetId}.cif`;
34
- }
35
- if (db === 'pdb100') {
36
- const pdbId = targetId.split('_')[0];
37
- if (pdbId.length === 4) {
38
- return getPdbStructureUrl(pdbId);
39
- }
40
- }
41
- return undefined;
42
- }
43
- export function getConfidenceUrlFromTarget(target) {
44
- const targetId = extractTargetId(target);
45
- if (targetId.startsWith('AF-')) {
46
- const confidenceId = targetId.replace('-model_', '-confidence_');
47
- return `https://alphafold.ebi.ac.uk/files/${confidenceId}.json`;
48
- }
49
- return undefined;
50
- }
7
+ // Re-exported so existing importers keep their `launchViewUtils` import path.
8
+ export { ALPHAFOLD_VERSION, getAlphaFoldConfidenceUrl, getAlphaFoldMsaUrl, getAlphaFoldStructureUrl, getConfidenceUrlFromTarget, getPdbStructureUrl, getStructureUrlFromTarget, getUniprotIdFromAlphaFoldTarget, };
51
9
  export function formatViewName(prefix, feature, selectedTranscript, uniprotId) {
52
10
  return [
53
11
  ...new Set([
@@ -0,0 +1,8 @@
1
+ export declare const ALPHAFOLD_VERSION = "v6";
2
+ export declare function getAlphaFoldStructureUrl(uniprotId: string, version?: string): string;
3
+ export declare function getAlphaFoldConfidenceUrl(uniprotId: string, version?: string): string;
4
+ export declare function getAlphaFoldMsaUrl(uniprotId: string, version?: string): string;
5
+ export declare function getPdbStructureUrl(pdbId: string): string;
6
+ export declare function getUniprotIdFromAlphaFoldTarget(target: string): string | undefined;
7
+ export declare function getStructureUrlFromTarget(target: string, db: string): string | undefined;
8
+ export declare function getConfidenceUrlFromTarget(target: string): string | undefined;
@@ -0,0 +1,48 @@
1
+ // Pure structure-URL builders and target parsers. Kept dependency-free so both
2
+ // the launch flow and the ProteinView model (e.g. resolving a `uniprotId`/`pdbId`
3
+ // shorthand at hydration) can import them without pulling in heavy launch code.
4
+ export const ALPHAFOLD_VERSION = 'v6';
5
+ export function getAlphaFoldStructureUrl(uniprotId, version = ALPHAFOLD_VERSION) {
6
+ return `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-model_${version}.cif`;
7
+ }
8
+ export function getAlphaFoldConfidenceUrl(uniprotId, version = ALPHAFOLD_VERSION) {
9
+ return `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-confidence_${version}.json`;
10
+ }
11
+ export function getAlphaFoldMsaUrl(uniprotId, version = ALPHAFOLD_VERSION) {
12
+ return `https://alphafold.ebi.ac.uk/files/msa/AF-${uniprotId}-F1-msa_${version}.a3m`;
13
+ }
14
+ export function getPdbStructureUrl(pdbId) {
15
+ return `https://files.rcsb.org/download/${pdbId}.cif`;
16
+ }
17
+ // Foldseek targets may contain a description after the ID separated by a
18
+ // space, e.g. "AF-P16442-F1-model_v6 Histo-blood group ABO transferase".
19
+ function extractTargetId(target) {
20
+ return target.split(' ')[0];
21
+ }
22
+ export function getUniprotIdFromAlphaFoldTarget(target) {
23
+ // Handles both "AF-P16442-F1-model_v6" and full URLs like
24
+ // "https://alphafold.ebi.ac.uk/files/AF-P16442-F1-model_v6.cif"
25
+ const match = /AF-([A-Z0-9]+)-F\d+/.exec(extractTargetId(target));
26
+ return match?.[1];
27
+ }
28
+ export function getStructureUrlFromTarget(target, db) {
29
+ const targetId = extractTargetId(target);
30
+ if (targetId.startsWith('AF-')) {
31
+ return `https://alphafold.ebi.ac.uk/files/${targetId}.cif`;
32
+ }
33
+ if (db === 'pdb100') {
34
+ const pdbId = targetId.split('_')[0];
35
+ if (pdbId.length === 4) {
36
+ return getPdbStructureUrl(pdbId);
37
+ }
38
+ }
39
+ return undefined;
40
+ }
41
+ export function getConfidenceUrlFromTarget(target) {
42
+ const targetId = extractTargetId(target);
43
+ if (targetId.startsWith('AF-')) {
44
+ const confidenceId = targetId.replace('-model_', '-confidence_');
45
+ return `https://alphafold.ebi.ac.uk/files/${confidenceId}.json`;
46
+ }
47
+ return undefined;
48
+ }
@@ -1,5 +1,13 @@
1
1
  import type { Feature } from '@jbrowse/core/util';
2
2
  export declare function stripStopCodon(seq: string): string;
3
+ /**
4
+ * Pull an NCBI taxon id out of reference-sequence-track metadata. jb2hubs
5
+ * assemblies expose it differently by source: UCSC golden-path spreads it flat
6
+ * (`metadata.taxId`), GenArk nests the raw hub stanza (`metadata.ucsc.taxId`).
7
+ * `taxonId` is accepted too. Returns a positive integer, or undefined when
8
+ * absent/unparseable so callers can fall back to a default organism.
9
+ */
10
+ export declare function extractTaxonId(metadata: unknown): number | undefined;
3
11
  export declare function getTranscriptFeatures(feature: Feature): Feature[];
4
12
  export declare function stripTrailingVersion(s?: string): string | undefined;
5
13
  export declare function getId(val?: Feature): string;
@@ -1,6 +1,24 @@
1
1
  export function stripStopCodon(seq) {
2
2
  return seq.replaceAll('*', '');
3
3
  }
4
+ /**
5
+ * Pull an NCBI taxon id out of reference-sequence-track metadata. jb2hubs
6
+ * assemblies expose it differently by source: UCSC golden-path spreads it flat
7
+ * (`metadata.taxId`), GenArk nests the raw hub stanza (`metadata.ucsc.taxId`).
8
+ * `taxonId` is accepted too. Returns a positive integer, or undefined when
9
+ * absent/unparseable so callers can fall back to a default organism.
10
+ */
11
+ export function extractTaxonId(metadata) {
12
+ if (metadata === null || typeof metadata !== 'object') {
13
+ return undefined;
14
+ }
15
+ const m = metadata;
16
+ const ucsc = m.ucsc !== null && typeof m.ucsc === 'object'
17
+ ? m.ucsc
18
+ : undefined;
19
+ const n = Number(m.taxId ?? m.taxonId ?? ucsc?.taxId);
20
+ return Number.isFinite(n) && n > 0 ? n : undefined;
21
+ }
4
22
  export function getTranscriptFeatures(feature) {
5
23
  // check if we are looking at a 'two-level' or 'three-level' feature by
6
24
  // finding exon/CDS subfeatures. we want to select from transcript names
@@ -9,6 +9,8 @@ import type { SimpleFeatureSerialized } from '@jbrowse/core/util';
9
9
  export interface ProteinStructureSpec {
10
10
  url?: string;
11
11
  data?: string;
12
+ uniprotId?: string;
13
+ pdbId?: string;
12
14
  connectedViewId?: string;
13
15
  feature?: SimpleFeatureSerialized;
14
16
  userProvidedTranscriptSequence?: string;
@@ -48,6 +50,8 @@ export declare function proteinViewSnapshot(spec: ProteinViewSpec): {
48
50
  userProvidedTranscriptSequence: string;
49
51
  url?: string;
50
52
  data?: string;
53
+ uniprotId?: string;
54
+ pdbId?: string;
51
55
  connectedViewId?: string;
52
56
  feature?: SimpleFeatureSerialized;
53
57
  initialSelection?: {
@@ -12,7 +12,7 @@ import { kyteDoolittleScores, mapResidueValuesToColumns } from './residueTracks'
12
12
  import subscribeMolstarInteraction from './subscribeMolstarInteraction';
13
13
  import { coerceAlignmentAlgorithm } from './types';
14
14
  import { checkHovered } from './util';
15
- import { getUniprotIdFromAlphaFoldTarget } from '../LaunchProteinView/utils/launchViewUtils';
15
+ import { getAlphaFoldStructureUrl, getPdbStructureUrl, getUniprotIdFromAlphaFoldTarget, } from '../LaunchProteinView/utils/structureUrls';
16
16
  import { stripStopCodon } from '../LaunchProteinView/utils/util';
17
17
  import { genomeToTranscriptSeqMapping } from '../mappings';
18
18
  const Structure = types
@@ -52,6 +52,24 @@ const Structure = types
52
52
  * session spec open with a domain pre-highlighted, with no click.
53
53
  */
54
54
  initialSelection: types.frozen(),
55
+ })
56
+ // Input-only shorthand: remap a `{ uniprotId }`/`{ pdbId }` snapshot to a
57
+ // concrete `url` at hydration and strip the shorthand keys (they are not
58
+ // stored — uniprotId stays derivable from the url via the getter below), so a
59
+ // hand-authored snapshot loads without the caller knowing the AlphaFold/RCSB
60
+ // URL format. An explicit url/data always wins; the shorthand resolves the
61
+ // canonical isoform (AF-<id>-F1) only. Idempotent: a re-snapshot has no
62
+ // shorthand keys and an already-set url, so it passes through unchanged.
63
+ .preProcessSnapshot(({ uniprotId, pdbId, ...rest }) => {
64
+ const hasSource = rest.url !== undefined || rest.data !== undefined;
65
+ const url = hasSource
66
+ ? rest.url
67
+ : uniprotId !== undefined
68
+ ? getAlphaFoldStructureUrl(uniprotId)
69
+ : pdbId !== undefined
70
+ ? getPdbStructureUrl(pdbId)
71
+ : rest.url;
72
+ return { ...rest, url };
55
73
  })
56
74
  .volatile(() => ({
57
75
  /**