jbrowse-plugin-protein3d 0.5.2 → 0.5.4

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 (78) hide show
  1. package/README.md +10 -0
  2. package/dist/AddHighlightModel/ProteinToMsaHoverSync.js +8 -4
  3. package/dist/AddHighlightModel/findConnectedMsaView.d.ts +23 -0
  4. package/dist/AddHighlightModel/findConnectedMsaView.js +23 -0
  5. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +3 -3
  6. package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.d.ts +2 -4
  7. package/dist/LaunchProteinView/components/IdentifierSelector.js +2 -36
  8. package/dist/LaunchProteinView/components/IsoformSequencesToggle.d.ts +2 -5
  9. package/dist/LaunchProteinView/components/MSATable.d.ts +2 -5
  10. package/dist/LaunchProteinView/components/TabPanel.js +4 -2
  11. package/dist/LaunchProteinView/components/TranscriptSelector.d.ts +2 -4
  12. package/dist/LaunchProteinView/components/TranscriptSelector.js +16 -32
  13. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.d.ts +2 -8
  14. package/dist/LaunchProteinView/hooks/useIsoformProteinSequences.d.ts +2 -4
  15. package/dist/LaunchProteinView/hooks/useTranscriptIsoformSelection.d.ts +2 -8
  16. package/dist/LaunchProteinView/hooks/useTranscriptSelection.d.ts +2 -4
  17. package/dist/LaunchProteinView/hooks/useTranscriptSelection.js +3 -3
  18. package/dist/LaunchProteinView/services/lookupMethods.js +2 -20
  19. package/dist/LaunchProteinView/utils/launchViewUtils.js +8 -6
  20. package/dist/LaunchProteinView/utils/util.d.ts +24 -13
  21. package/dist/LaunchProteinView/utils/util.js +60 -49
  22. package/dist/Protein1DViewRegistry/index.js +4 -9
  23. package/dist/ProteinView/__fixtures__/structureFixtures.d.ts +7 -0
  24. package/dist/ProteinView/__fixtures__/structureFixtures.js +16 -0
  25. package/dist/ProteinView/applyLociInteractivity.d.ts +4 -1
  26. package/dist/ProteinView/applyLociInteractivity.js +9 -1
  27. package/dist/ProteinView/chooseMappedEntity.d.ts +32 -0
  28. package/dist/ProteinView/chooseMappedEntity.js +71 -0
  29. package/dist/ProteinView/components/FeatureBar.js +1 -0
  30. package/dist/ProteinView/extractStructureSequences.d.ts +12 -0
  31. package/dist/ProteinView/extractStructureSequences.js +9 -1
  32. package/dist/ProteinView/loadStructureData.d.ts +2 -1
  33. package/dist/ProteinView/loadStructureData.js +4 -4
  34. package/dist/ProteinView/model.d.ts +33 -9
  35. package/dist/ProteinView/proteinToGenomeMapping.d.ts +1 -1
  36. package/dist/ProteinView/proteinToGenomeMapping.js +10 -7
  37. package/dist/ProteinView/structureModel.d.ts +40 -2
  38. package/dist/ProteinView/structureModel.js +87 -33
  39. package/dist/ProteinView/subscribeMolstarInteraction.d.ts +3 -0
  40. package/dist/ProteinView/subscribeMolstarInteraction.js +1 -0
  41. package/dist/jbrowse-plugin-protein3d.umd.production.min.js +16 -16
  42. package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
  43. package/dist/mappings.d.ts +1 -0
  44. package/dist/mappings.js +14 -1
  45. package/dist/version.d.ts +1 -1
  46. package/dist/version.js +1 -1
  47. package/package.json +3 -1
  48. package/src/AddHighlightModel/ProteinToMsaHoverSync.tsx +9 -4
  49. package/src/AddHighlightModel/findConnectedMsaView.test.ts +53 -0
  50. package/src/AddHighlightModel/findConnectedMsaView.ts +35 -0
  51. package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +5 -5
  52. package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +2 -1
  53. package/src/LaunchProteinView/components/IdentifierSelector.tsx +2 -37
  54. package/src/LaunchProteinView/components/IsoformSequencesToggle.tsx +2 -2
  55. package/src/LaunchProteinView/components/MSATable.tsx +2 -2
  56. package/src/LaunchProteinView/components/TabPanel.tsx +4 -2
  57. package/src/LaunchProteinView/components/TranscriptSelector.tsx +15 -33
  58. package/src/LaunchProteinView/hooks/useIsoformProteinSequences.ts +2 -3
  59. package/src/LaunchProteinView/hooks/useTranscriptSelection.ts +11 -13
  60. package/src/LaunchProteinView/services/lookupMethods.ts +2 -21
  61. package/src/LaunchProteinView/utils/launchViewUtils.ts +8 -6
  62. package/src/LaunchProteinView/utils/util.ts +98 -64
  63. package/src/Protein1DViewRegistry/index.test.ts +55 -0
  64. package/src/Protein1DViewRegistry/index.ts +5 -11
  65. package/src/ProteinView/__fixtures__/structureFixtures.ts +29 -0
  66. package/src/ProteinView/applyLociInteractivity.ts +12 -0
  67. package/src/ProteinView/chooseMappedEntity.test.ts +65 -0
  68. package/src/ProteinView/chooseMappedEntity.ts +97 -0
  69. package/src/ProteinView/components/FeatureBar.tsx +1 -0
  70. package/src/ProteinView/extractStructureSequences.ts +20 -3
  71. package/src/ProteinView/geneExplorerLinkage.test.ts +214 -0
  72. package/src/ProteinView/loadStructureData.ts +6 -5
  73. package/src/ProteinView/proteinToGenomeMapping.ts +12 -9
  74. package/src/ProteinView/structureLoader.test.ts +12 -9
  75. package/src/ProteinView/structureModel.ts +103 -38
  76. package/src/ProteinView/subscribeMolstarInteraction.ts +4 -0
  77. package/src/mappings.ts +18 -1
  78. package/src/version.ts +1 -1
