jbrowse-plugin-protein3d 0.6.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 (75) hide show
  1. package/dist/AddHighlightModel/GenomeTo1DProteinHoverHighlight.js +5 -2
  2. package/dist/AddHighlightModel/ProteinToMsaHoverSync.js +4 -0
  3. package/dist/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.d.ts +17 -17
  4. package/dist/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.js +18 -37
  5. package/dist/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.d.ts +16 -21
  6. package/dist/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.js +21 -60
  7. package/dist/BaseProteinAnnotationAdapter.d.ts +29 -0
  8. package/dist/BaseProteinAnnotationAdapter.js +41 -0
  9. package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +10 -2
  10. package/dist/LaunchProteinView/components/FoldseekSearch.js +11 -0
  11. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.d.ts +3 -0
  12. package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.js +25 -1
  13. package/dist/LaunchProteinView/hooks/useUniProtSearch.d.ts +2 -1
  14. package/dist/LaunchProteinView/hooks/useUniProtSearch.js +4 -1
  15. package/dist/LaunchProteinView/services/lookupMethods.js +1 -1
  16. package/dist/LaunchProteinView/utils/launchViewUtils.d.ts +2 -9
  17. package/dist/LaunchProteinView/utils/launchViewUtils.js +9 -51
  18. package/dist/LaunchProteinView/utils/structureUrls.d.ts +8 -0
  19. package/dist/LaunchProteinView/utils/structureUrls.js +48 -0
  20. package/dist/LaunchProteinView/utils/util.d.ts +8 -0
  21. package/dist/LaunchProteinView/utils/util.js +18 -0
  22. package/dist/LaunchProteinViewExtensionPoint/index.js +4 -5
  23. package/dist/Protein1DViewRegistry/index.d.ts +6 -1
  24. package/dist/Protein1DViewRegistry/index.js +16 -23
  25. package/dist/ProteinView/components/AddStructureDialog.js +1 -1
  26. package/dist/ProteinView/components/FeatureBar.js +14 -24
  27. package/dist/ProteinView/hooks/useProteinFeatureTrackData.d.ts +12 -0
  28. package/dist/ProteinView/hooks/useProteinFeatureTrackData.js +8 -0
  29. package/dist/ProteinView/model.d.ts +12 -38
  30. package/dist/ProteinView/model.js +31 -82
  31. package/dist/ProteinView/proteinViewSpec.d.ts +76 -0
  32. package/dist/ProteinView/proteinViewSpec.js +16 -0
  33. package/dist/ProteinView/structureModel.d.ts +3 -1
  34. package/dist/ProteinView/structureModel.js +47 -10
  35. package/dist/ProteinView/structureSuperposer.d.ts +30 -0
  36. package/dist/ProteinView/structureSuperposer.js +53 -0
  37. package/dist/UniProtVariationAdapter/UniProtVariationAdapter.d.ts +77 -13
  38. package/dist/UniProtVariationAdapter/UniProtVariationAdapter.js +28 -48
  39. package/dist/jbrowse-plugin-protein3d.umd.production.min.js +15 -15
  40. package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
  41. package/dist/version.d.ts +1 -1
  42. package/dist/version.js +1 -1
  43. package/package.json +1 -1
  44. package/src/AddHighlightModel/GenomeTo1DProteinHoverHighlight.tsx +6 -2
  45. package/src/AddHighlightModel/ProteinToMsaHoverSync.tsx +3 -0
  46. package/src/AddHighlightModel/msaHoverSyncGuard.test.ts +14 -14
  47. package/src/AlphaFoldConfidenceAdapter/AlphaFoldConfidenceAdapter.ts +32 -50
  48. package/src/AlphaFoldConfidenceAdapter/parseAlphaFoldConfidence.test.ts +26 -0
  49. package/src/AlphaMissensePathogenicityAdapter/AlphaMissensePathogenicityAdapter.ts +30 -74
  50. package/src/AlphaMissensePathogenicityAdapter/parseAlphaMissense.test.ts +9 -4
  51. package/src/BaseProteinAnnotationAdapter.ts +70 -0
  52. package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +30 -7
  53. package/src/LaunchProteinView/components/FoldseekSearch.tsx +12 -0
  54. package/src/LaunchProteinView/hooks/useAlphaFoldDBSearch.ts +33 -1
  55. package/src/LaunchProteinView/hooks/useUniProtSearch.ts +7 -0
  56. package/src/LaunchProteinView/services/lookupMethods.ts +1 -1
  57. package/src/LaunchProteinView/utils/extractTaxonId.test.ts +28 -0
  58. package/src/LaunchProteinView/utils/launchViewUtils.ts +29 -70
  59. package/src/LaunchProteinView/utils/structureUrls.ts +66 -0
  60. package/src/LaunchProteinView/utils/util.ts +20 -0
  61. package/src/LaunchProteinViewExtensionPoint/index.ts +28 -23
  62. package/src/Protein1DViewRegistry/index.ts +20 -35
  63. package/src/ProteinView/components/AddStructureDialog.tsx +1 -1
  64. package/src/ProteinView/components/FeatureBar.tsx +14 -26
  65. package/src/ProteinView/hooks/useProteinFeatureTrackData.ts +12 -0
  66. package/src/ProteinView/model.ts +48 -117
  67. package/src/ProteinView/proteinViewSpec.test.ts +48 -0
  68. package/src/ProteinView/proteinViewSpec.ts +65 -0
  69. package/src/ProteinView/structureModel.test.ts +48 -0
  70. package/src/ProteinView/structureModel.ts +56 -17
  71. package/src/ProteinView/structureSuperposer.test.ts +108 -0
  72. package/src/ProteinView/structureSuperposer.ts +70 -0
  73. package/src/UniProtVariationAdapter/UniProtVariationAdapter.ts +33 -62
  74. package/src/UniProtVariationAdapter/parseUniProtVariants.test.ts +42 -0
  75. package/src/version.ts +1 -1
