jbrowse-plugin-msaview 5.16.1 → 5.16.2
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/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +12 -4
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.d.ts +6 -2
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.js +4 -4
- package/dist/LaunchMsaView/components/pickSelectedId.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/pickSelectedId.test.js +17 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +1 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +1 -1
- package/dist/LaunchMsaView/detectQueryRow.d.ts +1 -1
- package/dist/LaunchMsaView/detectQueryRow.js +37 -25
- package/dist/LaunchMsaView/detectQueryRow.test.js +9 -0
- package/dist/LaunchMsaView/useQueryRowName.d.ts +1 -1
- package/dist/LaunchMsaView/useQueryRowName.js +2 -2
- package/dist/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.d.ts +9 -6
- package/dist/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.js +50 -19
- package/dist/OrthoFinderMsaAdapter/parseResults.d.ts +20 -5
- package/dist/OrthoFinderMsaAdapter/parseResults.js +32 -14
- package/dist/OrthoFinderMsaAdapter/parseResults.test.js +25 -7
- package/dist/chunks/LaunchMsaViewDialog-CZR76VIV.js +3 -0
- package/dist/chunks/LaunchMsaViewDialog-CZR76VIV.js.map +7 -0
- package/dist/chunks/OrthoFinderMsaAdapter-BAVVTZHO.js +2 -0
- package/dist/chunks/OrthoFinderMsaAdapter-BAVVTZHO.js.map +7 -0
- package/dist/jbrowse-plugin-msaview.esm.js +2 -2
- package/dist/jbrowse-plugin-msaview.esm.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +18 -5
- package/src/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.ts +12 -4
- package/src/LaunchMsaView/components/pickSelectedId.test.ts +24 -0
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +1 -1
- package/src/LaunchMsaView/detectQueryRow.test.ts +11 -0
- package/src/LaunchMsaView/detectQueryRow.ts +49 -33
- package/src/LaunchMsaView/useQueryRowName.ts +7 -3
- package/src/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.ts +61 -24
- package/src/OrthoFinderMsaAdapter/parseResults.test.ts +34 -7
- package/src/OrthoFinderMsaAdapter/parseResults.ts +42 -14
- package/src/version.ts +1 -1
- package/dist/chunks/LaunchMsaViewDialog-H23R3EDJ.js +0 -3
- package/dist/chunks/LaunchMsaViewDialog-H23R3EDJ.js.map +0 -7
- package/dist/chunks/OrthoFinderMsaAdapter-I366HWAA.js +0 -2
- package/dist/chunks/OrthoFinderMsaAdapter-I366HWAA.js.map +0 -7
|
@@ -28,11 +28,18 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
28
28
|
const { classes } = useStyles();
|
|
29
29
|
const { pluginManager } = getEnv(model);
|
|
30
30
|
const { assemblyNames } = view;
|
|
31
|
+
const assemblyName = assemblyNames[0];
|
|
31
32
|
const { launchError, submit } = useLaunchSubmit(handleClose);
|
|
32
33
|
const datasets = readMsaDatasets(session.jbrowse);
|
|
33
34
|
const [selectedDatasetId, setSelectedDatasetId] = useState(datasets?.[0]?.datasetId);
|
|
34
35
|
const selectedDataset = datasets?.find(d => d.datasetId === selectedDatasetId);
|
|
35
|
-
const { data: msaList, isLoading: msaListLoading, error: msaListFetchError, } = useFetch(selectedDataset
|
|
36
|
+
const { data: msaList, isLoading: msaListLoading, error: msaListFetchError, } = useFetch(selectedDataset
|
|
37
|
+
? `${selectedDataset.datasetId}-${assemblyName}-msa-list`
|
|
38
|
+
: null, () => fetchMSAList({
|
|
39
|
+
config: selectedDataset.adapter,
|
|
40
|
+
pluginManager,
|
|
41
|
+
assemblyName,
|
|
42
|
+
}));
|
|
36
43
|
const msaIds = useMemo(() => msaList && new Set(msaList), [msaList]);
|
|
37
44
|
const transcriptSelection = useTranscriptSelection({
|
|
38
45
|
feature,
|
|
@@ -49,9 +56,10 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
49
56
|
? findDatasetId(selectedTranscript, feature, msaIds)
|
|
50
57
|
: undefined;
|
|
51
58
|
const { data: msaData, isLoading: msaDataLoading, error: msaDataFetchError, } = useFetch(msaId && selectedDataset
|
|
52
|
-
? `${selectedDataset.datasetId}-${msaId}-msa`
|
|
59
|
+
? `${selectedDataset.datasetId}-${assemblyName}-${msaId}-msa`
|
|
53
60
|
: null, () => fetchMSA({
|
|
54
61
|
msaId: msaId,
|
|
62
|
+
assemblyName,
|
|
55
63
|
config: selectedDataset.adapter,
|
|
56
64
|
pluginManager,
|
|
57
65
|
}));
|
|
@@ -61,12 +69,12 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
61
69
|
// checked, and a name the alignment does not carry fails silently: the view
|
|
62
70
|
// opens, renders, and never navigates. The row is found by sequence instead,
|
|
63
71
|
// the same way the Manual tab finds it.
|
|
64
|
-
const queryRow = useQueryRowName(msaText, proteinSequence);
|
|
72
|
+
const queryRow = useQueryRowName(msaText, proteinSequence, msaData?.queryRowName);
|
|
65
73
|
// The name this panel used to launch with unconditionally. A dataset built to
|
|
66
74
|
// that convention does carry the row, so it is worth falling back to when the
|
|
67
75
|
// residues do not match closely enough to find it -- but only when the
|
|
68
76
|
// alignment really has it, which is the check the old code never made.
|
|
69
|
-
const querySeqName = resolveQueryRowName(queryRow, `${msaId ?? ''}_${
|
|
77
|
+
const querySeqName = resolveQueryRowName(queryRow, `${msaId ?? ''}_${assemblyName ?? ''}`);
|
|
70
78
|
const e = msaListFetchError ??
|
|
71
79
|
msaDataFetchError ??
|
|
72
80
|
transcriptSelection.error ??
|
|
@@ -3,13 +3,17 @@ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
|
3
3
|
export interface DatasetMsa {
|
|
4
4
|
msa: string;
|
|
5
5
|
tree?: string;
|
|
6
|
+
/** the row the dataset names as the protein's own, preferred over a row that matches it equally */
|
|
7
|
+
queryRowName?: string;
|
|
6
8
|
}
|
|
7
|
-
export declare function fetchMSAList({ config, pluginManager, }: {
|
|
9
|
+
export declare function fetchMSAList({ config, pluginManager, assemblyName, }: {
|
|
8
10
|
config: AnyConfigurationModel;
|
|
9
11
|
pluginManager: PluginManager;
|
|
12
|
+
assemblyName?: string;
|
|
10
13
|
}): Promise<string[]>;
|
|
11
|
-
export declare function fetchMSA({ config, pluginManager, msaId, }: {
|
|
14
|
+
export declare function fetchMSA({ config, pluginManager, msaId, assemblyName, }: {
|
|
12
15
|
config: AnyConfigurationModel;
|
|
13
16
|
pluginManager: PluginManager;
|
|
14
17
|
msaId: string;
|
|
18
|
+
assemblyName?: string;
|
|
15
19
|
}): Promise<DatasetMsa>;
|
|
@@ -3,11 +3,11 @@ async function getMsaAdapter(pluginManager, config) {
|
|
|
3
3
|
const result = await getAdapter(pluginManager, 'msa', config);
|
|
4
4
|
return result.dataAdapter;
|
|
5
5
|
}
|
|
6
|
-
export async function fetchMSAList({ config, pluginManager, }) {
|
|
6
|
+
export async function fetchMSAList({ config, pluginManager, assemblyName, }) {
|
|
7
7
|
const adapter = await getMsaAdapter(pluginManager, config);
|
|
8
|
-
return adapter.getMSAList();
|
|
8
|
+
return adapter.getMSAList(assemblyName);
|
|
9
9
|
}
|
|
10
|
-
export async function fetchMSA({ config, pluginManager, msaId, }) {
|
|
10
|
+
export async function fetchMSA({ config, pluginManager, msaId, assemblyName, }) {
|
|
11
11
|
const adapter = await getMsaAdapter(pluginManager, config);
|
|
12
|
-
return adapter.getMSA(msaId);
|
|
12
|
+
return adapter.getMSA(msaId, assemblyName);
|
|
13
13
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SimpleFeature } from '@jbrowse/core/util';
|
|
2
|
+
import { expect, test } from 'vitest';
|
|
3
|
+
import { pickSelectedId } from './useTranscriptSelection';
|
|
4
|
+
const transcript = (id) => new SimpleFeature({ uniqueId: id, refName: '2L', start: 0, end: 10 });
|
|
5
|
+
const options = ['t1', 't2', 't3'].map(transcript);
|
|
6
|
+
test('with no hasData the selection always stands', () => {
|
|
7
|
+
expect(pickSelectedId('t2', options, undefined)).toBe('t2');
|
|
8
|
+
});
|
|
9
|
+
test('a selection with data stands', () => {
|
|
10
|
+
expect(pickSelectedId('t2', options, () => true)).toBe('t2');
|
|
11
|
+
});
|
|
12
|
+
test('a selection without data moves to the first option that has some', () => {
|
|
13
|
+
expect(pickSelectedId('t1', options, t => t.id() !== 't1')).toBe('t2');
|
|
14
|
+
});
|
|
15
|
+
test('with no option holding data the selection stands', () => {
|
|
16
|
+
expect(pickSelectedId('t3', options, () => false)).toBe('t3');
|
|
17
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SequenceStatus } from './SequenceStatus';
|
|
2
2
|
import type { Feature } from '@jbrowse/core/util';
|
|
3
|
+
export declare function pickSelectedId(currentId: string, options: Feature[], hasData: ((transcript: Feature) => boolean) | undefined): string;
|
|
3
4
|
export declare function useTranscriptSelection({ feature, view, hasData, preferredTranscriptId, }: {
|
|
4
5
|
feature: Feature;
|
|
5
6
|
view: {
|
|
@@ -4,7 +4,7 @@ import { useFeatureSequence } from './useFeatureSequence';
|
|
|
4
4
|
// Keep the current selection if it has data, otherwise fall back to the first
|
|
5
5
|
// option that does (or the current id if none do). With no hasData constraint
|
|
6
6
|
// the current selection always stands.
|
|
7
|
-
function pickSelectedId(currentId, options, hasData) {
|
|
7
|
+
export function pickSelectedId(currentId, options, hasData) {
|
|
8
8
|
if (!hasData) {
|
|
9
9
|
return currentId;
|
|
10
10
|
}
|
|
@@ -44,4 +44,4 @@ export interface MsaQueryRow {
|
|
|
44
44
|
* wanted on every keystroke in the paste box, and asking separately parsed a
|
|
45
45
|
* few-hundred-row alignment twice per character.
|
|
46
46
|
*/
|
|
47
|
-
export declare function findQueryRow(msaText: string, proteinSequence: string): MsaQueryRow;
|
|
47
|
+
export declare function findQueryRow(msaText: string, proteinSequence: string, preferredName?: string): MsaQueryRow;
|
|
@@ -41,7 +41,7 @@ const PARTIAL_COVERAGE_FLOOR = 0.5;
|
|
|
41
41
|
* wanted on every keystroke in the paste box, and asking separately parsed a
|
|
42
42
|
* few-hundred-row alignment twice per character.
|
|
43
43
|
*/
|
|
44
|
-
export function findQueryRow(msaText, proteinSequence) {
|
|
44
|
+
export function findQueryRow(msaText, proteinSequence, preferredName) {
|
|
45
45
|
if (!msaText.trim()) {
|
|
46
46
|
return { names: [] };
|
|
47
47
|
}
|
|
@@ -56,42 +56,54 @@ export function findQueryRow(msaText, proteinSequence) {
|
|
|
56
56
|
}
|
|
57
57
|
const names = parsed.getNames();
|
|
58
58
|
const query = normalize(proteinSequence);
|
|
59
|
-
return {
|
|
59
|
+
return {
|
|
60
|
+
names,
|
|
61
|
+
match: query ? bestMatch(parsed, names, query, preferredName) : undefined,
|
|
62
|
+
};
|
|
60
63
|
}
|
|
61
|
-
function
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return { name, quality: 'exact', identity: 1, offset: 0 };
|
|
71
|
-
}
|
|
64
|
+
function scoreRow(parsed, name, query) {
|
|
65
|
+
const row = normalize(getUngappedSequence(parsed.getRow(name)));
|
|
66
|
+
if (!row) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
else if (row === query) {
|
|
70
|
+
return { name, quality: 'exact', identity: 1, offset: 0 };
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
72
73
|
// BLAST reports the aligned region, so the row is often the query trimmed
|
|
73
74
|
// at one or both ends rather than the whole protein
|
|
74
75
|
const contained = query.indexOf(row);
|
|
75
76
|
const containing = row.indexOf(query);
|
|
76
77
|
if (contained !== -1 || containing !== -1) {
|
|
77
78
|
const coverage = Math.min(row.length, query.length) / Math.max(row.length, query.length);
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
return coverage >= PARTIAL_COVERAGE_FLOOR
|
|
80
|
+
? {
|
|
80
81
|
name,
|
|
81
82
|
quality: 'partial',
|
|
82
83
|
identity: coverage,
|
|
83
84
|
offset: contained === -1 ? -containing : contained,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
continue;
|
|
85
|
+
}
|
|
86
|
+
: undefined;
|
|
87
87
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
else {
|
|
89
|
+
const identity = identityOverOverlap(row, query);
|
|
90
|
+
return identity >= SIMILARITY_FLOOR
|
|
91
|
+
? { name, quality: 'similar', identity, offset: 0 }
|
|
92
|
+
: undefined;
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
}
|
|
96
|
+
const qualityOrder = ['exact', 'partial', 'similar'];
|
|
97
|
+
/**
|
|
98
|
+
* The row matching the protein best. A paralog with an identical protein
|
|
99
|
+
* matches exactly as well as the query does, so a row the dataset itself names
|
|
100
|
+
* as the protein's own wins any tie.
|
|
101
|
+
*/
|
|
102
|
+
function bestMatch(parsed, names, query, preferredName) {
|
|
103
|
+
return names
|
|
104
|
+
.map(name => scoreRow(parsed, name, query))
|
|
105
|
+
.filter(match => match !== undefined)
|
|
106
|
+
.sort((a, b) => qualityOrder.indexOf(a.quality) - qualityOrder.indexOf(b.quality) ||
|
|
107
|
+
b.identity - a.identity ||
|
|
108
|
+
Number(b.name === preferredName) - Number(a.name === preferredName))[0];
|
|
97
109
|
}
|
|
@@ -93,3 +93,12 @@ describe('findQueryRow row names', () => {
|
|
|
93
93
|
expect(findQueryRow('', protein).names).toEqual([]);
|
|
94
94
|
});
|
|
95
95
|
});
|
|
96
|
+
test("an identical paralog loses the tie to the row the dataset names as the protein's own", () => {
|
|
97
|
+
const msa = '>sp_H4copy\nMSGRGKGG\n>sp_His4r\nMSGRGKGG\n>sp_other\nMSGRGKAA\n';
|
|
98
|
+
expect(findQueryRow(msa, 'MSGRGKGG').match?.name).toBe('sp_H4copy');
|
|
99
|
+
expect(findQueryRow(msa, 'MSGRGKGG', 'sp_His4r').match?.name).toBe('sp_His4r');
|
|
100
|
+
});
|
|
101
|
+
test('a named row that matches worse than another does not win', () => {
|
|
102
|
+
const msa = '>sp_exact\nMSGRGKGG\n>sp_named\nMSGRGKAA\n';
|
|
103
|
+
expect(findQueryRow(msa, 'MSGRGKGG', 'sp_named').match?.name).toBe('sp_exact');
|
|
104
|
+
});
|
|
@@ -18,7 +18,7 @@ export declare function resolveQueryRowName(queryRow: {
|
|
|
18
18
|
querySeqName: string;
|
|
19
19
|
names: string[];
|
|
20
20
|
}, fallback: string): string;
|
|
21
|
-
export declare function useQueryRowName(msaText: string, proteinSequence: string): {
|
|
21
|
+
export declare function useQueryRowName(msaText: string, proteinSequence: string, preferredName?: string): {
|
|
22
22
|
detected: import("./detectQueryRow").QueryRowMatch | undefined;
|
|
23
23
|
names: string[];
|
|
24
24
|
querySeqName: string;
|
|
@@ -19,11 +19,11 @@ import { findQueryRow } from './detectQueryRow';
|
|
|
19
19
|
export function resolveQueryRowName(queryRow, fallback) {
|
|
20
20
|
return (queryRow.querySeqName || (queryRow.names.includes(fallback) ? fallback : ''));
|
|
21
21
|
}
|
|
22
|
-
export function useQueryRowName(msaText, proteinSequence) {
|
|
22
|
+
export function useQueryRowName(msaText, proteinSequence, preferredName) {
|
|
23
23
|
const [override, setOverride] = useState();
|
|
24
24
|
// parsing runs on every keystroke in the paste box otherwise, and an
|
|
25
25
|
// alignment of a few hundred rows is not free
|
|
26
|
-
const { names, match } = useMemo(() => findQueryRow(msaText, proteinSequence), [msaText, proteinSequence]);
|
|
26
|
+
const { names, match } = useMemo(() => findQueryRow(msaText, proteinSequence, preferredName), [msaText, proteinSequence, preferredName]);
|
|
27
27
|
// a row picked from one alignment says nothing about the next one pasted
|
|
28
28
|
const picked = override !== undefined && (names.length === 0 || names.includes(override))
|
|
29
29
|
? override
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
+
import type { ProteinEntry } from './parseResults';
|
|
2
3
|
/**
|
|
3
4
|
* Reads the `Results_*` folder of an OrthoFinder run as a dataset: each protein
|
|
4
5
|
* id it saw names its orthogroup's alignment and gene tree.
|
|
5
6
|
*/
|
|
6
7
|
export default class OrthoFinderMsaAdapter extends BaseAdapter {
|
|
7
|
-
orthogroupsP: Promise<Map<string, string
|
|
8
|
-
resolvedTreesP: Promise<Map<string, string> |
|
|
8
|
+
orthogroupsP: Promise<Map<string, Map<string, ProteinEntry>>> | undefined;
|
|
9
|
+
resolvedTreesP: Promise<Map<string, string> | 'per-orthogroup'> | undefined;
|
|
9
10
|
readResultsFile(path: string): Promise<string>;
|
|
10
|
-
getOrthogroups(): Promise<Map<string, string
|
|
11
|
-
getResolvedTrees(): Promise<Map<string, string> |
|
|
11
|
+
getOrthogroups(): Promise<Map<string, Map<string, ProteinEntry>>>;
|
|
12
|
+
getResolvedTrees(): Promise<Map<string, string> | "per-orthogroup">;
|
|
12
13
|
readAlignment(orthogroup: string): Promise<string>;
|
|
13
14
|
readTree(orthogroup: string): Promise<string | undefined>;
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
speciesFor(assemblyName?: string): Promise<string[]>;
|
|
16
|
+
getMSAList(assemblyName?: string): Promise<string[]>;
|
|
17
|
+
getMSA(proteinId: string, assemblyName?: string): Promise<{
|
|
16
18
|
msa: string;
|
|
17
19
|
tree: string | undefined;
|
|
20
|
+
queryRowName: string;
|
|
18
21
|
}>;
|
|
19
22
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
2
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
3
|
-
import { parseOrthogroups, parseResolvedGeneTrees, resultsFile, } from './parseResults';
|
|
3
|
+
import { alignmentRowName, isMissingFile, parseOrthogroups, parseResolvedGeneTrees, resultsFile, } from './parseResults';
|
|
4
4
|
/**
|
|
5
5
|
* Reads the `Results_*` folder of an OrthoFinder run as a dataset: each protein
|
|
6
6
|
* id it saw names its orthogroup's alignment and gene tree.
|
|
@@ -18,12 +18,15 @@ export default class OrthoFinderMsaAdapter extends BaseAdapter {
|
|
|
18
18
|
});
|
|
19
19
|
return this.orthogroupsP;
|
|
20
20
|
}
|
|
21
|
-
// OrthoFinder 2 writes Resolved_Gene_Trees/<orthogroup>_tree.txt instead, so
|
|
22
|
-
// a missing combined file means a version 2 run rather than an error
|
|
23
21
|
getResolvedTrees() {
|
|
24
|
-
this.resolvedTreesP ??= this.readResultsFile('Resolved_Gene_Trees/Resolved_Gene_Trees.txt').then(parseResolvedGeneTrees, () => {
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
this.resolvedTreesP ??= this.readResultsFile('Resolved_Gene_Trees/Resolved_Gene_Trees.txt').then(parseResolvedGeneTrees, (e) => {
|
|
23
|
+
if (isMissingFile(e)) {
|
|
24
|
+
return 'per-orthogroup';
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.resolvedTreesP = undefined;
|
|
28
|
+
throw e;
|
|
29
|
+
}
|
|
27
30
|
});
|
|
28
31
|
return this.resolvedTreesP;
|
|
29
32
|
}
|
|
@@ -32,27 +35,55 @@ export default class OrthoFinderMsaAdapter extends BaseAdapter {
|
|
|
32
35
|
return await this.readResultsFile(`MultipleSequenceAlignments/${orthogroup}.fa`);
|
|
33
36
|
}
|
|
34
37
|
catch (e) {
|
|
35
|
-
throw
|
|
38
|
+
throw isMissingFile(e)
|
|
39
|
+
? new Error(`OrthoFinder wrote no alignment for ${orthogroup}. It skips a few small orthogroups, and writes none at all when run with -og, or as OrthoFinder 2 without -M msa`, { cause: e })
|
|
40
|
+
: e;
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
|
-
// only orthogroups of four or more genes
|
|
43
|
+
// only orthogroups of four or more genes have a tree
|
|
39
44
|
async readTree(orthogroup) {
|
|
40
45
|
const resolvedTrees = await this.getResolvedTrees();
|
|
41
|
-
return resolvedTrees
|
|
42
|
-
?
|
|
43
|
-
|
|
46
|
+
return resolvedTrees === 'per-orthogroup'
|
|
47
|
+
? this.readResultsFile(`Resolved_Gene_Trees/${orthogroup}_tree.txt`).catch((e) => {
|
|
48
|
+
if (isMissingFile(e)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
: resolvedTrees.get(orthogroup);
|
|
44
56
|
}
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
// the column named after the assembly when there is one, else every column,
|
|
58
|
+
// for a run whose proteome files are named some other way
|
|
59
|
+
async speciesFor(assemblyName) {
|
|
60
|
+
const bySpecies = await this.getOrthogroups();
|
|
61
|
+
return assemblyName && bySpecies.has(assemblyName)
|
|
62
|
+
? [assemblyName]
|
|
63
|
+
: [...bySpecies.keys()];
|
|
47
64
|
}
|
|
48
|
-
async
|
|
49
|
-
const
|
|
50
|
-
|
|
65
|
+
async getMSAList(assemblyName) {
|
|
66
|
+
const bySpecies = await this.getOrthogroups();
|
|
67
|
+
const species = await this.speciesFor(assemblyName);
|
|
68
|
+
return [
|
|
69
|
+
...new Set(species.flatMap(s => [...(bySpecies.get(s)?.keys() ?? [])])),
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
async getMSA(proteinId, assemblyName) {
|
|
73
|
+
const bySpecies = await this.getOrthogroups();
|
|
74
|
+
const found = (await this.speciesFor(assemblyName))
|
|
75
|
+
.map(species => ({
|
|
76
|
+
species,
|
|
77
|
+
entry: bySpecies.get(species)?.get(proteinId),
|
|
78
|
+
}))
|
|
79
|
+
.find(({ entry }) => entry);
|
|
80
|
+
if (found?.entry) {
|
|
81
|
+
const { species, entry } = found;
|
|
51
82
|
const [msa, tree] = await Promise.all([
|
|
52
|
-
this.readAlignment(orthogroup),
|
|
53
|
-
this.readTree(orthogroup),
|
|
83
|
+
this.readAlignment(entry.orthogroup),
|
|
84
|
+
this.readTree(entry.orthogroup),
|
|
54
85
|
]);
|
|
55
|
-
return { msa, tree };
|
|
86
|
+
return { msa, tree, queryRowName: alignmentRowName(species, entry.id) };
|
|
56
87
|
}
|
|
57
88
|
else {
|
|
58
89
|
throw new Error(`${proteinId} is in no OrthoFinder orthogroup`);
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import type { FileLocation } from '@jbrowse/core/util/types';
|
|
2
|
+
export interface ProteinEntry {
|
|
3
|
+
orthogroup: string;
|
|
4
|
+
/** the id as OrthoFinder wrote it, which names the protein's alignment row */
|
|
5
|
+
id: string;
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
8
|
+
* OrthoFinder's `Orthogroups/Orthogroups.tsv`, one map per species column from
|
|
9
|
+
* protein id to its orthogroup. Per species because OrthoFinder leaves a name
|
|
10
|
+
* two species share unprefixed there: human's `ADAM32` and chicken's are
|
|
11
|
+
* different genes in different orthogroups. Each id is also listed without a
|
|
12
|
+
* trailing `.N` version, since a proteome FASTA usually versions its ids where
|
|
13
|
+
* the matching GFF does not (Ensembl's `ENSP00000269305.4` against
|
|
14
|
+
* `protein_id=ENSP00000269305`).
|
|
7
15
|
*/
|
|
8
|
-
export declare function parseOrthogroups(tsv: string): Map<string, string
|
|
16
|
+
export declare function parseOrthogroups(tsv: string): Map<string, Map<string, ProteinEntry>>;
|
|
17
|
+
/**
|
|
18
|
+
* The alignment and tree name each protein `<species>_<id>`, the species with
|
|
19
|
+
* `.` and space made `_` (OrthoFinder's species_prefixes).
|
|
20
|
+
*/
|
|
21
|
+
export declare function alignmentRowName(species: string, id: string): string;
|
|
22
|
+
/** a file the results folder does not hold, as opposed to one it failed to serve */
|
|
23
|
+
export declare function isMissingFile(e: unknown): boolean;
|
|
9
24
|
/**
|
|
10
25
|
* OrthoFinder 3 writes every gene tree into one
|
|
11
26
|
* `Resolved_Gene_Trees/Resolved_Gene_Trees.txt`, a line per orthogroup of the
|
|
@@ -1,31 +1,49 @@
|
|
|
1
1
|
import { isLocalPathLocation, isUriLocation } from '@jbrowse/core/util/types';
|
|
2
2
|
const versionSuffix = /\.\d+$/;
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* OrthoFinder's `Orthogroups/Orthogroups.tsv`, one map per species column from
|
|
5
|
+
* protein id to its orthogroup. Per species because OrthoFinder leaves a name
|
|
6
|
+
* two species share unprefixed there: human's `ADAM32` and chicken's are
|
|
7
|
+
* different genes in different orthogroups. Each id is also listed without a
|
|
8
|
+
* trailing `.N` version, since a proteome FASTA usually versions its ids where
|
|
9
|
+
* the matching GFF does not (Ensembl's `ENSP00000269305.4` against
|
|
10
|
+
* `protein_id=ENSP00000269305`).
|
|
8
11
|
*/
|
|
9
12
|
export function parseOrthogroups(tsv) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const [header = '', ...rows] = tsv.split(/\r?\n/);
|
|
14
|
+
const species = header.split('\t').slice(1);
|
|
15
|
+
const bySpecies = new Map(species.map(name => [name, new Map()]));
|
|
16
|
+
for (const row of rows) {
|
|
17
|
+
const [orthogroup, ...cells] = row.split('\t');
|
|
13
18
|
if (orthogroup) {
|
|
14
|
-
|
|
19
|
+
cells.forEach((cell, i) => {
|
|
20
|
+
const proteins = bySpecies.get(species[i] ?? '');
|
|
15
21
|
for (const geneId of cell.split(',')) {
|
|
16
22
|
const id = geneId.trim();
|
|
17
|
-
if (id) {
|
|
18
|
-
|
|
23
|
+
if (proteins && id) {
|
|
24
|
+
const entry = { orthogroup, id };
|
|
25
|
+
proteins.set(id, entry);
|
|
19
26
|
const unversioned = id.replace(versionSuffix, '');
|
|
20
|
-
if (!
|
|
21
|
-
|
|
27
|
+
if (!proteins.has(unversioned)) {
|
|
28
|
+
proteins.set(unversioned, entry);
|
|
22
29
|
}
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
|
-
}
|
|
32
|
+
});
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
|
-
return
|
|
35
|
+
return bySpecies;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The alignment and tree name each protein `<species>_<id>`, the species with
|
|
39
|
+
* `.` and space made `_` (OrthoFinder's species_prefixes).
|
|
40
|
+
*/
|
|
41
|
+
export function alignmentRowName(species, id) {
|
|
42
|
+
return `${species.replace(/[. ]/g, '_')}_${id}`;
|
|
43
|
+
}
|
|
44
|
+
/** a file the results folder does not hold, as opposed to one it failed to serve */
|
|
45
|
+
export function isMissingFile(e) {
|
|
46
|
+
return e instanceof Error && /HTTP 40[34]\b|ENOENT/.test(e.message);
|
|
29
47
|
}
|
|
30
48
|
/**
|
|
31
49
|
* OrthoFinder 3 writes every gene tree into one
|
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
|
-
import { parseOrthogroups, parseResolvedGeneTrees, resultsFile, } from './parseResults';
|
|
2
|
+
import { alignmentRowName, isMissingFile, parseOrthogroups, parseResolvedGeneTrees, resultsFile, } from './parseResults';
|
|
3
3
|
test('a versioned protein id also answers to its unversioned form', () => {
|
|
4
|
-
const
|
|
5
|
-
expect(
|
|
6
|
-
expect(
|
|
7
|
-
|
|
4
|
+
const human = parseOrthogroups('Orthogroup\tHuman\tMouse\nOG0000001\tENSP00000269305.4\tENSMUSP00000104298.3, ENSMUSP00000005371\n').get('Human');
|
|
5
|
+
expect(human?.get('ENSP00000269305.4')?.orthogroup).toBe('OG0000001');
|
|
6
|
+
expect(human?.get('ENSP00000269305')).toEqual({
|
|
7
|
+
orthogroup: 'OG0000001',
|
|
8
|
+
id: 'ENSP00000269305.4',
|
|
9
|
+
});
|
|
8
10
|
});
|
|
9
11
|
test('an unversioned alias never displaces a real id of the same name', () => {
|
|
10
12
|
const groups = parseOrthogroups('Orthogroup\tA\nOG0000001\tP1.2\nOG0000002\tP1\n');
|
|
11
|
-
expect(groups.get('P1')).toBe('OG0000002');
|
|
13
|
+
expect(groups.get('A')?.get('P1')?.orthogroup).toBe('OG0000002');
|
|
12
14
|
const reversed = parseOrthogroups('Orthogroup\tA\nOG0000002\tP1\nOG0000001\tP1.2\n');
|
|
13
|
-
expect(reversed.get('P1')).toBe('OG0000002');
|
|
15
|
+
expect(reversed.get('A')?.get('P1')?.orthogroup).toBe('OG0000002');
|
|
16
|
+
});
|
|
17
|
+
test("a name two species share stays each species' own", () => {
|
|
18
|
+
const groups = parseOrthogroups('Orthogroup\tchicken\thuman\r\nOG0013180\t\tADAM32\r\nOG0015286\tADAM32\t\r\n');
|
|
19
|
+
expect(groups.get('human')?.get('ADAM32')?.orthogroup).toBe('OG0013180');
|
|
20
|
+
expect(groups.get('chicken')?.get('ADAM32')?.orthogroup).toBe('OG0015286');
|
|
21
|
+
});
|
|
22
|
+
test("an alignment row is the species, made OrthoFinder's prefix, and the id", () => {
|
|
23
|
+
expect(alignmentRowName('melanogaster', 'Dmel_CG2671')).toBe('melanogaster_Dmel_CG2671');
|
|
24
|
+
expect(alignmentRowName('Homo sapiens.v2', 'P1')).toBe('Homo_sapiens_v2_P1');
|
|
25
|
+
});
|
|
26
|
+
test('only a 403, 404 or absent local file reads as missing', () => {
|
|
27
|
+
expect(isMissingFile(new Error('HTTP 404 fetching https://x/a'))).toBe(true);
|
|
28
|
+
expect(isMissingFile(new Error('HTTP 403 fetching https://x/a'))).toBe(true);
|
|
29
|
+
expect(isMissingFile(new Error("ENOENT: no such file, open '/a'"))).toBe(true);
|
|
30
|
+
expect(isMissingFile(new Error('HTTP 503 fetching https://x/a'))).toBe(false);
|
|
31
|
+
expect(isMissingFile(new TypeError('Failed to fetch'))).toBe(false);
|
|
14
32
|
});
|
|
15
33
|
test('Resolved_Gene_Trees.txt splits on the first colon-space only', () => {
|
|
16
34
|
const trees = parseResolvedGeneTrees('OG0000001: (A_x:0.1,B_y:0.2)n0;\nOG0000002: (C:1,D:2);\n');
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{a as Ct}from"./chunk-MLATYHL2.js";import{F as Pt,a as B,c as ft,d as Y,e as Me,f as Ve,g as X,h as _,i as ht,j as Ae,k as Ce,l as Le,m as yt,n as vt,s as At,t as Qe,u as Ie,x as Be,z as Ft}from"./chunk-ET6J3BQX.js";import{g as K,h as dt,i as me,k as Ge,l as Ne,m as wt,n as ke,o as xt,p as Tt,r as Fe,s as Pe,t as Mt,v as Lt,w as Nt}from"./chunk-7WGSBPHS.js";import{a as pt,b as kt}from"./chunk-CRESEEY4.js";import"./chunk-K55ZVN73.js";import{a as gt,b as ce,c as St,d as bt}from"./chunk-DECSTFAT.js";import{a as y}from"./chunk-3KTCIF2L.js";import{e as We,f as j,n as Je,q as pe,r as fe}from"./chunk-BKGWP5O6.js";import{a as Ze}from"./chunk-YN7VVNZR.js";import{c as He}from"./chunk-QBRNXRNU.js";import"./chunk-73KAPCJ2.js";import{a as Re,b as Se,c as $e,d as te,e as et}from"./chunk-YOLY6H5C.js";import{a as u}from"./chunk-PFO3G4CM.js";import{B as F,D as ne,E as we,G as le,M as ut,N as xe,R as Te,S as M,a as tt,b as ot,c as rt,d as oe,g as O,h as nt,l as at,m as be,n as ye,o as st,q as ve,r as U,t as re,v as it,w as lt,x as mt,z as ct}from"./chunk-EXFKF6XR.js";import{b as Ue,c as ze,g as Ke,h as Ye,i as Xe,j as he,n as I,o as ge,p as d,r as e}from"./chunk-TEYYBCMF.js";import"./chunk-KVMUXFPB.js";var It=pe(fe("path",{d:"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6"}),"Settings");var So=u(function({children:t,...r}){return e.createElement("div",null,e.createElement(xe,{...r},t))}),b=So;var bo=y()({dialogContent:{width:"80em"}}),yo=u(function({error:t,children:r}){let{classes:n}=bo();return e.createElement(ye,{className:n.dialogContent},t?e.createElement(Se,{error:t}):null,r)}),$=yo;var vo=u(function({status:t}){return t==="loading"?e.createElement(te,{message:"Translating transcript"}):t==="missing"?e.createElement(M,{color:"textSecondary",variant:"body2"},"no coding sequence to align"):null}),z=vo;var Bt=ze(void 0),qt=u(function({children:t}){let r=d(()=>ce()==="splitRight");return e.createElement(Bt.Provider,{value:r},t)});function qe(){let o=Xe(Bt),t=d(()=>ce()==="splitRight");return o??t}var wo=u(function({checked:t,onChange:r}){return e.createElement(U,{label:"Open beside the genome view",control:e.createElement(nt,{checked:t,onChange:n=>{r(n.target.checked)}})})}),xo=u(function({onSubmit:t,onCancel:r,submitDisabled:n,hint:a,submitLabel:s="Submit",model:i}){let[m,p]=qe(),l=!!i&>(j(i));return e.createElement(be,{sx:{flexWrap:"wrap",rowGap:1}},l?e.createElement(wo,{checked:m,onChange:p}):null,e.createElement("div",{style:{display:"flex",alignItems:"center",gap:8,marginLeft:"auto"}},a,e.createElement(O,{sx:{flexShrink:0},color:"primary",variant:"contained",disabled:n,onClick:()=>{if(l){let h=m?"splitRight":"stack";St(h),t(h)}else t(ce())}},s),e.createElement(O,{sx:{flexShrink:0},color:"secondary",variant:"contained",onClick:()=>{r()}},"Cancel")))}),V=xo;var To=y()({textAreaFont:{fontFamily:"Courier New"}}),Mo=u(function({value:t}){let{classes:r}=To();return e.createElement(b,{variant:"outlined",multiline:!0,minRows:5,maxRows:10,fullWidth:!0,value:t,slotProps:{input:{readOnly:!0,classes:{input:r.textAreaFont}}}})}),Et=Mo;var Ao=y()({flex:{display:"flex"},minWidth:{minWidth:300},centered:{alignContent:"center",marginLeft:20}}),Co=u(function({feature:t,options:r,selectedId:n,selectedTranscript:a,setSelectedId:s,proteinSequence:i,sequenceStatus:m="ready",hasData:p}){let{classes:l}=Ao(),[h,g]=d(!1);return e.createElement(e.Fragment,null,e.createElement("div",{className:l.flex},e.createElement(xe,{variant:"outlined",label:`Choose isoform of ${_(t)}`,helperText:"the query row, so the alignment stays linked to the genome view",select:!0,className:l.minWidth,value:n,onChange:c=>{s(c.target.value)}},r.map(c=>{let f=p?p(c):!0,{len:T,mod:S}=ft(c);return e.createElement(F,{value:Y(c),key:c.id(),disabled:!f},X(c)," (",T," aa)"," ",S?" (possible fragment)":"",p?f?" (has data)":" (no data)":"")})),e.createElement("div",{className:l.centered},e.createElement(O,{variant:"contained",color:"primary",onClick:()=>{g(!h)}},h?"Hide sequence":"Show sequence"))),h?e.createElement(Et,{value:i?`>${X(a)}
|
|
2
|
+
${i}`:m==="missing"?"This transcript has no coding sequence, so there is nothing to translate.":m==="error"?"The sequence could not be fetched.":"Loading..."}):null)}),G=Co;var ae={drawNodeBubbles:!0,colWidth:10,rowHeight:12};function q({view:o,feature:t,placement:r,...n}){return bt(j(o),{placement:r,connectedViewId:o.id,connectedFeature:t?.toJSON(),...n})}function Q(o){let[t,r]=d(),n=ge(!1);return{launchError:t,submit:a=>{n.current||(n.current=!0,r(void 0),a().then(o,s=>{n.current=!1,console.error(s),r(s)}))}}}var Dt=o=>o==null||o===!1;function Lo(o){return Dt(o)||Array.isArray(o)&&o.some(Dt)?null:JSON.stringify(o)}function E(o,t,{onSuccess:r}={}){let n=Lo(o),[a,s]=d(0),[i,m]=d(),p=n===null?null:`${a}\0${n}`,l=i?.attempt===p,h=i?.question===n,g=ge({fetcher:t,onSuccess:r});return he(()=>{g.current={fetcher:t,onSuccess:r}}),he(()=>{if(p===null||n===null)return;let c=new AbortController;return g.current.fetcher(c.signal).then(f=>{c.signal.aborted||(m({attempt:p,question:n,data:f}),g.current.onSuccess?.(f))}).catch(f=>{c.signal.aborted||m({attempt:p,question:n,error:f})}),()=>{c.abort()}},[p,n]),{data:h?i.data:void 0,error:l?i.error:void 0,isLoading:p!==null&&!l,mutate:Ye(()=>{s(c=>c+1)},[])}}function Ee(o,t){let[r,n]=d(o);return he(()=>{let a=setTimeout(()=>{n(o)},t);return()=>{clearTimeout(a)}},[o,t]),r}function Ot({view:o,feature:t}){let r=o?.assemblyNames?.[0],{data:n,error:a,isLoading:s}=E(t&&r?[t.id(),r,"feature-sequence"]:null,async m=>{let{start:p,end:l,refName:h}=t.toJSON();return vt({start:p,end:l,refName:h,assemblyName:r,session:j(o),signal:m})});return{proteinSequence:I(()=>n&&t?yt({transcript:t,...n})?.protein??"":"",[n,t]),sequence:n,error:a,isLoading:s}}function No(o,t,r){if(!r)return o;let n=t.find(s=>Y(s)===o);if(n&&r(n))return o;let a=t.find(r);return a?Y(a):o}function ko({error:o,isLoading:t,proteinSequence:r}){return o?"error":t?"loading":r?"ready":"missing"}function H({feature:o,view:t,hasData:r,preferredTranscriptId:n}){let a=I(()=>Ae(o),[o]),[s,i]=d(()=>Y(a.find(c=>Me(c,n??""))??a[0])),m=No(s,a,r),p=a.find(c=>Y(c)===m),{proteinSequence:l,error:h,isLoading:g}=Ot({view:t,feature:p});return{options:a,selectedId:m,setSelectedId:i,selectedTranscript:p,proteinSequence:l,error:h,isLoading:g,sequenceStatus:ko({error:h,isLoading:g,proteinSequence:l}),hasData:r}}var jt=pe(fe("path",{d:"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z"}),"Delete");var Fo=y()({header:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:8},resultList:{maxHeight:300,overflow:"auto"}});function _t(o){let t=[o.geneName,o.transcriptName!==o.geneName?o.transcriptName:void 0].filter(r=>!!r);return t.length>0?t.join(" - "):o.geneId??o.transcriptId??"Unknown"}function Po(o){return[o.blastDatabase,o.searchProgram??o.blastProgram??"blastp",o.msaAlgorithm].filter(Boolean).join(" / ")}var Io=u(function({model:o,handleClose:t,feature:r,cached:{results:n,handleDelete:a,handleClearAll:s}}){let{classes:i}=Fo(),m=B(o),[p,l]=d(),[h]=qe(),g=c=>{let{transcriptId:f,msa:T,tree:S,treeMetadata:L}=c;q({view:m,feature:f?Ae(r).find(x=>Me(x,f)):void 0,placement:h?"splitRight":"stack",displayName:`BLAST - ${_t(c)}`,...ae,data:{msa:T,tree:S,treeMetadata:L}}).then(t,x=>{console.error(x),l(x)})};return e.createElement("div",null,p?e.createElement(Se,{error:p}):null,e.createElement("div",{className:i.header},e.createElement(M,{variant:"subtitle1"},"Cached BLAST Results (",n.length,")"),e.createElement(O,{size:"small",color:"error",onClick:async()=>{try{l(void 0),await s()}catch(c){l(c)}}},"Clear results for this gene")),e.createElement(it,{dense:!0,className:i.resultList},n.map(c=>e.createElement(lt,{key:c.id,disablePadding:!0,secondaryAction:e.createElement(re,{edge:"end",size:"small",onClick:async f=>{f.stopPropagation();try{l(void 0),await a(c.id)}catch(T){l(T)}}},e.createElement(jt,{fontSize:"small"}))},e.createElement(mt,{onClick:()=>{g(c)}},e.createElement(ct,{primary:`${_t(c)} - ${Po(c)}`,secondary:`${new Date(c.timestamp).toLocaleString()} - Seq: ${c.proteinSequence.slice(0,30)}...`}))))))}),$t=Io;var Bo=u(function({value:t,onChange:r,className:n}){return e.createElement(b,{variant:"outlined",label:"MSA Algorithm",className:n,select:!0,value:t,helperText:t==="browser"?"no EBI job; rows aligned to the query":void 0,onChange:a=>{r(a.target.value)}},Ne.map(a=>e.createElement(F,{value:a,key:a},wt[a])))}),De=Bo;var qo="msaView-blastSearch",Eo="msaView-msaAlgorithm",Vt=Fe("blastp"),Gt="clustalo";function Do(o){let{program:t,database:r}=o??{},n=ke.find(a=>a===t);return n&&Pe(n).some(a=>a===r)?{program:n,database:r}:Vt}function Oo(o){return Ne.find(t=>t===o)??Gt}function Qt(){let[o,t]=K(qo,Vt);return[Do(o),r=>{t(r)}]}function Oe(){let[o,t]=K(Eo,Gt);return[Oo(o),r=>{t(r)}]}function Ht(o){let{data:t,error:r,isLoading:n,mutate:a}=E(`cached-blast-${o.join(",")}`,async()=>(await At()).filter(i=>i.geneId&&o.includes(i.geneId)));return{results:t??[],error:r,isLoading:n,handleDelete:async s=>{await Qe(s),a()},handleClearAll:async()=>{await Promise.all((t??[]).map(s=>Qe(s.id))),a()}}}var jo=y()({selectField:{width:150},databaseField:{width:260},cachedResultsAccordion:{marginTop:20},infoText:{marginTop:20}}),_o=u(function({handleClose:o,feature:t,model:r,children:n,preferredTranscriptId:a}){let{classes:s}=jo(),i=B(r),{launchError:m,submit:p}=Q(o),[l,h]=Qt(),[g,c]=Oe(),[f,T]=d(String(Tt)),S=Number(f),L=Number.isInteger(S)&&S>=1,x=l.program==="phmmer",k=I(()=>Le(t),[t]),A=Ht(k),C=H({feature:t,view:i,preferredTranscriptId:a}),{selectedTranscript:v,proteinSequence:P,sequenceStatus:W}=C,N=C.error??m??A.error;return e.createElement(e.Fragment,null,e.createElement($,{error:N},n,e.createElement(b,{variant:"outlined",label:"Search program",className:s.selectField,select:!0,value:l.program,onChange:w=>{h(Fe(w.target.value))}},ke.map(w=>e.createElement(F,{value:w,key:w},xt[w]))),e.createElement(b,{variant:"outlined",label:"Database",className:s.databaseField,select:!0,value:l.database,onChange:w=>{h({program:l.program,database:w.target.value})}},Pe(l.program).map(w=>e.createElement(F,{value:w,key:w},Mt(w)))),x?null:e.createElement(De,{className:s.selectField,value:g,onChange:c}),e.createElement(b,{variant:"outlined",label:"Hits",className:s.selectField,type:"number",value:f,onChange:w=>{T(w.target.value)},error:!L,helperText:"best-scoring sequences to keep"}),e.createElement(G,{feature:t,...C}),e.createElement(M,{variant:"body2",className:s.infoText},x?"phmmer aligns the hits as it finds them, so nothing is realigned afterwards.":"The hits come back from EBI and are then run through the chosen aligner."," ","The EBI queue is the wait, and it runs from seconds to many minutes."),A.results.length>0?e.createElement(tt,{className:s.cachedResultsAccordion},e.createElement(rt,{expandIcon:e.createElement(pt,null)},e.createElement(M,null,"Previous BLAST Results")),e.createElement(ot,null,e.createElement($t,{model:r,handleClose:o,feature:t,cached:A}))):null),e.createElement(V,{model:r,hint:e.createElement(z,{status:W}),submitDisabled:!P||!L,onSubmit:w=>{if(v){let R=l.program==="phmmer"?{searchProgram:"phmmer",blastDatabase:l.database,maxHits:S,selectedTranscript:v.toJSON(),proteinSequence:P}:{searchProgram:"blastp",blastDatabase:l.database,msaAlgorithm:g,maxHits:S,selectedTranscript:v.toJSON(),proteinSequence:P};p(()=>q({view:i,feature:v,placement:w,displayName:ht(t,v),...ae,blastParams:R}))}},onCancel:o}))}),Wt=_o;function Ut(o){return Ce(o).toUpperCase()}function $o(o,t){let r=Math.min(o.length,t.length);if(r===0)return 0;let n=0;for(let a=0;a<r;a++)o[a]===t[a]&&n++;return n/r}var Vo=.9,Go=.5;function zt(o,t,r){if(!o.trim())return{names:[]};let n;try{n=Lt(o)}catch{return{names:[]}}let a=n.getNames(),s=Ut(t);return{names:a,match:s?Ho(n,a,s,r):void 0}}function Qo(o,t,r){let n=Ut(Nt(o.getRow(t)));if(n){if(n===r)return{name:t,quality:"exact",identity:1,offset:0};{let a=r.indexOf(n),s=n.indexOf(r);if(a!==-1||s!==-1){let i=Math.min(n.length,r.length)/Math.max(n.length,r.length);return i>=Go?{name:t,quality:"partial",identity:i,offset:a===-1?-s:a}:void 0}else{let i=$o(n,r);return i>=Vo?{name:t,quality:"similar",identity:i,offset:0}:void 0}}}else return}var Jt=["exact","partial","similar"];function Ho(o,t,r,n){return t.map(a=>Qo(o,a,r)).filter(a=>a!==void 0).sort((a,s)=>Jt.indexOf(a.quality)-Jt.indexOf(s.quality)||s.identity-a.identity||+(s.name===n)-+(a.name===n))[0]}function Kt(o,t){return o.querySeqName||(o.names.includes(t)?t:"")}function se(o,t,r){let[n,a]=d(),{names:s,match:i}=I(()=>zt(o,t,r),[o,t,r]),m=n!==void 0&&(s.length===0||s.includes(n))?n:void 0,p=m??i?.name??"";return{detected:i,names:s,querySeqName:p,querySeqOffset:p===i?.name?i.offset:0,setQuerySeqName:a,isAutoDetected:m===void 0&&!!i}}var Wo=y()({field:{marginTop:20},alert:{marginTop:10}});function Jo({identity:o,offset:t}){let r=`, covering ${Math.round(o*100)}% of it`;return t>0?`${r} from residue ${t+1}`:t<0?`${r} and running ${-t} residues past its start`:r}var Uo=u(function({names:t,detected:r,querySeqName:n,setQuerySeqName:a,isAutoDetected:s}){let{classes:i}=Wo();return e.createElement(e.Fragment,null,t.length>0?e.createElement(b,{variant:"outlined",label:"MSA row matching the selected transcript",select:!0,fullWidth:!0,className:i.field,value:t.includes(n)?n:"",onChange:m=>{a(m.target.value)}},t.map(m=>e.createElement(F,{value:m,key:m},m,r?.name===m?" \u2014 matches your protein":""))):e.createElement(b,{variant:"outlined",label:"MSA row matching the selected transcript",fullWidth:!0,className:i.field,helperText:"Paste an alignment above and this fills in on its own",value:n,onChange:m=>{a(m.target.value)}}),s&&r?r.quality==="similar"?e.createElement(oe,{severity:"warning",className:i.alert},e.createElement("strong",null,r.name)," is"," ",Math.round(r.identity*100),"% identical to your protein but is not the same sequence. Clicking the alignment navigates the genome view, approximately: wherever the two differ by an insertion or a deletion, every residue after it lands one codon off."):e.createElement(oe,{severity:"success",className:i.alert},"Matched ",e.createElement("strong",null,r.name)," to your protein sequence",r.quality==="exact"?"":Jo(r),". Clicking the alignment will navigate the genome view."):t.length>0&&!n?e.createElement(oe,{severity:"warning",className:i.alert},"No row matched your protein sequence \u2014 pick the one for your gene above. Without it the alignment still renders, but clicking it will not navigate the genome view."):null)}),ie=Uo;var zo=y()({ncbiLink:{wordBreak:"break-all"},textAreaFont:{fontFamily:"Courier New"},msaInput:{marginBottom:20},step:{marginTop:20},stepBody:{marginLeft:20,marginTop:8}}),Ko=u(function({handleClose:o,feature:t,model:r,children:n,preferredTranscriptId:a}){let{classes:s}=zo(),i=B(r),{launchError:m,submit:p}=Q(o),[l,h]=d(""),[g,c]=d(""),f=H({feature:t,view:i,preferredTranscriptId:a}),{proteinSequence:T,selectedTranscript:S,error:L}=f,x=se(l,T),k=Ce(T),A=`${Ge}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${k}`,C=`${Ge}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${Je(k,10)}`;return e.createElement(e.Fragment,null,e.createElement($,{error:m??L},n,e.createElement(G,{feature:t,...f}),e.createElement("div",{className:s.step},e.createElement(M,{variant:"subtitle2"},"1. Run the search at NCBI"),e.createElement("div",{className:s.stepBody},T?e.createElement("div",{className:s.ncbiLink},e.createElement(Ct,{href:A},C)):e.createElement(oe,{severity:"info"},"Pick a transcript above to get a link carrying its protein sequence."))),e.createElement("div",{className:s.step},e.createElement(M,{variant:"subtitle2"},"2. Align the hits"),e.createElement("div",{className:s.stepBody},e.createElement(M,null,`On the results page click "Multiple Alignment" to run COBALT, NCBI's aligner. Download the alignment (.aln) and, if you want the tree drawn, the Newick tree (.nh).`))),e.createElement("div",{className:s.step},e.createElement(M,{variant:"subtitle2"},"3. Paste the results back here"),e.createElement("div",{className:s.stepBody},e.createElement(b,{variant:"outlined",label:"Alignment",multiline:!0,minRows:5,maxRows:10,fullWidth:!0,className:s.msaInput,slotProps:{input:{className:s.textAreaFont}},placeholder:"Paste the .aln contents here",value:l,onChange:v=>{h(v.target.value)}}),e.createElement(b,{variant:"outlined",label:"Tree (optional)",multiline:!0,minRows:3,maxRows:10,fullWidth:!0,slotProps:{input:{className:s.textAreaFont}},placeholder:"Paste the .nh Newick tree here",value:g,onChange:v=>{c(v.target.value)}}),e.createElement(ie,{...x})))),e.createElement(V,{model:r,submitDisabled:!S||!l.trim(),onSubmit:v=>{S&&p(()=>q({view:i,feature:S,placement:v,displayName:_(S),querySeqName:x.querySeqName,querySeqOffset:x.querySeqOffset,data:{msa:l,tree:g}}))},onCancel:o}))}),Yt=Ko;var Yo=u(function({lookupMethod:t,setLookupMethod:r}){return e.createElement(ve,{component:"fieldset"},e.createElement(we,{row:!0,value:t,onChange:n=>{r(n.target.value)}},e.createElement(U,{value:"automatic",control:e.createElement(ne,null),label:"Automatic"}),e.createElement(U,{value:"manual",control:e.createElement(ne,null),label:"Manual"})))}),Xt=Yo;var Xo=y()({field:{minWidth:300},help:{marginBottom:8}}),Zo=u(function({handleClose:t,ebiEmail:r}){let{classes:n}=Xo(),[a,s]=d(r);return e.createElement(at,{open:!0,maxWidth:"lg",onClose:()=>{t()}},e.createElement(st,null,"EBI settings"),e.createElement(ye,null,e.createElement(M,{variant:"subtitle2",className:n.help},"Every search this tab runs is submitted to EBI with this address, which is how they reach whoever is generating the load. If your site sends real volume, use your own."),e.createElement(b,{autoFocus:!0,margin:"dense",label:"EBI contact email",fullWidth:!0,variant:"outlined",value:a,className:n.field,onChange:i=>{s(i.target.value)}}),e.createElement(O,{variant:"contained",onClick:()=>{s(me)}},"Reset")),e.createElement(be,null,e.createElement(O,{variant:"contained",color:"secondary",onClick:()=>{t()}},"Cancel"),e.createElement(O,{color:"primary",variant:"contained",onClick:()=>{t({ebiEmail:a})}},"Save")))}),Zt=Zo;var Ro=y()({settingsButton:{float:"right"}}),er={automatic:Wt,manual:Yt},tr=u(function({handleClose:t,model:r,feature:n,preferredTranscriptId:a}){let[s,i]=d("automatic"),[m,p]=K(dt,me),[l,h]=d(!1),{classes:g}=Ro(),c=er[s];return e.createElement(e.Fragment,null,e.createElement(Te,{title:`EBI settings \u2014 jobs are submitted with ${m.trim()||me}`},e.createElement(re,{className:g.settingsButton,"aria-label":"EBI settings",size:"small",onClick:()=>{h(!0)}},e.createElement(It,null))),e.createElement(c,{model:r,feature:n,handleClose:t,preferredTranscriptId:a},e.createElement(Xt,{lookupMethod:s,setLookupMethod:i})),l?e.createElement(Zt,{ebiEmail:m,handleClose:f=>{f&&p(f.ebiEmail),h(!1)}}):null)}),Rt=tr;var or=Ke(()=>import("./HelpDialog-3IR65NL3.js")),rr=u(function(){let[t,r]=d(!1);return e.createElement(e.Fragment,null,e.createElement(Te,{title:"What each tab does"},e.createElement(re,{"aria-label":"Help",onClick:()=>{r(!0)}},e.createElement(kt,null))),t?e.createElement(Ue,{fallback:null},e.createElement(or,{handleClose:()=>{r(!1)}})):null)}),eo=rr;function nr(o){let t=Ee(o,500),{data:r,error:n,isLoading:a}=E(t?[JSON.stringify(t),"msa-file-text"]:null,()=>Ze(t).readFile("utf8"));return{text:r??"",error:n,isLoading:a||t!==o}}var ar=y()({inputContainer:{marginBottom:30},fileContainer:{maxWidth:500},msaInput:{marginBottom:20}}),sr=u(function({model:t,feature:r,handleClose:n,preferredTranscriptId:a}){let s=B(t),{classes:i}=ar(),{launchError:m,submit:p}=Q(n),[l,h]=d("file"),[g,c]=d(""),[f,T]=d(""),[S,L]=d(),[x,k]=d(),A=H({feature:r,view:s,preferredTranscriptId:a}),{selectedTranscript:C,proteinSequence:v,error:P,sequenceStatus:W}=A,N=nr(l==="file"?S:void 0),w=se(l==="file"?N.text:g,v),R=m??P??N.error;return e.createElement(e.Fragment,null,e.createElement($,{error:R},e.createElement(ve,{component:"fieldset"},e.createElement(we,{row:!0,value:l,onChange:J=>{h(J.target.value)}},e.createElement(U,{value:"file",control:e.createElement(ne,null),label:"Open files"}),e.createElement(U,{value:"text",control:e.createElement(ne,null),label:"Paste text"}))),e.createElement("div",{className:i.inputContainer},l==="file"?e.createElement("div",{className:i.fileContainer},e.createElement($e,{name:"MSA File .aln (Clustal), .fa/.mfa (aligned FASTA), .stock (Stockholm), etc)",inline:!0,location:S,setLocation:L}),e.createElement($e,{name:"Tree file .nh (Newick) or .asn (NCBI COBALT ASN.1)",inline:!0,location:x,setLocation:k})):e.createElement(e.Fragment,null,e.createElement(b,{variant:"outlined",name:"MSA",multiline:!0,minRows:5,className:i.msaInput,maxRows:10,fullWidth:!0,placeholder:"Paste MSA here",value:g,onChange:J=>{c(J.target.value)}}),e.createElement(b,{variant:"outlined",name:"Tree",multiline:!0,minRows:5,maxRows:10,fullWidth:!0,placeholder:"Paste newick tree (optional)",value:f,onChange:J=>{T(J.target.value)}}))),e.createElement(G,{feature:r,...A}),e.createElement(ie,{...w})),e.createElement(V,{model:t,hint:e.createElement(z,{status:W}),submitDisabled:!C||l==="file"&&(!S||N.isLoading)||l==="text"&&!g.trim(),onSubmit:J=>{C&&p(()=>q({view:s,feature:C,placement:J,displayName:_(C),querySeqName:w.querySeqName,querySeqOffset:w.querySeqOffset,...l==="file"?{msaFilehandle:S,treeFilehandle:x}:{data:{msa:g,tree:f}}}))},onCancel:n}))}),to=sr;var ir="msaview-ortholog-source",oo={ncbi:"NCBI orthologs",panther:"PANTHER",uniref:"UniRef cluster"},ro=Object.keys(oo);function lr(o){return ro.find(t=>t===o)??"ncbi"}function no(){let[o,t]=K(ir,"ncbi");return[lr(o),r=>{t(r)}]}var mr={ncbi:"vertebrates and insects",panther:"also yeast, worm, fly and plants",uniref:"all of UniProtKB within 50% identity"},cr=u(function({value:t,onChange:r,className:n}){return e.createElement(b,{variant:"outlined",label:"Source",className:n,select:!0,value:t,helperText:mr[t],onChange:a=>{r(a.target.value)}},ro.map(a=>e.createElement(F,{value:a,key:a},oo[a])))}),ao=cr;async function so(o,t){let r=o.trim();if(!r)return;if(/^\d+$/.test(r))return Number(r);let a=(await Be(Ie("esearch",{db:"taxonomy",term:r,retmax:"1",retmode:"json"}),{signal:t})).esearchresult?.idlist?.[0];return a?Number(a):void 0}async function io(o,t){let r=o.trim();if(!r)return;let a=(await Be(Ie("esearch",{db:"assembly",term:r,retmax:"1",retmode:"json"}),{signal:t})).esearchresult?.idlist?.[0];if(!a)return;let i=(await Be(Ie("esummary",{db:"assembly",id:a,retmode:"json"}),{signal:t})).result?.[a],m=Number(i?.speciestaxid);return i?.speciesname&&m?{taxId:m,speciesName:i.speciesname}:void 0}async function ur(o,t){let r=await so(o,t);if(!r)throw new Error(`No NCBI taxon matches "${o}"`);let n=[],a=(await Pt([r],t,i=>{n.push(i)})).get(r);if(!a&&n.length>0)throw new Error(`NCBI did not answer for the name of taxon ${r}`,{cause:n[0]});let s=[a?.sciname,a?.commonName&&`(${a.commonName})`].filter(Boolean).join(" ");return{taxId:r,label:s||`taxon ${r}`}}var dr=u(function({value:t,assemblyName:r,onChange:n,className:a}){let[s,i]=d(),m=Ee(s,400),{data:p}=E(r&&s===void 0?[r,"assembly-species"]:null,f=>io(r,f),{onSuccess:f=>{f&&n(f.taxId)}}),{data:l,error:h}=E(m?.trim()?[m.trim(),"taxon"]:null,f=>ur(m,f),{onSuccess:({taxId:f})=>{n(f)}}),g=s??p?.speciesName??"human",c=s===void 0?p?.speciesName:l?.label;return e.createElement(b,{variant:"outlined",label:"Query species",className:a,value:g,onChange:f=>{i(f.target.value)},error:!!h,helperText:h?`${h}`:c??`the species this gene is from (taxon ${t})`})}),lo=dr;var pr=y()({selectField:{width:180}}),fr={ncbi:o=>`the ${o} closest species NCBI has`,panther:o=>`the ${o} closest species PANTHER has`,uniref:o=>`${o} rows, one per species, reviewed entries first`},hr=u(function({handleClose:o,feature:t,model:r,preferredTranscriptId:n}){let{classes:a}=pr(),s=B(r),{launchError:i,submit:m}=Q(o),[p,l]=d(9606),[h,g]=no(),[c,f]=Oe(),[T,S]=d(String(Ft)),L=I(()=>Le(t),[t]),x=H({feature:t,view:s,preferredTranscriptId:n}),{selectedTranscript:k,proteinSequence:A,sequenceStatus:C}=x,v=x.error??i,P=Number(T),W=Number.isInteger(P)&&P>=2;return e.createElement(e.Fragment,null,e.createElement($,{error:v},e.createElement(M,{variant:"body2"},"Precomputed orthologs, one gene per species, looked up rather than searched for. No BLAST job to queue, and with the in-browser aligner no job at all."),e.createElement("div",null,e.createElement(ao,{className:a.selectField,value:h,onChange:g}),e.createElement(lo,{className:a.selectField,value:p,assemblyName:s.assemblyNames[0],onChange:l}),e.createElement(De,{className:a.selectField,value:c,onChange:f}),e.createElement(b,{variant:"outlined",label:"Rows to align",className:a.selectField,type:"number",value:T,onChange:N=>{S(N.target.value)},error:!W,helperText:W?fr[h](P):"a whole number, 2 or more"})),e.createElement(G,{feature:t,...x})),e.createElement(V,{model:r,hint:e.createElement(z,{status:C}),submitDisabled:!A||!W,onSubmit:N=>{k&&m(()=>q({view:s,feature:k,placement:N,displayName:`Orthologs - ${_(t)} - ${X(k)}`,...ae,orthologParams:{taxId:p,source:h,maxSpecies:P,geneCandidates:L,msaAlgorithm:c,selectedTranscript:k.toJSON(),proteinSequence:A}}))},onCancel:o}))}),mo=hr;var Z=JBrowseExports["@jbrowse/core/data_adapters/dataAdapterCache"],gr=o=>Z!=null&&o in Object(Z)?Z[o]:Object.assign(function(){throw new Error("This JBrowse does not provide '"+o+"' from '@jbrowse/core/data_adapters/dataAdapterCache' to plugins: MsaView was built for a different JBrowse version, so update the plugin or JBrowse")},{jbrowseHostMissing:o});var co=gr("getAdapter");var hi=Z&&Z.__esModule?Z.default:Z;async function uo(o,t){return(await co(o,"msa",t)).dataAdapter}async function po({config:o,pluginManager:t,assemblyName:r}){return(await uo(t,o)).getMSAList(r)}async function fo({config:o,pluginManager:t,msaId:r,assemblyName:n}){return(await uo(t,o)).getMSA(r,n)}function je(o){return o.msa?He(o.msa,"datasets"):void 0}var Sr=y()({selectedContainer:{marginTop:50}}),br=u(function({model:o,feature:t,handleClose:r,preferredTranscriptId:n}){let a=j(o),s=B(o),{classes:i}=Sr(),{pluginManager:m}=We(o),{assemblyNames:p}=s,l=p[0],{launchError:h,submit:g}=Q(r),c=je(a.jbrowse),[f,T]=d(c?.[0]?.datasetId),S=c?.find(D=>D.datasetId===f),{data:L,isLoading:x,error:k}=E(S?`${S.datasetId}-${l}-msa-list`:null,()=>po({config:S.adapter,pluginManager:m,assemblyName:l})),A=I(()=>L&&new Set(L),[L]),C=H({feature:t,view:s,hasData:A?D=>!!Ve(D,t,A):void 0,preferredTranscriptId:n}),{selectedTranscript:v,proteinSequence:P,sequenceStatus:W}=C,N=v&&A?Ve(v,t,A):void 0,{data:w,isLoading:R,error:J}=E(N&&S?`${S.datasetId}-${l}-${N}-msa`:null,()=>fo({msaId:N,assemblyName:l,config:S.adapter,pluginManager:m})),ee=w?.msa??"",_e=se(ee,P,w?.queryRowName),de=Kt(_e,`${N??""}_${l??""}`),go=k??J??C.error??h;return e.createElement(e.Fragment,null,e.createElement($,{error:go},e.createElement(b,{select:!0,label:"Select MSA dataset",value:f,onChange:D=>{T(D.target.value)}},c?.map(D=>e.createElement(F,{key:D.datasetId,value:D.datasetId},D.name))),S?e.createElement("div",{className:i.selectedContainer},!x&&R?e.createElement(te,{variant:"h6",message:`Loading MSA for ${X(v)||N}`}):null,x?e.createElement(te,{variant:"h6",message:`Loading available MSAs for (${S.name})`}):null,L?e.createElement("div",null,e.createElement(et,{html:S.description}),e.createElement(G,{feature:t,...C}),ee?e.createElement(ie,{..._e,querySeqName:de}):null):null):null),e.createElement(V,{model:o,hint:R?e.createElement(te,{message:"Loading alignment"}):ee&&!de?e.createElement(M,{color:"textSecondary",variant:"body2"},"no row matches this transcript"):e.createElement(z,{status:W}),submitDisabled:!v||!ee||!de,onSubmit:D=>{v&&ee&&g(()=>q({view:s,feature:v,placement:D,displayName:_(v),drawNodeBubbles:!1,labelsAlignRight:!0,showBranchLen:!1,colWidth:10,rowHeight:12,colorSchemeName:"percent_identity_dynamic",querySeqName:de,querySeqOffset:_e.querySeqOffset,data:{msa:ee,tree:w?.tree}}))},onCancel:r}))}),ho=br;var yr=u(function({children:t,value:r,index:n}){let a=r===n,[s,i]=d(a);return a&&!s&&i(!0),e.createElement("div",{role:"tabpanel",hidden:!a},s||a?t:null)}),ue=yr;var vr=u(function({handleClose:t,feature:r,model:n,preferredTranscriptId:a}){let s=j(n),m=!!je(s.jbrowse)?.length,[p,l]=d("orthologs");return e.createElement(Re,{maxWidth:"xl",title:"Launch MSA view",open:!0,onClose:t},e.createElement(qt,null,e.createElement("div",{style:{display:"flex",alignItems:"center"}},e.createElement(ut,{value:p,onChange:(h,g)=>{l(g)}},e.createElement(le,{label:"Orthologs",value:"orthologs"}),e.createElement(le,{label:"BLAST query",value:"ncbi_blast"}),m?e.createElement(le,{label:"Pre-loaded MSA datasets",value:"preloaded_msa"}):null,e.createElement(le,{label:"Manual upload",value:"manual_msa"})),e.createElement(eo,null)),e.createElement(ue,{value:p,index:"orthologs"},e.createElement(mo,{handleClose:t,feature:r,model:n,preferredTranscriptId:a})),e.createElement(ue,{value:p,index:"ncbi_blast"},e.createElement(Rt,{handleClose:t,feature:r,model:n,preferredTranscriptId:a})),m?e.createElement(ue,{value:p,index:"preloaded_msa"},e.createElement(ho,{model:n,feature:r,handleClose:t,preferredTranscriptId:a})):null,e.createElement(ue,{value:p,index:"manual_msa"},e.createElement(to,{model:n,feature:r,handleClose:t,preferredTranscriptId:a}))))}),rl=vr;export{rl as default};
|
|
3
|
+
//# sourceMappingURL=LaunchMsaViewDialog-CZR76VIV.js.map
|