@@ -1,7 +1,6 @@
1
1
  import { SimpleFeature } from '@jbrowse/core/util';
2
- import { getCodonRange } from 'g2p_mapper';
3
2
  import { action, computed, makeObservable, observable } from 'mobx';
4
- import { genomeToTranscriptSeqMapping } from '../mappings';
3
+ import { codonGenomeSpan, genomeToTranscriptSeqMapping } from '../mappings';
5
4
  class Protein1DViewRegistry {
6
5
  views = observable.map();
7
6
  constructor() {
@@ -53,13 +52,9 @@ class Protein1DViewRegistry {
53
52
  if (!mapping) {
54
53
  return undefined;
55
54
  }
56
- const { p2g, strand, refName } = mapping;
57
- const result = getCodonRange(p2g, proteinPos, strand);
58
- if (!result) {
59
- return undefined;
60
- }
61
- const [start, end] = result;
62
- return { refName, start, end };
55
+ const { p2gCodon, refName } = mapping;
56
+ const span = codonGenomeSpan(p2gCodon, proteinPos);
57
+ return span ? { refName, start: span[0], end: span[1] } : undefined;
63
58
  }
64
59
  }
65
60
  export const protein1DViewRegistry = new Protein1DViewRegistry();
@@ -0,0 +1,7 @@
1
+ export declare const HBA_ALPHA_4HHB_ENTITY0 = "VLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR";
2
+ export declare const HBB_BETA_4HHB_ENTITY1 = "VHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH";
3
+ export declare const HBA_TRANSCRIPT_P69905 = "MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR";
4
+ export declare const HBB_TRANSCRIPT_P68871 = "MVHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH";
5
+ export declare const DNA_1TUP_ENTITY0 = "TTTCCTAGACTTGCCCAATTA";
6
+ export declare const P53_1TUP_ENTITY2 = "SSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNT";
7
+ export declare const P53_TRANSCRIPT_P04637 = "MEEPQSDPSVEPPLSQETFSDLWKLLPENNVLSPLPSQAMDDLMLSPDDIEQWFTEDPGPDEAPRMPEAAPPVAPAPAAPTPAAPAPAPSWPLSSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNTSSSPQPKKKPLDGEYFTLQIRGRERFEMFRELNEALELKDAQAGKEPGGSRAHSSHLKSKKGQSTSRHKKLMFKTEGPDSD";
@@ -0,0 +1,16 @@
1
+ // Real entity sequences (RCSB entity_poly one-letter, canonical) and transcript
2
+ // sequences (UniProt canonical) for structures the entity-[0] assumption maps
3
+ // wrong. Frozen here so the tests are deterministic and offline.
4
+ //
5
+ // 4HHB: hemoglobin α/β tetramer — entity[0] is the α chain, so a β (HBB)
6
+ // transcript must NOT map to [0]. (PDB strips the initiator Met, hence the
7
+ // leading-M difference vs UniProt.)
8
+ // 1TUP: p53 core bound to DNA — entity[0] and [1] are DNA strands; the protein
9
+ // is entity[2].
10
+ export const HBA_ALPHA_4HHB_ENTITY0 = 'VLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR';
11
+ export const HBB_BETA_4HHB_ENTITY1 = 'VHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH';
12
+ export const HBA_TRANSCRIPT_P69905 = 'MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSHGSAQVKGHGKKVADALTNAVAHVDDMPNALSALSDLHAHKLRVDPVNFKLLSHCLLVTLAAHLPAEFTPAVHASLDKFLASVSTVLTSKYR';
13
+ export const HBB_TRANSCRIPT_P68871 = 'MVHLTPEEKSAVTALWGKVNVDEVGGEALGRLLVVYPWTQRFFESFGDLSTPDAVMGNPKVKAHGKKVLGAFSDGLAHLDNLKGTFATLSELHCDKLHVDPENFRLLGNVLVCVLAHHFGKEFTPPVQAAYQKVVAGVANALAHKYH';
14
+ export const DNA_1TUP_ENTITY0 = 'TTTCCTAGACTTGCCCAATTA';
15
+ export const P53_1TUP_ENTITY2 = 'SSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNT';
16
+ export const P53_TRANSCRIPT_P04637 = 'MEEPQSDPSVEPPLSQETFSDLWKLLPENNVLSPLPSQAMDDLMLSPDDIEQWFTEDPGPDEAPRMPEAAPPVAPAPAAPTPAAPAPAPSWPLSSSVPSQKTYQGSYGFRLGFLHSGTAKSVTCTYSPALNKMFCQLAKTCPVQLWVDSTPPPGTRVRAMAIYKQSQHMTEVVRRCPHHERCSDSDGLAPPQHLIRVEGNLRVEYLDDRNTFRHSVVVPYEPPEVGSDCTTIHYNYMCNSSCMGGMNRRPILTIITLEDSSGNLLGRNSFEVRVCACPGRDRRTEEENLRKKGEPHHELPPGSTKRALPNNTSSSPQPKKKPLDGEYFTLQIRGRERFEMFRELNEALELKDAQAGKEPGGSRAHSSHLKSKKGQSTSRHKKLMFKTEGPDSD';
@@ -21,9 +21,12 @@ export type ResidueSpec = {
21
21
  * channel, so callers describe the target state declaratively rather than
22
22
  * juggling clear/apply calls.
23
23
  */
24
- export declare function setMolstarLoci({ structure, plugin, channel, spec, }: {
24
+ export declare function setMolstarLoci({ structure, plugin, channel, spec, entityId, }: {
25
25
  structure: Structure;
26
26
  plugin: PluginContext;
27
27
  channel: 'highlight' | 'select';
28
28
  spec: ResidueSpec | undefined;
29
+ /** Confine the loci to this mmCIF entity so a residue number doesn't light up
30
+ * on unrelated chains (binding partners, the other half of a homodimer). */
31
+ entityId?: string;
29
32
  }): Promise<void>;
@@ -14,7 +14,7 @@ const isActive = (spec) => spec !== undefined &&
14
14
  * channel, so callers describe the target state declaratively rather than
15
15
  * juggling clear/apply calls.
16
16
  */
17
- export async function setMolstarLoci({ structure, plugin, channel, spec, }) {
17
+ export async function setMolstarLoci({ structure, plugin, channel, spec, entityId, }) {
18
18
  const { lociHighlights, lociSelects } = plugin.managers.interactivity;
19
19
  if (channel === 'highlight') {
20
20
  lociHighlights.clearHighlights();
@@ -25,6 +25,14 @@ export async function setMolstarLoci({ structure, plugin, channel, spec, }) {
25
25
  if (isActive(spec)) {
26
26
  const { StructureSelection, Script } = await loadMolstar();
27
27
  const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
28
+ ...(entityId
29
+ ? {
30
+ 'chain-test': Q.core.rel.eq([
31
+ Q.struct.atomProperty.macromolecular.label_entity_id(),
32
+ entityId,
33
+ ]),
34
+ }
35
+ : {}),
28
36
  'residue-test': specToTest(spec)(Q),
29
37
  'group-by': Q.struct.atomProperty.macromolecular.residueKey(),
30
38
  }), structure);
@@ -0,0 +1,32 @@
1
+ import type { PairwiseAlignment } from '../mappings';
2
+ import type { AlignmentAlgorithm } from './types';
3
+ /**
4
+ * Whether a structure interaction (hover/click) should drive genome navigation.
5
+ * Only the transcript's mapped entity may — a hover on any other chain carries
6
+ * that chain's own label_seq_id and would mis-map through the wrong alignment.
7
+ * A structure with no resolved mapping (`mappedEntityId` undefined, e.g. a
8
+ * standalone viewer with no transcript) stays fully interactive.
9
+ */
10
+ export declare function interactionMatchesMappedEntity(entityId: string, mappedEntityId: string | undefined): boolean;
11
+ export interface EntitySelection {
12
+ /** index into the entity-sequence array that best matches the transcript */
13
+ index: number;
14
+ /** alignment of the transcript against the chosen entity (stop codons
15
+ * stripped on both sides, matching the rest of the mapping pipeline) */
16
+ alignment: PairwiseAlignment;
17
+ /** identical aligned residues, the score used to pick the entity */
18
+ matches: number;
19
+ }
20
+ /**
21
+ * Pick which polymer entity of a structure corresponds to the transcript.
22
+ *
23
+ * The plugin historically hardcoded entity `[0]`, which silently mis-maps every
24
+ * heteromeric / protein-DNA / processed-peptide structure where the protein of
25
+ * interest is some other chain. Selecting by alignment makes the structure self-
26
+ * describe which entity is the gene's protein: an exact sequence match wins
27
+ * outright, otherwise the entity with the most identical aligned residues.
28
+ *
29
+ * Returns `undefined` only when there is nothing to map (no transcript or no
30
+ * entities) — never a silent fallback to the wrong entity.
31
+ */
32
+ export declare function chooseMappedEntity(transcript: string, entitySeqs: string[], algorithm: AlignmentAlgorithm): EntitySelection | undefined;
@@ -0,0 +1,71 @@
1
+ import { runLocalAlignment } from './pairwiseAlignment';
2
+ import { stripStopCodon } from '../LaunchProteinView/utils/util';
3
+ /**
4
+ * Whether a structure interaction (hover/click) should drive genome navigation.
5
+ * Only the transcript's mapped entity may — a hover on any other chain carries
6
+ * that chain's own label_seq_id and would mis-map through the wrong alignment.
7
+ * A structure with no resolved mapping (`mappedEntityId` undefined, e.g. a
8
+ * standalone viewer with no transcript) stays fully interactive.
9
+ */
10
+ export function interactionMatchesMappedEntity(entityId, mappedEntityId) {
11
+ return mappedEntityId === undefined || entityId === mappedEntityId;
12
+ }
13
+ function countMatches(pa) {
14
+ const a = pa.alns[0].seq;
15
+ const b = pa.alns[1].seq;
16
+ let matches = 0;
17
+ for (let i = 0; i < a.length; i++) {
18
+ const ca = a[i];
19
+ const cb = b[i];
20
+ if (ca !== '-' && cb !== '-' && ca?.toUpperCase() === cb?.toUpperCase()) {
21
+ matches++;
22
+ }
23
+ }
24
+ return matches;
25
+ }
26
+ /**
27
+ * Pick which polymer entity of a structure corresponds to the transcript.
28
+ *
29
+ * The plugin historically hardcoded entity `[0]`, which silently mis-maps every
30
+ * heteromeric / protein-DNA / processed-peptide structure where the protein of
31
+ * interest is some other chain. Selecting by alignment makes the structure self-
32
+ * describe which entity is the gene's protein: an exact sequence match wins
33
+ * outright, otherwise the entity with the most identical aligned residues.
34
+ *
35
+ * Returns `undefined` only when there is nothing to map (no transcript or no
36
+ * entities) — never a silent fallback to the wrong entity.
37
+ */
38
+ export function chooseMappedEntity(transcript, entitySeqs, algorithm) {
39
+ const t = stripStopCodon(transcript);
40
+ if (!t || entitySeqs.length === 0) {
41
+ return undefined;
42
+ }
43
+ const stripped = entitySeqs.map(stripStopCodon);
44
+ const exactIndex = stripped.findIndex(s => s.length > 0 && s === t);
45
+ if (exactIndex !== -1) {
46
+ return {
47
+ index: exactIndex,
48
+ alignment: {
49
+ consensus: '|'.repeat(t.length),
50
+ alns: [
51
+ { id: 'seq1', seq: t },
52
+ { id: 'seq2', seq: stripped[exactIndex] },
53
+ ],
54
+ },
55
+ matches: t.length,
56
+ };
57
+ }
58
+ let best;
59
+ for (let index = 0; index < stripped.length; index++) {
60
+ const s = stripped[index];
61
+ if (s.length === 0) {
62
+ continue;
63
+ }
64
+ const alignment = runLocalAlignment(t, s, algorithm);
65
+ const matches = countMatches(alignment);
66
+ if (!best || matches > best.matches) {
67
+ best = { index, alignment, matches };
68
+ }
69
+ }
70
+ return best;
71
+ }
@@ -56,6 +56,7 @@ const FeatureBar = observer(function FeatureBar({ feature, model, }) {
56
56
  structure,
57
57
  plugin: molstarPluginContext,
58
58
  channel: 'select',
59
+ entityId: model.mappedEntityId,
59
60
  spec: newSelected
60
61
  ? { kind: 'range', start: feature.start - 1, end: feature.end }
61
62
  : undefined,
@@ -1,8 +1,17 @@
1
+ /** A polymer entity of a loaded structure: its mmCIF entity id and its full
2
+ * (label_seq_id-indexed) one-letter sequence. The entity id is what lets every
3
+ * downstream step talk about "the gene's protein" by identity instead of by the
4
+ * fragile entity-[0] position. */
5
+ export interface Entity {
6
+ entityId: string;
7
+ seq: string;
8
+ }
1
9
  interface StructureModel {
2
10
  obj?: {
3
11
  data: {
4
12
  sequence: {
5
13
  sequences: readonly {
14
+ entityId: string;
6
15
  sequence: {
7
16
  label: {
8
17
  toArray(): ArrayLike<string>;
@@ -13,5 +22,8 @@ interface StructureModel {
13
22
  };
14
23
  };
15
24
  }
25
+ export declare function extractEntities(model: StructureModel): Entity[] | undefined;
26
+ /** Back-compat helper for callers that only need the sequence strings (e.g. the
27
+ * launch dialog's isoform matching). */
16
28
  export declare function extractStructureSequences(model: StructureModel): string[] | undefined;
17
29
  export {};
@@ -1,3 +1,11 @@
1
+ export function extractEntities(model) {
2
+ return model.obj?.data.sequence.sequences.map(s => ({
3
+ entityId: s.entityId,
4
+ seq: Array.from(s.sequence.label.toArray()).join(''),
5
+ }));
6
+ }
7
+ /** Back-compat helper for callers that only need the sequence strings (e.g. the
8
+ * launch dialog's isoform matching). */
1
9
  export function extractStructureSequences(model) {
2
- return model.obj?.data.sequence.sequences.map(s => Array.from(s.sequence.label.toArray()).join(''));
10
+ return extractEntities(model)?.map(e => e.seq);
3
11
  }
@@ -1,6 +1,7 @@
1
+ import type { Entity } from './extractStructureSequences';
1
2
  import type { PluginContext } from 'molstar/lib/mol-plugin/context';
2
3
  export interface StructureData {
3
- sequences?: string[];
4
+ entities?: Entity[];
4
5
  confidence?: number[];
5
6
  }
6
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { addStructureFromData } from './addStructureFromData';
2
2
  import { addStructureFromURL } from './addStructureFromURL';
3
3
  import { extractPerResidueConfidence } from './extractPerResidueConfidence';
4
- import { extractStructureSequences } from './extractStructureSequences';
4
+ import { extractEntities } from './extractStructureSequences';
5
5
  /**
6
6
  * Loads a structure (from inline data or a URL) into the given Molstar plugin
7
7
  * and pulls out its per-chain sequences and per-residue confidence. Pure with
@@ -14,9 +14,9 @@ export async function loadStructureData({ structure, plugin, }) {
14
14
  : structure.url
15
15
  ? await addStructureFromURL({ url: structure.url, plugin })
16
16
  : { model: undefined };
17
- const sequences = model ? extractStructureSequences(model) : undefined;
17
+ const entities = model ? extractEntities(model) : undefined;
18
18
  const confidence = model
19
- ? extractPerResidueConfidence(model, sequences?.[0]?.length)
19
+ ? extractPerResidueConfidence(model, entities?.[0]?.seq.length)
20
20
  : undefined;
21
- return { sequences, confidence };
21
+ return { entities, confidence };
22
22
  }
@@ -47,10 +47,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
47
47
  hoverPosition: {
48
48
  structureSeqPos?: number;
49
49
  code?: string;
50
- chain?: string;
50
+ chain? /**
51
+ * #action
52
+ */: string;
51
53
  source: "structure" | "genome";
52
54
  } | undefined;
53
- structureSequences: string[] | undefined;
55
+ entities: import("./extractStructureSequences").Entity[] | undefined;
56
+ mappedEntityIndex: number;
54
57
  structureConfidence: number[] | undefined;
55
58
  isMouseInAlignment: boolean;
56
59
  loadedToMolstar: boolean;
@@ -62,7 +65,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
62
65
  hiddenFeatureTypes: Set<string>;
63
66
  } & {
64
67
  setStructureData(data: {
65
- sequences?: string[];
68
+ entities?: import("./extractStructureSequences").Entity[];
66
69
  confidence?: number[];
67
70
  }): void;
68
71
  hideFeatureType(type: string): void;
@@ -581,8 +584,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
581
584
  }): void;
582
585
  setGenomeHoveredPosition(structureSeqPos?: number): void;
583
586
  setAlignment(r?: import("../mappings").PairwiseAlignment): void;
587
+ setMappedEntityIndex(n: number): void;
584
588
  setIsMouseInAlignment(val: boolean): void;
585
589
  } & {
590
+ readonly structureSequences: string[] | undefined;
591
+ readonly mappedEntity: import("./extractStructureSequences").Entity | undefined;
592
+ readonly mappedStructureSeq: string | undefined;
593
+ readonly mappedEntityId: string | undefined;
586
594
  readonly uniprotId: string | undefined;
587
595
  readonly coordinateMapper: import("./coordinates").CoordinateMapper | undefined;
588
596
  readonly structureSeqToTranscriptSeqPosition: Record<number, number> | undefined;
@@ -795,10 +803,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
795
803
  hoverPosition: {
796
804
  structureSeqPos?: number;
797
805
  code?: string;
798
- chain?: string;
806
+ chain? /**
807
+ * #action
808
+ */: string;
799
809
  source: "structure" | "genome";
800
810
  } | undefined;
801
- structureSequences: string[] | undefined;
811
+ entities: import("./extractStructureSequences").Entity[] | undefined;
812
+ mappedEntityIndex: number;
802
813
  structureConfidence: number[] | undefined;
803
814
  isMouseInAlignment: boolean;
804
815
  loadedToMolstar: boolean;
@@ -810,7 +821,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
810
821
  hiddenFeatureTypes: Set<string>;
811
822
  } & {
812
823
  setStructureData(data: {
813
- sequences?: string[];
824
+ entities?: import("./extractStructureSequences").Entity[];
814
825
  confidence?: number[];
815
826
  }): void;
816
827
  hideFeatureType(type: string): void;
@@ -1329,8 +1340,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
1329
1340
  }): void;
1330
1341
  setGenomeHoveredPosition(structureSeqPos?: number): void;
1331
1342
  setAlignment(r?: import("../mappings").PairwiseAlignment): void;
1343
+ setMappedEntityIndex(n: number): void;
1332
1344
  setIsMouseInAlignment(val: boolean): void;
1333
1345
  } & {
1346
+ readonly structureSequences: string[] | undefined;
1347
+ readonly mappedEntity: import("./extractStructureSequences").Entity | undefined;
1348
+ readonly mappedStructureSeq: string | undefined;
1349
+ readonly mappedEntityId: string | undefined;
1334
1350
  readonly uniprotId: string | undefined;
1335
1351
  readonly coordinateMapper: import("./coordinates").CoordinateMapper | undefined;
1336
1352
  readonly structureSeqToTranscriptSeqPosition: Record<number, number> | undefined;
@@ -1422,10 +1438,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
1422
1438
  hoverPosition: {
1423
1439
  structureSeqPos?: number;
1424
1440
  code?: string;
1425
- chain?: string;
1441
+ chain? /**
1442
+ * #action
1443
+ */: string;
1426
1444
  source: "structure" | "genome";
1427
1445
  } | undefined;
1428
- structureSequences: string[] | undefined;
1446
+ entities: import("./extractStructureSequences").Entity[] | undefined;
1447
+ mappedEntityIndex: number;
1429
1448
  structureConfidence: number[] | undefined;
1430
1449
  isMouseInAlignment: boolean;
1431
1450
  loadedToMolstar: boolean;
@@ -1437,7 +1456,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
1437
1456
  hiddenFeatureTypes: Set<string>;
1438
1457
  } & {
1439
1458
  setStructureData(data: {
1440
- sequences?: string[];
1459
+ entities?: import("./extractStructureSequences").Entity[];
1441
1460
  confidence?: number[];
1442
1461
  }): void;
1443
1462
  hideFeatureType(type: string): void;
@@ -1956,8 +1975,13 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
1956
1975
  }): void;
1957
1976
  setGenomeHoveredPosition(structureSeqPos?: number): void;
1958
1977
  setAlignment(r?: import("../mappings").PairwiseAlignment): void;
1978
+ setMappedEntityIndex(n: number): void;
1959
1979
  setIsMouseInAlignment(val: boolean): void;
1960
1980
  } & {
1981
+ readonly structureSequences: string[] | undefined;
1982
+ readonly mappedEntity: import("./extractStructureSequences").Entity | undefined;
1983
+ readonly mappedStructureSeq: string | undefined;
1984
+ readonly mappedEntityId: string | undefined;
1961
1985
  readonly uniprotId: string | undefined;
1962
1986
  readonly coordinateMapper: import("./coordinates").CoordinateMapper | undefined;
1963
1987
  readonly structureSeqToTranscriptSeqPosition: Record<number, number> | undefined;
@@ -2,7 +2,7 @@ import type { PairwiseAlignment } from '../mappings';
2
2
  import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
3
3
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
4
4
  interface GenomeToTranscriptSeqMapping {
5
- p2g: Record<number, number>;
5
+ p2gCodon: Record<number, number[]>;
6
6
  strand: number;
7
7
  refName: string;
8
8
  }
@@ -1,5 +1,5 @@
1
1
  import { getSession } from '@jbrowse/core/util';
2
- import { getCodonRange } from 'g2p_mapper';
2
+ import { codonGenomeSpan } from '../mappings';
3
3
  /**
4
4
  * Maps a protein structure position to genome coordinates
5
5
  * @returns [start, end] tuple of genome coordinates, or undefined if mapping fails
@@ -9,12 +9,11 @@ export function proteinToGenomeMapping({ model, structureSeqPos, }) {
9
9
  if (!genomeToTranscriptSeqMapping || !pairwiseAlignment) {
10
10
  return undefined;
11
11
  }
12
- const { p2g, strand } = genomeToTranscriptSeqMapping;
12
+ const { p2gCodon } = genomeToTranscriptSeqMapping;
13
13
  const transcriptPos = structureSeqToTranscriptSeqPosition?.[structureSeqPos];
14
- if (transcriptPos === undefined) {
15
- return undefined;
16
- }
17
- return getCodonRange(p2g, transcriptPos, strand);
14
+ return transcriptPos === undefined
15
+ ? undefined
16
+ : codonGenomeSpan(p2gCodon, transcriptPos);
18
17
  }
19
18
  /**
20
19
  * Maps a protein structure range to genome coordinates
@@ -63,7 +62,11 @@ export async function navigateToProteinPosition({ model, structureSeqPos, struct
63
62
  }
64
63
  const [start, end] = result;
65
64
  if (zoomToBaseLevel) {
66
- await connectedView.navToLocString(`${refName}:${start}-${end}${strand === -1 ? '[rev]' : ''}`, undefined, 0.2);
65
+ // start/end are 0-based half-open (from getCodonRanges). navToLocString
66
+ // parses a 1-based locString (parseLocString does start -= 1), so the start
67
+ // must be shifted to 1-based; the half-open end already equals the 1-based
68
+ // inclusive end. Passing the raw 0-based start landed the view 1bp 5'.
69
+ await connectedView.navToLocString(`${refName}:${start + 1}-${end}${strand === -1 ? '[rev]' : ''}`, undefined, 0.2);
67
70
  }
68
71
  else {
69
72
  const { assemblyManager } = session;
@@ -1,5 +1,6 @@
1
1
  import { type Instance } from '@jbrowse/mobx-state-tree';
2
2
  import { type CoordinateMapper } from './coordinates';
3
+ import type { Entity } from './extractStructureSequences';
3
4
  import type { PairwiseAlignment } from '../mappings';
4
5
  import type { AlignmentAlgorithm } from './types';
5
6
  import type { SimpleFeatureSerialized } from '@jbrowse/core/util';
@@ -91,7 +92,15 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
91
92
  /**
92
93
  * #volatile
93
94
  */
94
- structureSequences: string[] | undefined;
95
+ entities: Entity[] | undefined;
96
+ /**
97
+ * #volatile
98
+ * Index into entities of the one that matches the transcript. Resolved by
99
+ * alignment (see chooseMappedEntity) instead of assuming the protein of
100
+ * interest is entity [0], which mis-maps heteromers / protein-DNA complexes /
101
+ * processed peptides.
102
+ */
103
+ mappedEntityIndex: number;
95
104
  /**
96
105
  * #volatile
97
106
  * Per-residue B-factor / pLDDT for the first chain, indexed by 0-based
@@ -127,7 +136,7 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
127
136
  hiddenFeatureTypes: Set<string>;
128
137
  } & {
129
138
  setStructureData(data: {
130
- sequences?: string[];
139
+ entities?: Entity[];
131
140
  confidence?: number[];
132
141
  }): void;
133
142
  /**
@@ -189,11 +198,37 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
189
198
  * #action
190
199
  */
191
200
  setAlignment(r?: PairwiseAlignment): void;
201
+ /**
202
+ * #action
203
+ */
204
+ setMappedEntityIndex(n: number): void;
192
205
  /**
193
206
  * #action
194
207
  */
195
208
  setIsMouseInAlignment(val: boolean): void;
196
209
  } & {
210
+ /**
211
+ * #getter
212
+ * Sequence strings of every polymer entity (back-compat for the alignment
213
+ * autorun and presence checks).
214
+ */
215
+ readonly structureSequences: string[] | undefined;
216
+ /**
217
+ * #getter
218
+ * The entity that maps to the transcript (chosen by chooseMappedEntity), not
219
+ * blindly entity [0].
220
+ */
221
+ readonly mappedEntity: Entity | undefined;
222
+ /**
223
+ * #getter
224
+ */
225
+ readonly mappedStructureSeq: string | undefined;
226
+ /**
227
+ * #getter
228
+ * mmCIF entity id of the mapped entity. Used to reject hovers/clicks on
229
+ * other chains and to confine highlights to the gene's protein.
230
+ */
231
+ readonly mappedEntityId: string | undefined;
197
232
  /**
198
233
  * #getter
199
234
  * Extracts UniProt ID from AlphaFold URL if available
@@ -227,6 +262,9 @@ declare const Structure: import("@jbrowse/mobx-state-tree").IModelType<{
227
262
  * #getter
228
263
  * Per-residue pLDDT values mapped to alignment columns, shown only when the
229
264
  * structure's B-factor column actually looks like AlphaFold confidence.
265
+ * pLDDT is an AlphaFold concept — those models are single-entity, so the
266
+ * mapped entity is always [0] and structureConfidence (extracted from [0])
267
+ * stays coherent.
230
268
  */
231
269
  readonly confidenceCells: {
232
270
  col: number;