@@ -20,11 +20,14 @@ const GenomeTo1DProteinHoverHighlight = observer(function GenomeTo1DProteinHover
20
20
  if (!checkHovered(hovered)) {
21
21
  return null;
22
22
  }
23
- const { coord } = hovered.hoverPosition;
23
+ const { coord, refName } = hovered.hoverPosition;
24
24
  const feature = new SimpleFeature(protein1DInfo.feature);
25
25
  const mapping = genomeToTranscriptSeqMapping(feature);
26
26
  const { g2p } = mapping;
27
- const proteinPos = g2p[coord - 1];
27
+ // g2p is keyed by genomic position on the transcript's own refName; without
28
+ // this gate the same numeric coord on an unrelated chromosome would match a
29
+ // key and light a protein residue for a different locus.
30
+ const proteinPos = refName === mapping.refName ? g2p[coord - 1] : undefined;
28
31
  if (proteinPos === undefined) {
29
32
  return null;
30
33
  }
@@ -43,6 +43,10 @@ const ProteinToMsaHoverSync = observer(function ProteinToMsaHoverSync({ model, }
43
43
  : seqPos;
44
44
  setMousePos(col);
45
45
  }
46
+ else {
47
+ // structure went away (e.g. removed); clear the stale MSA hover
48
+ setMousePos(undefined);
49
+ }
46
50
  }));
47
51
  }
