jbrowse-plugin-msaview 5.15.0 → 5.16.1
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.
- package/README.md +2 -0
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +19 -1
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js +55 -3
- package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +10 -0
- package/dist/BgzipFastaMsaAdapter/configSchema.js +5 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +11 -9
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.d.ts +5 -2
- package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +2 -2
- package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -6
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +4 -3
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +10 -11
- package/dist/LaunchMsaView/featureMatchesId.test.d.ts +1 -0
- package/dist/LaunchMsaView/featureMatchesId.test.js +74 -0
- package/dist/LaunchMsaView/util.d.ts +2 -0
- package/dist/LaunchMsaView/util.js +38 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +18 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.js +25 -3
- package/dist/MsaViewPanel/displayedRefName.d.ts +19 -0
- package/dist/MsaViewPanel/displayedRefName.js +16 -0
- package/dist/MsaViewPanel/fetchIndexedMsa.js +10 -23
- package/dist/MsaViewPanel/handleMsaClick.test.d.ts +1 -0
- package/dist/MsaViewPanel/handleMsaClick.test.js +59 -0
- package/dist/MsaViewPanel/loadProteinDomains.d.ts +5 -1
- package/dist/MsaViewPanel/loadProteinDomains.js +16 -4
- package/dist/MsaViewPanel/loadProteinDomains.test.js +36 -5
- package/dist/MsaViewPanel/model.d.ts +158 -6
- package/dist/MsaViewPanel/model.js +175 -19
- package/dist/MsaViewPanel/resolveConnectedTranscript.d.ts +32 -3
- package/dist/MsaViewPanel/resolveConnectedTranscript.js +70 -22
- package/dist/MsaViewPanel/resolveConnectedTranscript.test.js +162 -5
- package/dist/MsaViewPanel/transcriptMap.d.ts +17 -1
- package/dist/MsaViewPanel/transcriptMap.js +13 -13
- package/dist/MsaViewPanel/transcriptMap.test.js +20 -34
- package/dist/MsaViewPanel/util.d.ts +3 -0
- package/dist/MsaViewPanel/util.js +7 -0
- package/dist/MsaViewPanel/viewReport.d.ts +113 -0
- package/dist/MsaViewPanel/viewReport.js +124 -0
- package/dist/MsaViewPanel/viewState.test.d.ts +1 -0
- package/dist/MsaViewPanel/viewState.test.js +303 -0
- package/dist/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.d.ts +19 -0
- package/dist/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.js +61 -0
- package/dist/OrthoFinderMsaAdapter/configSchema.d.ts +12 -0
- package/dist/OrthoFinderMsaAdapter/configSchema.js +13 -0
- package/dist/OrthoFinderMsaAdapter/index.d.ts +2 -0
- package/dist/OrthoFinderMsaAdapter/index.js +14 -0
- package/dist/OrthoFinderMsaAdapter/parseResults.d.ts +16 -0
- package/dist/OrthoFinderMsaAdapter/parseResults.js +63 -0
- package/dist/OrthoFinderMsaAdapter/parseResults.test.d.ts +1 -0
- package/dist/OrthoFinderMsaAdapter/parseResults.test.js +29 -0
- package/dist/chunks/BgzipFastaMsaAdapter-Y3LYQRQL.js +6 -0
- package/dist/chunks/BgzipFastaMsaAdapter-Y3LYQRQL.js.map +7 -0
- package/dist/chunks/LaunchMsaViewDialog-H23R3EDJ.js +3 -0
- package/dist/chunks/LaunchMsaViewDialog-H23R3EDJ.js.map +7 -0
- package/dist/chunks/MsaViewPanel-6ETJVX27.js +2 -0
- package/dist/chunks/{MsaViewPanel-UWT4FWLE.js.map → MsaViewPanel-6ETJVX27.js.map} +1 -1
- package/dist/chunks/OrthoFinderMsaAdapter-I366HWAA.js +2 -0
- package/dist/chunks/OrthoFinderMsaAdapter-I366HWAA.js.map +7 -0
- package/dist/chunks/chunk-3G2F2Y7O.js +2 -0
- package/dist/chunks/chunk-3G2F2Y7O.js.map +7 -0
- package/dist/chunks/chunk-CRESEEY4.js +2 -0
- package/dist/chunks/chunk-CRESEEY4.js.map +7 -0
- package/dist/chunks/chunk-ET6J3BQX.js +3 -0
- package/dist/chunks/chunk-ET6J3BQX.js.map +7 -0
- package/dist/chunks/chunk-JUE34WZU.js +2 -0
- package/dist/chunks/chunk-JUE34WZU.js.map +7 -0
- package/dist/chunks/{chunk-AO3YMCKS.js → chunk-TEQX6FF3.js} +6 -6
- package/dist/chunks/{chunk-AO3YMCKS.js.map → chunk-TEQX6FF3.js.map} +1 -1
- package/dist/chunks/chunk-WE35XW3J.js +3 -0
- package/dist/chunks/chunk-WE35XW3J.js.map +7 -0
- package/dist/chunks/chunk-YN7VVNZR.js +2 -0
- package/dist/chunks/chunk-YN7VVNZR.js.map +7 -0
- package/dist/chunks/dist-NZDQ46PF.js +2 -0
- package/dist/chunks/model-XMH3QSZV.js +11 -0
- package/dist/chunks/model-XMH3QSZV.js.map +7 -0
- package/dist/index.js +2 -0
- package/dist/jbrowse-plugin-msaview.esm.js +2 -2
- package/dist/jbrowse-plugin-msaview.esm.js.map +4 -4
- package/dist/utils/msaIndex.d.ts +14 -0
- package/dist/utils/msaIndex.js +13 -0
- package/dist/utils/ncbiDomains.d.ts +14 -1
- package/dist/utils/ncbiDomains.js +23 -7
- package/dist/utils/ncbiDomains.test.js +54 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.ts +74 -3
- package/src/BgzipFastaMsaAdapter/configSchema.ts +6 -0
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +13 -16
- package/src/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.ts +8 -4
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +4 -7
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +12 -13
- package/src/LaunchMsaView/featureMatchesId.test.ts +97 -0
- package/src/LaunchMsaView/util.ts +52 -0
- package/src/MsaViewPanel/afterCreateAutoruns.ts +39 -3
- package/src/MsaViewPanel/displayedRefName.ts +30 -0
- package/src/MsaViewPanel/fetchIndexedMsa.ts +15 -26
- package/src/MsaViewPanel/handleMsaClick.test.ts +75 -0
- package/src/MsaViewPanel/loadProteinDomains.test.ts +42 -7
- package/src/MsaViewPanel/loadProteinDomains.ts +16 -9
- package/src/MsaViewPanel/model.ts +206 -20
- package/src/MsaViewPanel/resolveConnectedTranscript.test.ts +198 -5
- package/src/MsaViewPanel/resolveConnectedTranscript.ts +89 -23
- package/src/MsaViewPanel/transcriptMap.test.ts +21 -38
- package/src/MsaViewPanel/transcriptMap.ts +15 -17
- package/src/MsaViewPanel/util.ts +9 -0
- package/src/MsaViewPanel/viewReport.ts +185 -0
- package/src/MsaViewPanel/viewState.test.ts +340 -0
- package/src/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.ts +87 -0
- package/src/OrthoFinderMsaAdapter/configSchema.ts +19 -0
- package/src/OrthoFinderMsaAdapter/index.ts +19 -0
- package/src/OrthoFinderMsaAdapter/parseResults.test.ts +61 -0
- package/src/OrthoFinderMsaAdapter/parseResults.ts +69 -0
- package/src/index.ts +2 -0
- package/src/utils/msaIndex.ts +32 -0
- package/src/utils/ncbiDomains.test.ts +62 -5
- package/src/utils/ncbiDomains.ts +32 -8
- package/src/version.ts +1 -1
- package/dist/chunks/BgzipFastaMsaAdapter-LXM6BNRP.js +0 -4
- package/dist/chunks/BgzipFastaMsaAdapter-LXM6BNRP.js.map +0 -7
- package/dist/chunks/LaunchMsaViewDialog-CVUQSMRL.js +0 -5
- package/dist/chunks/LaunchMsaViewDialog-CVUQSMRL.js.map +0 -7
- package/dist/chunks/MsaViewPanel-UWT4FWLE.js +0 -2
- package/dist/chunks/chunk-LXN7NIHB.js +0 -2
- package/dist/chunks/chunk-LXN7NIHB.js.map +0 -7
- package/dist/chunks/chunk-MXZ7GONI.js +0 -2
- package/dist/chunks/chunk-MXZ7GONI.js.map +0 -7
- package/dist/chunks/chunk-YRZUKVPZ.js +0 -3
- package/dist/chunks/chunk-YRZUKVPZ.js.map +0 -7
- package/dist/chunks/dist-2VELCAWU.js +0 -2
- package/dist/chunks/model-JKWKYCBL.js +0 -12
- package/dist/chunks/model-JKWKYCBL.js.map +0 -7
- /package/dist/chunks/{dist-2VELCAWU.js.map → dist-NZDQ46PF.js.map} +0 -0
package/README.md
CHANGED
|
@@ -79,6 +79,8 @@ Linking and embedding:
|
|
|
79
79
|
- [Launch parameters](docs/launch-parameters.md): every argument and field.
|
|
80
80
|
- [Linked views](docs/linked-views.md): how a column reaches its codon and a
|
|
81
81
|
protein3d structure.
|
|
82
|
+
- [Reading the view](docs/reading-the-view.md): `view.report` and `notices`,
|
|
83
|
+
what a launch resolved to, as data a script or an agent reads.
|
|
82
84
|
|
|
83
85
|
Working on the plugin:
|
|
84
86
|
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
+
import type { MsaIndexEntry } from '../utils/msaIndex';
|
|
2
3
|
import type { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
4
|
+
import type { FileLocation } from '@jbrowse/core/util';
|
|
3
5
|
export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
4
6
|
configureP: Promise<BaseFeatureDataAdapter> | undefined;
|
|
5
7
|
msaRowsP: Promise<Map<string, string[]>> | undefined;
|
|
8
|
+
msaIndexP: Promise<Map<string, MsaIndexEntry> | undefined> | undefined;
|
|
6
9
|
configurePre(): Promise<BaseFeatureDataAdapter<import("@jbrowse/core/configuration").AnyConfigurationModel>>;
|
|
7
10
|
configure(): Promise<BaseFeatureDataAdapter<import("@jbrowse/core/configuration").AnyConfigurationModel>>;
|
|
11
|
+
readMsaIndex(location: FileLocation): Promise<Map<string, {
|
|
12
|
+
name: string;
|
|
13
|
+
offset: number;
|
|
14
|
+
length: number;
|
|
15
|
+
}>>;
|
|
16
|
+
loadMsaIndex(): Promise<Map<string, {
|
|
17
|
+
name: string;
|
|
18
|
+
offset: number;
|
|
19
|
+
length: number;
|
|
20
|
+
}> | undefined>;
|
|
21
|
+
getMsaIndex(): Promise<Map<string, MsaIndexEntry> | undefined>;
|
|
8
22
|
groupRows(): Promise<Map<string, string[]>>;
|
|
9
23
|
getMSARows(): Promise<Map<string, string[]>>;
|
|
10
24
|
getMSAList(): Promise<string[]>;
|
|
11
|
-
|
|
25
|
+
readIndexedMSA(index: Map<string, MsaIndexEntry>, id: string): Promise<string>;
|
|
26
|
+
readRowsMSA(id: string): Promise<string>;
|
|
27
|
+
getMSA(id: string): Promise<{
|
|
28
|
+
msa: string;
|
|
29
|
+
}>;
|
|
12
30
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readConfObject } from '@jbrowse/core/configuration';
|
|
2
2
|
import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
3
|
+
import { openLocation } from '@jbrowse/core/util/io';
|
|
3
4
|
import { firstValueFrom, toArray } from 'rxjs';
|
|
5
|
+
import { parseMsaIndex, readMsaBlock } from '../utils/msaIndex';
|
|
4
6
|
export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
5
7
|
async configurePre() {
|
|
6
8
|
const getSubAdapter = this.getSubAdapter;
|
|
@@ -22,6 +24,29 @@ export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
|
22
24
|
});
|
|
23
25
|
return this.configureP;
|
|
24
26
|
}
|
|
27
|
+
async readMsaIndex(location) {
|
|
28
|
+
const text = await openLocation(location, this.pluginManager).readFile('utf8');
|
|
29
|
+
return new Map(parseMsaIndex(text).map(e => [e.name, e]));
|
|
30
|
+
}
|
|
31
|
+
// an `.idx` beside the FASTA is used when present, as the host finds the
|
|
32
|
+
// `.fai` and `.gzi`; only one named in the config is required to exist
|
|
33
|
+
async loadMsaIndex() {
|
|
34
|
+
const configured = this.getConf('msaIndexLocation');
|
|
35
|
+
const adapter = await this.configure();
|
|
36
|
+
const beside = withSuffix(adapter.getConf('fastaLocation'), '.idx');
|
|
37
|
+
return 'uri' in configured && !configured.uri
|
|
38
|
+
? beside
|
|
39
|
+
? this.readMsaIndex(beside).catch(() => undefined)
|
|
40
|
+
: undefined
|
|
41
|
+
: this.readMsaIndex(configured);
|
|
42
|
+
}
|
|
43
|
+
getMsaIndex() {
|
|
44
|
+
this.msaIndexP ??= this.loadMsaIndex().catch((e) => {
|
|
45
|
+
this.msaIndexP = undefined;
|
|
46
|
+
throw e;
|
|
47
|
+
});
|
|
48
|
+
return this.msaIndexP;
|
|
49
|
+
}
|
|
25
50
|
async groupRows() {
|
|
26
51
|
const adapter = await this.configure();
|
|
27
52
|
const separator = new RegExp(this.getConf('msaRegex'));
|
|
@@ -46,12 +71,23 @@ export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
|
46
71
|
return this.msaRowsP;
|
|
47
72
|
}
|
|
48
73
|
async getMSAList() {
|
|
49
|
-
return [...(await this.getMSARows()).keys()];
|
|
74
|
+
return [...((await this.getMsaIndex()) ?? (await this.getMSARows())).keys()];
|
|
50
75
|
}
|
|
51
|
-
async
|
|
76
|
+
async readIndexedMSA(index, id) {
|
|
77
|
+
const adapter = await this.configure();
|
|
78
|
+
const entry = index.get(id);
|
|
79
|
+
return entry
|
|
80
|
+
? readMsaBlock({
|
|
81
|
+
filehandle: openLocation(adapter.getConf('fastaLocation'), this.pluginManager),
|
|
82
|
+
gziFilehandle: openLocation(adapter.getConf('gziLocation'), this.pluginManager),
|
|
83
|
+
entry,
|
|
84
|
+
})
|
|
85
|
+
: '';
|
|
86
|
+
}
|
|
87
|
+
async readRowsMSA(id) {
|
|
52
88
|
const adapter = await this.configure();
|
|
53
89
|
const rows = (await this.getMSARows()).get(id) ?? [];
|
|
54
|
-
|
|
90
|
+
const features = await firstValueFrom(adapter
|
|
55
91
|
.getFeaturesInMultipleRegions(rows.map(refName => ({
|
|
56
92
|
refName,
|
|
57
93
|
start: 0,
|
|
@@ -59,5 +95,21 @@ export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
|
59
95
|
assemblyName: '',
|
|
60
96
|
})))
|
|
61
97
|
.pipe(toArray()));
|
|
98
|
+
return features.map(f => `>${f.get('refName')}\n${f.get('seq')}`).join('\n');
|
|
62
99
|
}
|
|
100
|
+
async getMSA(id) {
|
|
101
|
+
const index = await this.getMsaIndex();
|
|
102
|
+
return {
|
|
103
|
+
msa: await (index
|
|
104
|
+
? this.readIndexedMSA(index, id)
|
|
105
|
+
: this.readRowsMSA(id)),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function withSuffix(location, suffix) {
|
|
110
|
+
return 'uri' in location
|
|
111
|
+
? { ...location, uri: `${location.uri}${suffix}` }
|
|
112
|
+
: 'localPath' in location
|
|
113
|
+
? { ...location, localPath: `${location.localPath}${suffix}` }
|
|
114
|
+
: undefined;
|
|
63
115
|
}
|
|
@@ -5,10 +5,20 @@ export default function configSchemaF(pluginManager: PluginManager): import("@jb
|
|
|
5
5
|
} & {
|
|
6
6
|
defaultValue: string;
|
|
7
7
|
};
|
|
8
|
+
msaIndexLocation: {
|
|
9
|
+
type: "fileLocation";
|
|
10
|
+
} & {
|
|
11
|
+
defaultValue: unknown;
|
|
12
|
+
};
|
|
8
13
|
} | {
|
|
9
14
|
readonly msaRegex: {
|
|
10
15
|
type: "string";
|
|
11
16
|
} & {
|
|
12
17
|
defaultValue: string;
|
|
13
18
|
};
|
|
19
|
+
readonly msaIndexLocation: {
|
|
20
|
+
type: "fileLocation";
|
|
21
|
+
} & {
|
|
22
|
+
defaultValue: unknown;
|
|
23
|
+
};
|
|
14
24
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<any, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>;
|
|
@@ -6,6 +6,11 @@ export default function configSchemaF(pluginManager) {
|
|
|
6
6
|
type: 'string',
|
|
7
7
|
defaultValue: '_',
|
|
8
8
|
},
|
|
9
|
+
msaIndexLocation: {
|
|
10
|
+
type: 'fileLocation',
|
|
11
|
+
description: '`.idx` of name, offset and length per alignment, which lists the set and reads each alignment as one block in place of the per-row .fai. Found beside the FASTA when unset',
|
|
12
|
+
defaultValue: { uri: '', locationType: 'UriLocation' },
|
|
13
|
+
},
|
|
9
14
|
}, {
|
|
10
15
|
baseConfiguration: base.configSchema,
|
|
11
16
|
});
|
|
@@ -7,7 +7,7 @@ import { makeStyles } from 'tss-react/mui';
|
|
|
7
7
|
import TextField2 from '../../../components/TextField2';
|
|
8
8
|
import { useFetch } from '../../../utils/useFetch';
|
|
9
9
|
import { resolveQueryRowName, useQueryRowName } from '../../useQueryRowName';
|
|
10
|
-
import {
|
|
10
|
+
import { findDatasetId, getGeneDisplayName, getLinearGenomeView, getTranscriptDisplayName, } from '../../util';
|
|
11
11
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
12
12
|
import QueryRowSelector from '../QueryRowSelector';
|
|
13
13
|
import SequenceStatusMessage from '../SequenceStatus';
|
|
@@ -33,17 +33,20 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
33
33
|
const [selectedDatasetId, setSelectedDatasetId] = useState(datasets?.[0]?.datasetId);
|
|
34
34
|
const selectedDataset = datasets?.find(d => d.datasetId === selectedDatasetId);
|
|
35
35
|
const { data: msaList, isLoading: msaListLoading, error: msaListFetchError, } = useFetch(selectedDataset ? `${selectedDataset.datasetId}-msa-list` : null, () => fetchMSAList({ config: selectedDataset.adapter, pluginManager }));
|
|
36
|
+
const msaIds = useMemo(() => msaList && new Set(msaList), [msaList]);
|
|
36
37
|
const transcriptSelection = useTranscriptSelection({
|
|
37
38
|
feature,
|
|
38
39
|
view,
|
|
39
|
-
|
|
40
|
+
hasData: msaIds
|
|
41
|
+
? transcript => !!findDatasetId(transcript, feature, msaIds)
|
|
42
|
+
: undefined,
|
|
40
43
|
preferredTranscriptId,
|
|
41
44
|
});
|
|
42
45
|
const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
43
46
|
// the dataset's name for this transcript, which a track is free not to use
|
|
44
47
|
// as the feature's own id
|
|
45
|
-
const msaId = selectedTranscript
|
|
46
|
-
?
|
|
48
|
+
const msaId = selectedTranscript && msaIds
|
|
49
|
+
? findDatasetId(selectedTranscript, feature, msaIds)
|
|
47
50
|
: undefined;
|
|
48
51
|
const { data: msaData, isLoading: msaDataLoading, error: msaDataFetchError, } = useFetch(msaId && selectedDataset
|
|
49
52
|
? `${selectedDataset.datasetId}-${msaId}-msa`
|
|
@@ -52,8 +55,7 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
52
55
|
config: selectedDataset.adapter,
|
|
53
56
|
pluginManager,
|
|
54
57
|
}));
|
|
55
|
-
const msaText =
|
|
56
|
-
'', [msaData]);
|
|
58
|
+
const msaText = msaData?.msa ?? '';
|
|
57
59
|
// The dataset's row for this transcript used to be assumed -- the launch
|
|
58
60
|
// named `<transcriptId>_<assembly>` and hoped the file agreed. Nothing
|
|
59
61
|
// checked, and a name the alignment does not carry fails silently: the view
|
|
@@ -81,11 +83,11 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
81
83
|
React.createElement(SanitizedHTML, { html: selectedDataset.description }),
|
|
82
84
|
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
|
|
83
85
|
msaText ? (React.createElement(QueryRowSelector, { ...queryRow, querySeqName: querySeqName })) : null)) : null)) : null),
|
|
84
|
-
React.createElement(SubmitCancelActions, { model: model, hint: msaDataLoading ? (React.createElement(LoadingEllipses, { message: "Loading alignment" })) : !!
|
|
86
|
+
React.createElement(SubmitCancelActions, { model: model, hint: msaDataLoading ? (React.createElement(LoadingEllipses, { message: "Loading alignment" })) : !!msaText && !querySeqName ? (React.createElement(Typography, { color: "textSecondary", variant: "body2" }, "no row matches this transcript")) : (React.createElement(SequenceStatusMessage, { status: sequenceStatus })),
|
|
85
87
|
// launching without a query row opens a view that renders and then
|
|
86
88
|
// never navigates, which reads as a broken feature rather than a
|
|
87
89
|
// dataset that does not cover this gene
|
|
88
|
-
submitDisabled: !selectedTranscript || !
|
|
90
|
+
submitDisabled: !selectedTranscript || !msaText || !querySeqName, onSubmit: placement => {
|
|
89
91
|
if (selectedTranscript && msaText) {
|
|
90
92
|
submit(() => launchConnectedView({
|
|
91
93
|
view,
|
|
@@ -100,7 +102,7 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
100
102
|
colorSchemeName: 'percent_identity_dynamic',
|
|
101
103
|
querySeqName,
|
|
102
104
|
querySeqOffset: queryRow.querySeqOffset,
|
|
103
|
-
data: { msa: msaText },
|
|
105
|
+
data: { msa: msaText, tree: msaData?.tree },
|
|
104
106
|
}));
|
|
105
107
|
}
|
|
106
108
|
}, onCancel: handleClose })));
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
2
|
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
3
|
-
|
|
3
|
+
export interface DatasetMsa {
|
|
4
|
+
msa: string;
|
|
5
|
+
tree?: string;
|
|
6
|
+
}
|
|
4
7
|
export declare function fetchMSAList({ config, pluginManager, }: {
|
|
5
8
|
config: AnyConfigurationModel;
|
|
6
9
|
pluginManager: PluginManager;
|
|
@@ -9,4 +12,4 @@ export declare function fetchMSA({ config, pluginManager, msaId, }: {
|
|
|
9
12
|
config: AnyConfigurationModel;
|
|
10
13
|
pluginManager: PluginManager;
|
|
11
14
|
msaId: string;
|
|
12
|
-
}): Promise<
|
|
15
|
+
}): Promise<DatasetMsa>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { SequenceStatus } from './SequenceStatus';
|
|
3
3
|
import type { Feature } from '@jbrowse/core/util';
|
|
4
|
-
declare const TranscriptSelector: ({ feature, options, selectedId, selectedTranscript, setSelectedId, proteinSequence, sequenceStatus,
|
|
4
|
+
declare const TranscriptSelector: ({ feature, options, selectedId, selectedTranscript, setSelectedId, proteinSequence, sequenceStatus, hasData, }: {
|
|
5
5
|
feature: Feature;
|
|
6
6
|
options: Feature[];
|
|
7
7
|
selectedId: string;
|
|
@@ -9,6 +9,6 @@ declare const TranscriptSelector: ({ feature, options, selectedId, selectedTrans
|
|
|
9
9
|
setSelectedId: (transcriptId: string) => void;
|
|
10
10
|
proteinSequence: string | undefined;
|
|
11
11
|
sequenceStatus?: SequenceStatus;
|
|
12
|
-
|
|
12
|
+
hasData?: (transcript: Feature) => boolean;
|
|
13
13
|
}) => React.JSX.Element;
|
|
14
14
|
export default TranscriptSelector;
|
|
@@ -3,7 +3,7 @@ import { Button, MenuItem, TextField } from '@mui/material';
|
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
import { makeStyles } from 'tss-react/mui';
|
|
5
5
|
import ReadOnlyTextField2 from '../../components/ReadOnlyTextField2';
|
|
6
|
-
import {
|
|
6
|
+
import { getGeneDisplayName, getId, getTranscriptDisplayName, getTranscriptLength, } from '../util';
|
|
7
7
|
const useStyles = makeStyles()({
|
|
8
8
|
flex: {
|
|
9
9
|
display: 'flex',
|
|
@@ -16,7 +16,7 @@ const useStyles = makeStyles()({
|
|
|
16
16
|
marginLeft: 20,
|
|
17
17
|
},
|
|
18
18
|
});
|
|
19
|
-
const TranscriptSelector = observer(function TranscriptSelector({ feature, options, selectedId, selectedTranscript, setSelectedId, proteinSequence, sequenceStatus = 'ready',
|
|
19
|
+
const TranscriptSelector = observer(function TranscriptSelector({ feature, options, selectedId, selectedTranscript, setSelectedId, proteinSequence, sequenceStatus = 'ready', hasData, }) {
|
|
20
20
|
const { classes } = useStyles();
|
|
21
21
|
const [showSequence, setShowSequence] = useState(false);
|
|
22
22
|
return (React.createElement(React.Fragment, null,
|
|
@@ -30,9 +30,7 @@ const TranscriptSelector = observer(function TranscriptSelector({ feature, optio
|
|
|
30
30
|
helperText: "the query row, so the alignment stays linked to the genome view", select: true, className: classes.minWidth, value: selectedId, onChange: event => {
|
|
31
31
|
setSelectedId(event.target.value);
|
|
32
32
|
} }, options.map(val => {
|
|
33
|
-
const inSet =
|
|
34
|
-
? validIds.some(id => featureMatchesId(val, id))
|
|
35
|
-
: true;
|
|
33
|
+
const inSet = hasData ? hasData(val) : true;
|
|
36
34
|
const { len, mod } = getTranscriptLength(val);
|
|
37
35
|
return (React.createElement(MenuItem, { value: getId(val), key: val.id(), disabled: !inSet },
|
|
38
36
|
getTranscriptDisplayName(val),
|
|
@@ -41,7 +39,7 @@ const TranscriptSelector = observer(function TranscriptSelector({ feature, optio
|
|
|
41
39
|
" aa)",
|
|
42
40
|
' ',
|
|
43
41
|
mod ? ` (possible fragment)` : '',
|
|
44
|
-
|
|
42
|
+
hasData ? (inSet ? ' (has data)' : ' (no data)') : ''));
|
|
45
43
|
})),
|
|
46
44
|
React.createElement("div", { className: classes.centered },
|
|
47
45
|
React.createElement(Button, { variant: "contained", color: "primary", onClick: () => {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { SequenceStatus } from './SequenceStatus';
|
|
2
2
|
import type { Feature } from '@jbrowse/core/util';
|
|
3
|
-
export declare function useTranscriptSelection({ feature, view,
|
|
3
|
+
export declare function useTranscriptSelection({ feature, view, hasData, preferredTranscriptId, }: {
|
|
4
4
|
feature: Feature;
|
|
5
5
|
view: {
|
|
6
6
|
assemblyNames?: string[];
|
|
7
7
|
} | undefined;
|
|
8
|
-
|
|
8
|
+
/** which transcripts the panel can offer, when it can only offer some */
|
|
9
|
+
hasData?: (transcript: Feature) => boolean;
|
|
9
10
|
/**
|
|
10
11
|
* the isoform the user right-clicked, when the dialog opened on its gene.
|
|
11
12
|
* Ignored when this gene does not carry it, so a stale id falls back to the
|
|
@@ -21,5 +22,5 @@ export declare function useTranscriptSelection({ feature, view, validIds, prefer
|
|
|
21
22
|
error: unknown;
|
|
22
23
|
isLoading: boolean;
|
|
23
24
|
sequenceStatus: SequenceStatus;
|
|
24
|
-
|
|
25
|
+
hasData: ((transcript: Feature) => boolean) | undefined;
|
|
25
26
|
};
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { useMemo, useState } from 'react';
|
|
2
2
|
import { featureMatchesId, getId, getSortedTranscriptFeatures } from '../util';
|
|
3
3
|
import { useFeatureSequence } from './useFeatureSequence';
|
|
4
|
-
// Keep the current selection if it
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
function pickSelectedId(currentId, options,
|
|
8
|
-
if (!
|
|
4
|
+
// Keep the current selection if it has data, otherwise fall back to the first
|
|
5
|
+
// option that does (or the current id if none do). With no hasData constraint
|
|
6
|
+
// the current selection always stands.
|
|
7
|
+
function pickSelectedId(currentId, options, hasData) {
|
|
8
|
+
if (!hasData) {
|
|
9
9
|
return currentId;
|
|
10
10
|
}
|
|
11
|
-
const isValid = (opt) => validIds.some(id => featureMatchesId(opt, id));
|
|
12
11
|
const current = options.find(opt => getId(opt) === currentId);
|
|
13
|
-
if (current &&
|
|
12
|
+
if (current && hasData(current)) {
|
|
14
13
|
return currentId;
|
|
15
14
|
}
|
|
16
|
-
const firstValid = options.find(
|
|
15
|
+
const firstValid = options.find(hasData);
|
|
17
16
|
return firstValid ? getId(firstValid) : currentId;
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
@@ -31,11 +30,11 @@ function sequenceStatus({ error, isLoading, proteinSequence, }) {
|
|
|
31
30
|
}
|
|
32
31
|
return proteinSequence ? 'ready' : 'missing';
|
|
33
32
|
}
|
|
34
|
-
export function useTranscriptSelection({ feature, view,
|
|
33
|
+
export function useTranscriptSelection({ feature, view, hasData, preferredTranscriptId, }) {
|
|
35
34
|
const options = useMemo(() => getSortedTranscriptFeatures(feature), [feature]);
|
|
36
35
|
const [selectedId, setSelectedId] = useState(() => getId(options.find(opt => featureMatchesId(opt, preferredTranscriptId ?? '')) ??
|
|
37
36
|
options[0]));
|
|
38
|
-
const validatedSelectedId = pickSelectedId(selectedId, options,
|
|
37
|
+
const validatedSelectedId = pickSelectedId(selectedId, options, hasData);
|
|
39
38
|
const selectedTranscript = options.find(val => getId(val) === validatedSelectedId);
|
|
40
39
|
const { proteinSequence, error, isLoading } = useFeatureSequence({
|
|
41
40
|
view,
|
|
@@ -50,6 +49,6 @@ export function useTranscriptSelection({ feature, view, validIds, preferredTrans
|
|
|
50
49
|
error,
|
|
51
50
|
isLoading,
|
|
52
51
|
sequenceStatus: sequenceStatus({ error, isLoading, proteinSequence }),
|
|
53
|
-
|
|
52
|
+
hasData,
|
|
54
53
|
};
|
|
55
54
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { SimpleFeature } from '@jbrowse/core/util';
|
|
2
|
+
import { expect, test } from 'vitest';
|
|
3
|
+
import { featureMatchesId, findDatasetId } from './util';
|
|
4
|
+
const transcript = new SimpleFeature({
|
|
5
|
+
uniqueId: 'u1',
|
|
6
|
+
refName: 'chr17',
|
|
7
|
+
start: 0,
|
|
8
|
+
end: 10,
|
|
9
|
+
id: 'ENST00000269305.9',
|
|
10
|
+
transcript_id: 'ENST00000269305.9',
|
|
11
|
+
gene_name: 'TP53',
|
|
12
|
+
});
|
|
13
|
+
test('a transcript matches a dataset keyed by transcript id', () => {
|
|
14
|
+
expect(featureMatchesId(transcript, 'ENST00000269305.9')).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
test('a transcript matches a dataset keyed by its gene symbol', () => {
|
|
17
|
+
expect(featureMatchesId(transcript, 'TP53')).toBe(true);
|
|
18
|
+
expect(featureMatchesId(transcript, 'TP63')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
// the shape of NCBI's GFF3, as the OrthoFinder synteny tutorial's flies have it
|
|
21
|
+
const ncbiTranscript = new SimpleFeature({
|
|
22
|
+
uniqueId: 'u2',
|
|
23
|
+
refName: '2L',
|
|
24
|
+
start: 9839,
|
|
25
|
+
end: 18570,
|
|
26
|
+
id: 'rna-NM_078715.4',
|
|
27
|
+
name: 'NM_078715.4',
|
|
28
|
+
subfeatures: [
|
|
29
|
+
{
|
|
30
|
+
uniqueId: 'u3',
|
|
31
|
+
refName: '2L',
|
|
32
|
+
start: 9900,
|
|
33
|
+
end: 10000,
|
|
34
|
+
type: 'CDS',
|
|
35
|
+
protein_id: 'NP_523439.2',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
const ncbiGene = new SimpleFeature({
|
|
40
|
+
uniqueId: 'u4',
|
|
41
|
+
refName: '2L',
|
|
42
|
+
start: 9839,
|
|
43
|
+
end: 21376,
|
|
44
|
+
id: 'gene-Dmel_CG2671',
|
|
45
|
+
name: 'l(2)gl',
|
|
46
|
+
});
|
|
47
|
+
test("a transcript matches a proteome keyed by its CDS's protein_id", () => {
|
|
48
|
+
expect(featureMatchesId(ncbiTranscript, 'NP_523439.2')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
test('a set keyed by gene id matches through the gene, without its gene- prefix', () => {
|
|
51
|
+
expect(findDatasetId(ncbiTranscript, ncbiGene, new Set(['Dmel_CG2671']))).toBe('Dmel_CG2671');
|
|
52
|
+
const ensemblGene = new SimpleFeature({
|
|
53
|
+
uniqueId: 'u5',
|
|
54
|
+
refName: '17',
|
|
55
|
+
start: 0,
|
|
56
|
+
end: 10,
|
|
57
|
+
id: 'gene:ENSG00000141510',
|
|
58
|
+
});
|
|
59
|
+
expect(findDatasetId(transcript, ensemblGene, new Set(['ENSG00000141510']))).toBe('ENSG00000141510');
|
|
60
|
+
});
|
|
61
|
+
test("the transcript's own entry wins over its gene's", () => {
|
|
62
|
+
expect(findDatasetId(ncbiTranscript, ncbiGene, new Set(['Dmel_CG2671', 'NM_078715.4']))).toBe('NM_078715.4');
|
|
63
|
+
expect(findDatasetId(ncbiTranscript, ncbiGene, new Set(['other']))).toBe(undefined);
|
|
64
|
+
});
|
|
65
|
+
test('a gene named by symbol matches the name OrthoFinder rewrote', () => {
|
|
66
|
+
const gene = new SimpleFeature({
|
|
67
|
+
uniqueId: 'u6',
|
|
68
|
+
refName: '3R',
|
|
69
|
+
start: 0,
|
|
70
|
+
end: 10,
|
|
71
|
+
id: 'gene-E(spl)m2-BFM',
|
|
72
|
+
});
|
|
73
|
+
expect(findDatasetId(ncbiTranscript, gene, new Set(['E_spl_m2-BFM']))).toBe('E_spl_m2-BFM');
|
|
74
|
+
});
|
|
@@ -8,6 +8,8 @@ export declare function getTranscriptLength(feature: Feature): {
|
|
|
8
8
|
};
|
|
9
9
|
export declare function getId(val?: Feature): string;
|
|
10
10
|
export declare function featureMatchesId(feature: Feature, id: string): boolean;
|
|
11
|
+
/** the dataset entry for this transcript, else for the gene it belongs to */
|
|
12
|
+
export declare function findDatasetId(transcript: Feature, gene: Feature, ids: ReadonlySet<string>): string | undefined;
|
|
11
13
|
export declare function getTranscriptDisplayName(val?: Feature): string;
|
|
12
14
|
export declare function getGeneDisplayName(val?: Feature): string;
|
|
13
15
|
export declare function getBlastViewTitle(feature: Feature, transcript: Feature): string;
|
|
@@ -39,6 +39,13 @@ export function getTranscriptLength(feature) {
|
|
|
39
39
|
export function getId(val) {
|
|
40
40
|
return val?.id() ?? '';
|
|
41
41
|
}
|
|
42
|
+
// a proteome downloaded from NCBI or Ensembl names each protein by the CDS's
|
|
43
|
+
// protein_id rather than by its transcript
|
|
44
|
+
function getCdsProteinIds(transcript) {
|
|
45
|
+
return (transcript.get('subfeatures') ?? [])
|
|
46
|
+
.filter(f => f.get('type') === 'CDS')
|
|
47
|
+
.map(f => f.get('protein_id'));
|
|
48
|
+
}
|
|
42
49
|
function getMatchableIds(val) {
|
|
43
50
|
return val
|
|
44
51
|
? uniqueDefined([
|
|
@@ -46,12 +53,43 @@ function getMatchableIds(val) {
|
|
|
46
53
|
val.get('name'),
|
|
47
54
|
val.get('id'),
|
|
48
55
|
val.get('transcript_id'),
|
|
56
|
+
val.get('gene_name'),
|
|
57
|
+
val.get('locus_tag'),
|
|
58
|
+
...getCdsProteinIds(val),
|
|
49
59
|
])
|
|
50
60
|
: [];
|
|
51
61
|
}
|
|
52
62
|
export function featureMatchesId(feature, id) {
|
|
53
63
|
return getMatchableIds(feature).includes(id);
|
|
54
64
|
}
|
|
65
|
+
const geneIdPrefix = /^gene[-:]/;
|
|
66
|
+
// A set keyed by gene, such as an OrthoFinder run over one protein per gene,
|
|
67
|
+
// names the gene by its GFF3 ID without NCBI's `gene-` or Ensembl's `gene:`
|
|
68
|
+
function getGeneMatchableIds(gene) {
|
|
69
|
+
const id = gene.get('id');
|
|
70
|
+
return uniqueDefined([
|
|
71
|
+
id,
|
|
72
|
+
id?.replace(geneIdPrefix, ''),
|
|
73
|
+
gene.get('gene_id'),
|
|
74
|
+
gene.get('locus_tag'),
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
// OrthoFinder writes every gene name with each character that Newick or a TSV
|
|
78
|
+
// cannot hold replaced by `_` (its utils/gene_ids.py clean_name), so NCBI's
|
|
79
|
+
// gene `E(spl)m2-BFM` is `E_spl_m2-BFM` in its results. It also replaces control
|
|
80
|
+
// characters, which a GFF3 attribute cannot carry unescaped.
|
|
81
|
+
const orthoFinderUnsafe = /[\s:;(),[\]='"]/g;
|
|
82
|
+
function findIn(candidates, ids) {
|
|
83
|
+
return (candidates.find(id => ids.has(id)) ??
|
|
84
|
+
candidates
|
|
85
|
+
.map(id => id.replace(orthoFinderUnsafe, '_'))
|
|
86
|
+
.find(id => ids.has(id)));
|
|
87
|
+
}
|
|
88
|
+
/** the dataset entry for this transcript, else for the gene it belongs to */
|
|
89
|
+
export function findDatasetId(transcript, gene, ids) {
|
|
90
|
+
return (findIn(getMatchableIds(transcript), ids) ??
|
|
91
|
+
findIn(getGeneMatchableIds(gene), ids));
|
|
92
|
+
}
|
|
55
93
|
export function getTranscriptDisplayName(val) {
|
|
56
94
|
return val ? joinDefined(' ', [val.get('name'), val.get('id')]) : '';
|
|
57
95
|
}
|
|
@@ -8,6 +8,18 @@ export declare function loadStoredData(self: JBrowsePluginMsaViewModel): void;
|
|
|
8
8
|
* refusing IndexedDB fails once rather than in a loop.
|
|
9
9
|
*/
|
|
10
10
|
export declare function storeDataToIndexedDB(self: JBrowsePluginMsaViewModel): void;
|
|
11
|
+
interface TranscriptWait {
|
|
12
|
+
connectedTranscript?: string;
|
|
13
|
+
connectedFeature?: unknown;
|
|
14
|
+
blastParams?: unknown;
|
|
15
|
+
orthologParams?: unknown;
|
|
16
|
+
launchCompleted: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A launch that has not run yet and is held back by the transcript lookup, so
|
|
20
|
+
* a lookup that fails is a launch that cannot proceed.
|
|
21
|
+
*/
|
|
22
|
+
export declare function launchAwaitsTranscript(self: TranscriptWait): boolean;
|
|
11
23
|
export declare function launchOrthologsIfNeeded(self: JBrowsePluginMsaViewModel): void;
|
|
12
24
|
export declare function launchBlastIfNeeded(self: JBrowsePluginMsaViewModel): void;
|
|
13
25
|
/**
|
|
@@ -57,4 +69,10 @@ export declare function syncGenomeHoverToMsaColumn(self: JBrowsePluginMsaViewMod
|
|
|
57
69
|
*/
|
|
58
70
|
export declare function observeProteinHighlights(self: JBrowsePluginMsaViewModel): () => void;
|
|
59
71
|
export declare function runCleanup(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Waits for the genome view to show an assembly, since the track is added to
|
|
74
|
+
* that assembly: before navigation `showDomainsOnGenome` has none to name, and
|
|
75
|
+
* clearing the request then dropped it unfulfilled.
|
|
76
|
+
*/
|
|
60
77
|
export declare function showDomainsOnGenomeIfAsked(self: JBrowsePluginMsaViewModel): void;
|
|
78
|
+
export {};
|
|
@@ -11,7 +11,7 @@ import { cleanupOldData, generateDataStoreId, retrieveMsaData, storeMsaData, } f
|
|
|
11
11
|
import { runLaunch } from './runLaunch';
|
|
12
12
|
import { getProteinViews } from './structureConnection';
|
|
13
13
|
import { proteinPositionsInRange } from './transcriptMap';
|
|
14
|
-
import { getUniprotIdFromAlphaFoldUrl, hasQueryRow, transcriptPosToVisibleCol, } from './util';
|
|
14
|
+
import { errorMessage, getUniprotIdFromAlphaFoldUrl, hasQueryRow, transcriptPosToVisibleCol, } from './util';
|
|
15
15
|
const EXPIRED_MESSAGE = "This view's alignment is no longer in browser storage. Stored alignments are kept for 7 days after they were last used, and are lost when site data is cleared.";
|
|
16
16
|
const RELAUNCHABLE = ' Retry runs the original search again and rebuilds it.';
|
|
17
17
|
const START_OVER = ' Relaunch it from the gene to rebuild it.';
|
|
@@ -140,6 +140,15 @@ export function storeDataToIndexedDB(self) {
|
|
|
140
140
|
function awaitingTranscript(self) {
|
|
141
141
|
return !!self.connectedTranscript && !self.connectedFeature;
|
|
142
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* A launch that has not run yet and is held back by the transcript lookup, so
|
|
145
|
+
* a lookup that fails is a launch that cannot proceed.
|
|
146
|
+
*/
|
|
147
|
+
export function launchAwaitsTranscript(self) {
|
|
148
|
+
return (!!(self.blastParams ?? self.orthologParams) &&
|
|
149
|
+
!self.launchCompleted &&
|
|
150
|
+
awaitingTranscript(self));
|
|
151
|
+
}
|
|
143
152
|
/**
|
|
144
153
|
* The params are the request and `launchCompleted` marks it done. They stay in
|
|
145
154
|
* place on failure, so the error is attributable, and on success, because a
|
|
@@ -180,22 +189,30 @@ export function autoLoadProteinDomains(self) {
|
|
|
180
189
|
};
|
|
181
190
|
addDisposer(self, abort);
|
|
182
191
|
self.setDomainsRequested(true);
|
|
192
|
+
self.setLoadingDomains(true);
|
|
183
193
|
self.setStatus({
|
|
184
194
|
msg: 'Fetching protein domains from NCBI',
|
|
185
195
|
onCancel: abort,
|
|
186
196
|
});
|
|
187
197
|
void (async () => {
|
|
188
198
|
try {
|
|
189
|
-
await loadProteinDomains(self, controller.signal);
|
|
199
|
+
const notice = await loadProteinDomains(self, controller.signal);
|
|
200
|
+
if (notice && isAlive(self)) {
|
|
201
|
+
self.addWarning(notice);
|
|
202
|
+
}
|
|
190
203
|
}
|
|
191
204
|
catch (e) {
|
|
192
205
|
if (!isAbortError(e)) {
|
|
193
206
|
console.error('[msaview-domains] auto-load failed:', e);
|
|
207
|
+
if (isAlive(self)) {
|
|
208
|
+
self.addWarning(`Protein domains did not load from NCBI: ${errorMessage(e)}`);
|
|
209
|
+
}
|
|
194
210
|
}
|
|
195
211
|
}
|
|
196
212
|
finally {
|
|
197
213
|
if (isAlive(self)) {
|
|
198
214
|
self.setStatus(undefined);
|
|
215
|
+
self.setLoadingDomains(false);
|
|
199
216
|
}
|
|
200
217
|
}
|
|
201
218
|
})();
|
|
@@ -390,9 +407,14 @@ export function runCleanup() {
|
|
|
390
407
|
console.error('Failed to cleanup old MSA data:', e);
|
|
391
408
|
});
|
|
392
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Waits for the genome view to show an assembly, since the track is added to
|
|
412
|
+
* that assembly: before navigation `showDomainsOnGenome` has none to name, and
|
|
413
|
+
* clearing the request then dropped it unfulfilled.
|
|
414
|
+
*/
|
|
393
415
|
export function showDomainsOnGenomeIfAsked(self) {
|
|
394
416
|
if (self.domainsOnGenome &&
|
|
395
|
-
self.connectedView &&
|
|
417
|
+
self.connectedView?.assemblyNames[0] &&
|
|
396
418
|
self.connectedDomains.features.length > 0) {
|
|
397
419
|
self.showDomainsOnGenome();
|
|
398
420
|
self.setDomainsOnGenome(false);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface Resolver {
|
|
2
|
+
initialized?: boolean;
|
|
3
|
+
getCanonicalRefName: (refName: string) => string | undefined;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The name a view's displayed regions use for `refName`'s chromosome. core's
|
|
7
|
+
* bpToPx, and so centerAt, matches region names exactly and does nothing when
|
|
8
|
+
* the feature says `chr1` and the view displays `1`. The raw name is kept when
|
|
9
|
+
* a region already carries it, and when no region resolves to the same
|
|
10
|
+
* chromosome, so a view this cannot place behaves as it did before.
|
|
11
|
+
*/
|
|
12
|
+
export declare function displayedRefName({ displayedRegions, assembly, refName, }: {
|
|
13
|
+
displayedRegions: readonly {
|
|
14
|
+
refName: string;
|
|
15
|
+
}[];
|
|
16
|
+
assembly?: Resolver;
|
|
17
|
+
refName: string;
|
|
18
|
+
}): string;
|
|
19
|
+
export {};
|