48
52
  disposers.push(autorun(() => {
@@ -1,18 +1,18 @@
1
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
- import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
3
- import type { Feature, Region } from '@jbrowse/core/util';
4
- import type { Observable } from 'rxjs';
5
- export default class AlphaFoldConfidenceAdapter extends BaseFeatureDataAdapter {
6
- static capabilities: string[];
7
- feats: Promise<{
8
- uniqueId: string;
9
- start: number;
10
- end: number;
11
- score: number;
12
- }[]> | undefined;
13
- private loadDataP;
14
- private loadData;
15
- getRefNames(_opts?: BaseOptions): Promise<never[]>;
16
- getFeatures(query: Region, _opts?: BaseOptions): Observable<Feature>;
17
- freeResources(): void;
1
+ import { BaseProteinAnnotationAdapter, type ProteinAnnotationRow } from '../BaseProteinAnnotationAdapter';
2
+ export interface AlphaFoldConfidenceRow extends ProteinAnnotationRow {
3
+ score: number;
18
4
  }
5
+ interface AlphaFoldConfidenceJson {
6
+ residueNumber: number[];
7
+ confidenceScore: number[];
8
+ }
9
+ /**
10
+ * Converts AlphaFold confidence JSON to features. residueNumber is 1-based, so
11
+ * residue n becomes the 0-based half-open interval [n-1, n) to line up with the
12
+ * interbase protein reference sequence.
13
+ */
14
+ export declare function parseAlphaFoldConfidence(json: AlphaFoldConfidenceJson): AlphaFoldConfidenceRow[];
15
+ export default class AlphaFoldConfidenceAdapter extends BaseProteinAnnotationAdapter<AlphaFoldConfidenceRow> {
16
+ protected loadFeatures(): Promise<AlphaFoldConfidenceRow[]>;
17
+ }
18
+ export {};
@@ -1,40 +1,21 @@
1
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
- import { SimpleFeature, doesIntersect2 } from '@jbrowse/core/util';
3
1
  import { openLocation } from '@jbrowse/core/util/io';
4
- import { ObservableCreate } from '@jbrowse/core/util/rxjs';
5
- export default class AlphaFoldConfidenceAdapter extends BaseFeatureDataAdapter {
6
- static capabilities = ['getFeatures', 'getRefNames'];
7
- feats;
8
- async loadDataP() {
9
- const scores = JSON.parse(await openLocation(this.getConf('location')).readFile('utf8'));
10
- return scores.residueNumber.map((value, idx) => ({
11
- uniqueId: `feat-${idx}`,
12
- start: value,
13
- end: value + 1,
14
- score: scores.confidenceScore[idx],
15
- }));
16
- }
17
- async loadData(_opts = {}) {
18
- this.feats ??= this.loadDataP().catch((e) => {
19
- this.feats = undefined;
20
- throw e;
21
- });
22
- return this.feats;
23
- }
24
- async getRefNames(_opts = {}) {
25
- return [];
26
- }
27
- getFeatures(query, _opts = {}) {
28
- return ObservableCreate(async (observer) => {
29
- const { start, end, refName } = query;
30
- const data = await this.loadData();
31
- for (const f of data) {
32
- if (doesIntersect2(f.start, f.end, start, end)) {
33
- observer.next(new SimpleFeature({ ...f, refName }));
34
- }
35
- }
36
- observer.complete();
37
- });
2
+ import { BaseProteinAnnotationAdapter, } from '../BaseProteinAnnotationAdapter';
3
+ /**
4
+ * Converts AlphaFold confidence JSON to features. residueNumber is 1-based, so
5
+ * residue n becomes the 0-based half-open interval [n-1, n) to line up with the
6
+ * interbase protein reference sequence.
7
+ */
8
+ export function parseAlphaFoldConfidence(json) {
9
+ return json.residueNumber.map((residue, idx) => ({
10
+ uniqueId: `feat-${idx}`,
11
+ start: residue - 1,
12
+ end: residue,
13
+ score: json.confidenceScore[idx],
14
+ }));
15
+ }
16
+ export default class AlphaFoldConfidenceAdapter extends BaseProteinAnnotationAdapter {
17
+ async loadFeatures() {
18
+ const json = JSON.parse(await openLocation(this.getConf('location')).readFile('utf8'));
19
+ return parseAlphaFoldConfidence(json);
38
20
  }
39
- freeResources() { }
40
21
  }
@@ -1,28 +1,26 @@
1
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
1
+ import { BaseProteinAnnotationAdapter, type ProteinAnnotationRow } from '../BaseProteinAnnotationAdapter';
2
2
  import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
3
- import type { Feature, Region } from '@jbrowse/core/util';
4
- import type { Observable } from 'rxjs';
5
- export interface AlphaMissenseRow {
6
- uniqueId: string;
7
- start: number;
8
- end: number;
3
+ import type { Region } from '@jbrowse/core/util';
4
+ export interface AlphaMissenseRow extends ProteinAnnotationRow {
9
5
  score: number;
10
6
  ref: string;
11
7
  variant: string;
12
8
  am_class: string;
13
9
  }
14
- /**
15
- * Parses AlphaMissense CSV text (protein_variant,score,am_class). The
16
- * protein_variant column looks like "V123L": a ref AA, a 1-based residue
17
- * coordinate, and a variant AA. Rows that don't parse to a numeric coordinate
18
- * are skipped rather than emitted as bogus position-0 features.
19
- */
20
10
  export declare function parseAlphaMissense(text: string): AlphaMissenseRow[];
21
- export default class AlphaMissensePathogenicityAdapter extends BaseFeatureDataAdapter {
22
- static capabilities: string[];
23
- feats: Promise<AlphaMissenseRow[]> | undefined;
24
- private loadDataP;
25
- private loadData;
11
+ export default class AlphaMissensePathogenicityAdapter extends BaseProteinAnnotationAdapter<AlphaMissenseRow> {
12
+ protected loadFeatures(): Promise<AlphaMissenseRow[]>;
13
+ protected featureData(row: AlphaMissenseRow, refName: string): {
14
+ refName: string;
15
+ source: string;
16
+ score: number;
17
+ ref: string;
18
+ variant: string;
19
+ am_class: string;
20
+ uniqueId: string;
21
+ start: number;
22
+ end: number;
23
+ };
26
24
  getGlobalStats(_opts?: BaseOptions): Promise<{
27
25
  scoreMin: number;
28
26
  scoreMax: number;
@@ -30,11 +28,8 @@ export default class AlphaMissensePathogenicityAdapter extends BaseFeatureDataAd
30
28
  getMultiRegionFeatureDensityStats(_regions: Region[]): Promise<{
31
29
  featureDensity: number;
32
30
  }>;
33
- getRefNames(_opts?: BaseOptions): Promise<never[]>;
34
- getFeatures(query: Region, _opts?: BaseOptions): Observable<Feature>;
35
31
  getSources(): Promise<{
36
32
  name: string;
37
33
  __name: string;
38
34
  }[]>;
39
- freeResources(): void;
40
35
  }
@@ -1,13 +1,15 @@
1
- import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
- import { SimpleFeature, doesIntersect2, max, min } from '@jbrowse/core/util';
1
+ import { max, min } from '@jbrowse/core/util';
3
2
  import { openLocation } from '@jbrowse/core/util/io';
4
- import { ObservableCreate } from '@jbrowse/core/util/rxjs';
3
+ import { BaseProteinAnnotationAdapter, } from '../BaseProteinAnnotationAdapter';
5
4
  /**
6
5
  * Parses AlphaMissense CSV text (protein_variant,score,am_class). The
7
6
  * protein_variant column looks like "V123L": a ref AA, a 1-based residue
8
- * coordinate, and a variant AA. Rows that don't parse to a numeric coordinate
9
- * are skipped rather than emitted as bogus position-0 features.
7
+ * coordinate, and a variant AA, converted here to a 0-based half-open interval
8
+ * to match the interbase protein reference sequence. Rows that don't match the
9
+ * ref/coord/variant shape are skipped rather than emitted as bogus features (a
10
+ * short string like "VL" would otherwise parse to a position-0 feature).
10
11
  */
12
+ const VARIANT_RE = /^([A-Za-z])(\d+)([A-Za-z])$/;
11
13
  export function parseAlphaMissense(text) {
12
14
  return text
13
15
  .split('\n')
@@ -16,21 +18,15 @@ export function parseAlphaMissense(text) {
16
18
  .filter(f => !!f)
17
19
  .flatMap((row, idx) => {
18
20
  const [protein_variant = '', score, am_class] = row.split(',');
19
- const ref = protein_variant[0];
20
- const variant = protein_variant.at(-1);
21
- const coord = +protein_variant.slice(1, -1);
22
- return ref !== undefined &&
23
- variant !== undefined &&
24
- !Number.isNaN(coord) &&
25
- score !== undefined &&
26
- am_class !== undefined
21
+ const match = VARIANT_RE.exec(protein_variant);
22
+ return match && score !== undefined && am_class !== undefined
27
23
  ? [
28
24
  {
29
25
  uniqueId: `feat-${idx}`,
30
- ref,
31
- variant,
32
- start: coord,
33
- end: coord + 1,
26
+ ref: match[1],
27
+ variant: match[3],
28
+ start: +match[2] - 1,
29
+ end: +match[2],
34
30
  score: +score,
35
31
  am_class,
36
32
  },
@@ -38,58 +34,23 @@ export function parseAlphaMissense(text) {
38
34
  : [];
39
35
  });
40
36
  }
41
- export default class AlphaMissensePathogenicityAdapter extends BaseFeatureDataAdapter {
42
- static capabilities = ['getFeatures', 'getRefNames'];
43
- feats;
44
- async loadDataP() {
45
- const scores = await openLocation(this.getConf('location')).readFile('utf8');
46
- return parseAlphaMissense(scores);
37
+ export default class AlphaMissensePathogenicityAdapter extends BaseProteinAnnotationAdapter {
38
+ async loadFeatures() {
39
+ return parseAlphaMissense(await openLocation(this.getConf('location')).readFile('utf8'));
47
40
  }
48
- async loadData(_opts = {}) {
49
- this.feats ??= this.loadDataP().catch((e) => {
50
- this.feats = undefined;
51
- throw e;
52
- });
53
- return this.feats;
41
+ featureData(row, refName) {
42
+ return { ...row, refName, source: row.variant };
54
43
  }
55
44
  async getGlobalStats(_opts) {
56
- const data = await this.loadData();
57
- const scores = data.map(s => s.score);
45
+ const scores = (await this.loadData()).map(s => s.score);
58
46
  return { scoreMin: min(scores), scoreMax: max(scores) };
59
47
  }
60
48
  // always render bigwig instead of calculating a feature density for it
61
49
  async getMultiRegionFeatureDensityStats(_regions) {
62
50
  return { featureDensity: 0 };
63
51
  }
64
- async getRefNames(_opts = {}) {
65
- return [];
66
- }
67
- getFeatures(query, _opts = {}) {
68
- return ObservableCreate(async (observer) => {
69
- const { start, end, refName } = query;
70
- const data = await this.loadData();
71
- for (const f of data) {
72
- if (doesIntersect2(f.start, f.end, start, end)) {
73
- observer.next(new SimpleFeature({
74
- ...f,
75
- refName,
76
- source: f.variant,
77
- }));
78
- }
79
- }
80
- observer.complete();
81
- });
82
- }
83
52
  async getSources() {
84
- const sources = new Set();
85
- const data = await this.loadData();
86
- for (const f of data) {
87
- sources.add(f.variant);
88
- }
89
- return [...sources].map(s => ({
90
- name: s,
91
- __name: s,
92
- }));
53
+ const sources = new Set((await this.loadData()).map(f => f.variant));
54
+ return [...sources].map(s => ({ name: s, __name: s }));
93
55
  }
94
- freeResources() { }
95
56
  }
@@ -0,0 +1,29 @@
1
+ import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
3
+ import type { Feature, Region, SimpleFeatureSerialized } from '@jbrowse/core/util';
4
+ import type { Observable } from 'rxjs';
5
+ export interface ProteinAnnotationRow {
6
+ [key: string]: unknown;
7
+ uniqueId: string;
8
+ start: number;
9
+ end: number;
10
+ }
11
+ /**
12
+ * Shared plumbing for the protein-annotation adapters (AlphaFold confidence,
13
+ * AlphaMissense, UniProt variation). Each lives on the temporary protein
14
+ * assembly, exposes no ref names, caches its parsed rows once, and emits the
15
+ * rows that intersect a query region. Subclasses supply the parsing
16
+ * (loadFeatures) and may decorate the emitted feature (featureData).
17
+ */
18
+ export declare abstract class BaseProteinAnnotationAdapter<T extends ProteinAnnotationRow> extends BaseFeatureDataAdapter {
19
+ static capabilities: string[];
20
+ private feats;
21
+ /** Parse the configured source into rows. */
22
+ protected abstract loadFeatures(): Promise<T[]>;
23
+ /** Fields for the emitted feature; override to add extras (e.g. `source`). */
24
+ protected featureData(row: T, refName: string): SimpleFeatureSerialized;
25
+ protected loadData(): Promise<T[]>;
26
+ getRefNames(_opts?: BaseOptions): Promise<never[]>;
27
+ getFeatures(query: Region, _opts?: BaseOptions): Observable<Feature>;
28
+ freeResources(): void;
29
+ }
@@ -0,0 +1,41 @@
1
+ import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import { SimpleFeature, doesIntersect2 } from '@jbrowse/core/util';
3
+ import { ObservableCreate } from '@jbrowse/core/util/rxjs';
4
+ /**
5
+ * Shared plumbing for the protein-annotation adapters (AlphaFold confidence,
6
+ * AlphaMissense, UniProt variation). Each lives on the temporary protein
7
+ * assembly, exposes no ref names, caches its parsed rows once, and emits the
8
+ * rows that intersect a query region. Subclasses supply the parsing
9
+ * (loadFeatures) and may decorate the emitted feature (featureData).
10
+ */
11
+ export class BaseProteinAnnotationAdapter extends BaseFeatureDataAdapter {
12
+ static capabilities = ['getFeatures', 'getRefNames'];
13
+ feats;
14
+ /** Fields for the emitted feature; override to add extras (e.g. `source`). */
15
+ featureData(row, refName) {
16
+ return { ...row, refName };
17
+ }
18
+ // Parse once and cache; a failed parse clears the cache so it can retry.
19
+ loadData() {
20
+ this.feats ??= this.loadFeatures().catch((e) => {
21
+ this.feats = undefined;
22
+ throw e;
23
+ });
24
+ return this.feats;
25
+ }
26
+ async getRefNames(_opts = {}) {
27
+ return [];
28
+ }
29
+ getFeatures(query, _opts = {}) {
30
+ return ObservableCreate(async (observer) => {
31
+ const { start, end, refName } = query;
32
+ for (const f of await this.loadData()) {
33
+ if (doesIntersect2(f.start, f.end, start, end)) {
34
+ observer.next(new SimpleFeature(this.featureData(f, refName)));
35
+ }
36
+ }
37
+ observer.complete();
38
+ });
39
+ }
40
+ freeResources() { }
41
+ }
@@ -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" },
@@ -36,9 +36,19 @@ const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view
36
36
  ? stripStopCodon(selectedIsoformData.seq)
37
37
  : '';
38
38
  const sequence = userEditedSequence ?? cleanedSequence;
39
+ // Any change to the input sequence makes an existing 3Di prediction (and any
40
+ // results derived from it) stale. Clearing it returns the UI to the Predict
41
+ // step so a search can't silently run against the previously-predicted
42
+ // sequence after the user switches transcript or edits the residues.
43
+ const invalidatePrediction = () => {
44
+ if (di3Sequence !== undefined || results !== undefined) {
45
+ reset();
46
+ }
47
+ };
39
48
  const setUserSelectionWithReset = (id) => {
40
49
  setUserSelection(id);
41
50
  setUserEditedSequence(undefined);
51
+ invalidatePrediction();
42
52
  };
43
53
  const canPredict = sequence.trim().length > 0 && !isPredicting && !isLoading;
44
54
  const canSearch = !!cleanedAaSequence &&
@@ -55,6 +65,7 @@ const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view
55
65
  React.createElement(TranscriptSelector, { val: effectiveSelectedTranscriptId, setVal: setUserSelectionWithReset, isoforms: transcripts, isoformSequences: isoformSequences, feature: feature, disabled: isBusy }),
56
66
  React.createElement(TextField, { label: "Protein sequence (amino acids)", multiline: true, rows: 4, value: sequence, onChange: e => {
57
67
  setUserEditedSequence(e.target.value);
68
+ invalidatePrediction();
58
69
  }, placeholder: `MKTVRQERLKSIVRILERSKEPVSGAQLAEEL...`, disabled: isBusy, InputProps: {
59
70
  className: classes.sequenceInput,
60
71
  } }))) : null,
@@ -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;
@@ -24,11 +24,14 @@ export default function useUniProtSearch({ recognizedIds = [], geneId, geneName,
24
24
  selectedQueryId,
25
25
  idsToSearch.join(','),
26
26
  geneNameToSearch,
27
+ geneId,
28
+ organismId,
27
29
  ]
28
30
  : null, async () => searchUniProtEntries({
29
31
  recognizedIds: idsToSearch,
30
32
  geneId,
31
33
  geneName: geneNameToSearch,
34
+ organismId,
32
35
  }), {
33
36
  ...STATIC_SWR_OPTIONS,
34
37
  keepPreviousData: true,
@@ -53,7 +53,7 @@ export async function searchUniProtEntries({ recognizedIds = [], geneId, geneNam
53
53
  let geneNameError;
54
54
  if (!entries.some(e => e.isReviewed) && geneName) {
55
55
  try {
56
- const query = `gene:${geneName}+AND+organism_id:${organismId}+AND+reviewed:true`;
56
+ const query = `gene:${geneName} AND organism_id:${organismId} AND reviewed:true`;
57
57
  const geneNameResults = await searchUniProt(query, 5);
58
58
  entries = deduplicateEntries([...entries, ...geneNameResults]);
59
59
  }
@@ -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 {};