jbrowse-plugin-msaview 3.5.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/AddHighlightModel/index.js +3 -3
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +8 -2
- package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +3 -3
- package/dist/BgzipFastaMsaAdapter/configSchema.js +1 -1
- package/dist/LaunchMsaView/cleanProteinSequence.test.d.ts +1 -0
- package/dist/LaunchMsaView/cleanProteinSequence.test.js +77 -0
- package/dist/LaunchMsaView/codingFeature.d.ts +14 -0
- package/dist/LaunchMsaView/codingFeature.js +38 -0
- package/dist/LaunchMsaView/codingFeature.test.d.ts +1 -0
- package/dist/LaunchMsaView/codingFeature.test.js +75 -0
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +47 -55
- package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +16 -17
- package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.d.ts +7 -3
- package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.js +8 -7
- package/dist/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.js +2 -2
- package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.js +14 -13
- package/dist/LaunchMsaView/components/BlastQuery/consts.d.ts +2 -0
- package/dist/LaunchMsaView/components/BlastQuery/consts.js +28 -4
- package/dist/LaunchMsaView/components/HelpButton.d.ts +2 -0
- package/dist/LaunchMsaView/components/HelpButton.js +17 -0
- package/dist/LaunchMsaView/components/HelpDialog.d.ts +4 -0
- package/dist/LaunchMsaView/components/HelpDialog.js +18 -0
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +3 -1
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +21 -16
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.d.ts +3 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +34 -26
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +30 -26
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.d.ts +2 -1
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.js +18 -2
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.js +13 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +50 -27
- package/dist/LaunchMsaView/components/SequenceStatus.d.ts +14 -0
- package/dist/LaunchMsaView/components/SequenceStatus.js +15 -0
- package/dist/LaunchMsaView/components/SubmitCancelActions.d.ts +6 -2
- package/dist/LaunchMsaView/components/SubmitCancelActions.js +28 -13
- package/dist/LaunchMsaView/components/SubmitCancelActions.test.js +52 -4
- package/dist/LaunchMsaView/components/TabPanel.js +15 -3
- package/dist/LaunchMsaView/components/TabPanel.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/TabPanel.test.js +36 -0
- package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +3 -1
- package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -2
- package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +12 -2
- package/dist/LaunchMsaView/components/calculateProteinSequence.js +38 -23
- package/dist/LaunchMsaView/components/fetchSeq.d.ts +4 -1
- package/dist/LaunchMsaView/components/fetchSeq.js +14 -4
- package/dist/LaunchMsaView/components/launchConnectedView.d.ts +23 -0
- package/dist/LaunchMsaView/components/launchConnectedView.js +39 -0
- package/dist/LaunchMsaView/components/launchPlacement.d.ts +8 -0
- package/dist/LaunchMsaView/components/launchPlacement.js +25 -0
- package/dist/LaunchMsaView/components/useFeatureSequence.d.ts +2 -0
- package/dist/LaunchMsaView/components/useFeatureSequence.js +14 -11
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +10 -1
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +21 -3
- package/dist/LaunchMsaView/components/useTranscriptSelection.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.test.js +57 -0
- package/dist/LaunchMsaView/detectQueryRow.js +4 -7
- package/dist/LaunchMsaView/index.js +28 -9
- package/dist/LaunchMsaView/launchTarget.d.ts +36 -8
- package/dist/LaunchMsaView/launchTarget.js +23 -13
- package/dist/LaunchMsaView/launchTarget.test.js +80 -15
- package/dist/LaunchMsaView/useQueryRowName.d.ts +12 -0
- package/dist/LaunchMsaView/useQueryRowName.js +11 -0
- package/dist/LaunchMsaView/useQueryRowName.test.js +18 -1
- package/dist/LaunchMsaView/util.d.ts +6 -0
- package/dist/LaunchMsaView/util.js +12 -3
- package/dist/LaunchMsaViewExtensionPoint/index.test.js +15 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +6 -4
- package/dist/MsaViewPanel/afterCreateAutoruns.js +38 -14
- package/dist/MsaViewPanel/applyRegion.d.ts +23 -0
- package/dist/MsaViewPanel/applyRegion.js +19 -0
- package/dist/MsaViewPanel/applyRegion.test.d.ts +1 -0
- package/dist/MsaViewPanel/applyRegion.test.js +45 -0
- package/dist/MsaViewPanel/components/LaunchProgress.d.ts +4 -3
- package/dist/MsaViewPanel/components/LaunchProgress.js +10 -7
- package/dist/MsaViewPanel/components/MsaViewPanel.js +7 -3
- package/dist/MsaViewPanel/components/MsaViewPanel.test.js +24 -0
- package/dist/MsaViewPanel/doLaunchBlast.js +2 -1
- package/dist/MsaViewPanel/doLaunchBlast.test.js +12 -0
- package/dist/MsaViewPanel/doLaunchOrthologs.test.js +1 -1
- package/dist/MsaViewPanel/genomeToMSA.js +24 -6
- package/dist/MsaViewPanel/genomeToMSA.test.js +218 -44
- package/dist/MsaViewPanel/launchAutoruns.test.d.ts +1 -0
- package/dist/MsaViewPanel/launchAutoruns.test.js +36 -0
- package/dist/MsaViewPanel/model.d.ts +156 -61
- package/dist/MsaViewPanel/model.js +69 -3
- package/dist/MsaViewPanel/model.test.js +66 -0
- package/dist/MsaViewPanel/msaDataStore.d.ts +5 -3
- package/dist/MsaViewPanel/msaDataStore.js +3 -12
- package/dist/MsaViewPanel/msaDataStore.test.js +8 -1
- package/dist/MsaViewPanel/observeProteinHighlights.test.js +4 -4
- package/dist/MsaViewPanel/regionSnapshot.test.d.ts +1 -0
- package/dist/MsaViewPanel/regionSnapshot.test.js +29 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.d.ts +41 -4
- package/dist/MsaViewPanel/resolveConnectedTranscript.js +109 -68
- package/dist/MsaViewPanel/resolveConnectedTranscript.test.d.ts +1 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.test.js +149 -0
- package/dist/MsaViewPanel/runLaunch.d.ts +8 -4
- package/dist/MsaViewPanel/runLaunch.js +15 -4
- package/dist/MsaViewPanel/runLaunch.test.js +16 -0
- package/dist/MsaViewPanel/storedData.test.js +43 -0
- package/dist/MsaViewPanel/syncGenomeHoverToMsaColumn.test.js +15 -7
- package/dist/MsaViewPanel/util.d.ts +23 -23
- package/dist/MsaViewPanel/util.js +23 -24
- package/dist/index.d.ts +11 -11
- package/dist/index.js +17 -10
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +54 -67
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/blastCache.d.ts +26 -2
- package/dist/utils/blastCache.js +51 -39
- package/dist/utils/blastCache.test.js +46 -19
- package/dist/utils/domainCache.d.ts +1 -1
- package/dist/utils/domainCache.js +18 -14
- package/dist/utils/ebiJobDispatcher.d.ts +11 -0
- package/dist/utils/ebiJobDispatcher.js +12 -13
- package/dist/utils/ebiJobDispatcher.test.js +17 -3
- package/dist/utils/eutils.d.ts +14 -0
- package/dist/utils/eutils.js +48 -14
- package/dist/utils/eutils.test.d.ts +1 -0
- package/dist/utils/eutils.test.js +31 -0
- package/dist/utils/idb.d.ts +16 -3
- package/dist/utils/idb.js +51 -5
- package/dist/utils/idb.test.d.ts +1 -0
- package/dist/utils/idb.test.js +78 -0
- package/dist/utils/ncbiDomains.js +4 -4
- package/dist/utils/ncbiOrthologs.d.ts +1 -1
- package/dist/utils/ncbiOrthologs.js +18 -17
- package/dist/utils/ncbiOrthologs.test.js +17 -1
- package/dist/utils/ncbiTaxonomy.js +14 -15
- package/dist/utils/taxonomyNames.js +23 -19
- package/dist/utils/taxonomyNames.test.d.ts +1 -0
- package/dist/utils/taxonomyNames.test.js +33 -0
- package/dist/utils/unirefHomologs.js +2 -2
- package/dist/utils/unirefHomologs.test.js +8 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +23 -23
- package/src/AddHighlightModel/index.tsx +1 -1
- package/src/BgzipFastaMsaAdapter/configSchema.ts +1 -1
- package/src/LaunchMsaView/cleanProteinSequence.test.ts +91 -0
- package/src/LaunchMsaView/codingFeature.test.ts +96 -0
- package/src/LaunchMsaView/codingFeature.ts +49 -0
- package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +60 -56
- package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +18 -15
- package/src/LaunchMsaView/components/BlastQuery/BlastPanel.tsx +24 -11
- package/src/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.tsx +4 -4
- package/src/LaunchMsaView/components/BlastQuery/CachedBlastResults.tsx +16 -15
- package/src/LaunchMsaView/components/BlastQuery/consts.ts +31 -4
- package/src/LaunchMsaView/components/HelpButton.tsx +33 -0
- package/src/LaunchMsaView/components/HelpDialog.tsx +78 -0
- package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +54 -34
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +46 -23
- package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +41 -28
- package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.ts +16 -0
- package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.tsx +24 -2
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +81 -25
- package/src/LaunchMsaView/components/SequenceStatus.tsx +29 -0
- package/src/LaunchMsaView/components/SubmitCancelActions.test.tsx +88 -4
- package/src/LaunchMsaView/components/SubmitCancelActions.tsx +43 -16
- package/src/LaunchMsaView/components/TabPanel.test.tsx +51 -0
- package/src/LaunchMsaView/components/TabPanel.tsx +16 -4
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +6 -1
- package/src/LaunchMsaView/components/calculateProteinSequence.ts +51 -22
- package/src/LaunchMsaView/components/fetchSeq.ts +25 -5
- package/src/LaunchMsaView/components/launchConnectedView.ts +56 -0
- package/src/LaunchMsaView/components/launchPlacement.tsx +41 -0
- package/src/LaunchMsaView/components/useFeatureSequence.ts +18 -11
- package/src/LaunchMsaView/components/useTranscriptSelection.test.tsx +66 -0
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +41 -2
- package/src/LaunchMsaView/detectQueryRow.ts +5 -7
- package/src/LaunchMsaView/index.ts +32 -13
- package/src/LaunchMsaView/launchTarget.test.ts +88 -15
- package/src/LaunchMsaView/launchTarget.ts +49 -14
- package/src/LaunchMsaView/useQueryRowName.test.ts +28 -1
- package/src/LaunchMsaView/useQueryRowName.ts +17 -0
- package/src/LaunchMsaView/util.ts +15 -5
- package/src/LaunchMsaViewExtensionPoint/index.test.ts +16 -0
- package/src/LaunchMsaViewExtensionPoint/index.ts +28 -1
- package/src/MsaViewPanel/afterCreateAutoruns.ts +46 -14
- package/src/MsaViewPanel/applyRegion.test.ts +55 -0
- package/src/MsaViewPanel/applyRegion.ts +33 -0
- package/src/MsaViewPanel/components/LaunchProgress.tsx +12 -5
- package/src/MsaViewPanel/components/MsaViewPanel.test.tsx +26 -0
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +7 -5
- package/src/MsaViewPanel/doLaunchBlast.test.ts +20 -1
- package/src/MsaViewPanel/doLaunchBlast.ts +2 -1
- package/src/MsaViewPanel/doLaunchOrthologs.test.ts +1 -1
- package/src/MsaViewPanel/genomeToMSA.test.ts +255 -59
- package/src/MsaViewPanel/genomeToMSA.ts +30 -5
- package/src/MsaViewPanel/launchAutoruns.test.ts +45 -0
- package/src/MsaViewPanel/model.test.ts +75 -0
- package/src/MsaViewPanel/model.ts +76 -3
- package/src/MsaViewPanel/msaDataStore.test.ts +14 -2
- package/src/MsaViewPanel/msaDataStore.ts +4 -12
- package/src/MsaViewPanel/observeProteinHighlights.test.ts +4 -4
- package/src/MsaViewPanel/regionSnapshot.test.ts +34 -0
- package/src/MsaViewPanel/resolveConnectedTranscript.test.ts +183 -0
- package/src/MsaViewPanel/resolveConnectedTranscript.ts +160 -67
- package/src/MsaViewPanel/runLaunch.test.ts +19 -0
- package/src/MsaViewPanel/runLaunch.ts +21 -4
- package/src/MsaViewPanel/storedData.test.ts +58 -0
- package/src/MsaViewPanel/syncGenomeHoverToMsaColumn.test.ts +17 -7
- package/src/MsaViewPanel/util.ts +38 -29
- package/src/index.ts +19 -10
- package/src/utils/blastCache.test.ts +59 -20
- package/src/utils/blastCache.ts +62 -39
- package/src/utils/domainCache.ts +27 -15
- package/src/utils/ebiJobDispatcher.test.ts +18 -3
- package/src/utils/ebiJobDispatcher.ts +12 -16
- package/src/utils/eutils.test.ts +39 -0
- package/src/utils/eutils.ts +64 -14
- package/src/utils/idb.test.ts +106 -0
- package/src/utils/idb.ts +57 -5
- package/src/utils/ncbiDomains.ts +4 -4
- package/src/utils/ncbiOrthologs.test.ts +20 -0
- package/src/utils/ncbiOrthologs.ts +26 -28
- package/src/utils/ncbiTaxonomy.ts +17 -20
- package/src/utils/taxonomyNames.test.ts +41 -0
- package/src/utils/taxonomyNames.ts +33 -19
- package/src/utils/unirefHomologs.test.ts +11 -1
- package/src/utils/unirefHomologs.ts +2 -2
- package/src/version.ts +1 -1
- package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.d.ts +0 -16
- package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.js +0 -31
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +0 -16
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +0 -16
- package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.d.ts +0 -9
- package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.js +0 -15
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +0 -11
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +0 -21
- package/dist/LaunchMsaView/components/geneticCodes.d.ts +0 -15
- package/dist/LaunchMsaView/components/geneticCodes.js +0 -227
- package/src/LaunchMsaView/components/BlastQuery/blastLaunchView.ts +0 -59
- package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +0 -43
- package/src/LaunchMsaView/components/OrthologQuery/orthologLaunchView.ts +0 -31
- package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +0 -38
- package/src/LaunchMsaView/components/geneticCodes.ts +0 -298
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { getConf } from '@jbrowse/core/configuration'
|
|
2
2
|
import { getSession } from '@jbrowse/core/util'
|
|
3
|
+
import { isAlive } from '@jbrowse/mobx-state-tree'
|
|
4
|
+
import { transaction, untracked } from 'mobx'
|
|
3
5
|
|
|
4
6
|
import { getProteinSequenceFromFeature } from '../LaunchMsaView/components/calculateProteinSequence'
|
|
5
7
|
import { fetchSeq } from '../LaunchMsaView/components/fetchSeq'
|
|
6
8
|
import { getTranscriptFeatures } from '../LaunchMsaView/util'
|
|
7
9
|
|
|
8
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
BlastParams,
|
|
12
|
+
JBrowsePluginMsaViewModel,
|
|
13
|
+
OrthologParams,
|
|
14
|
+
} from './model'
|
|
9
15
|
import type { Feature } from '@jbrowse/core/util'
|
|
10
16
|
|
|
11
17
|
// The short form of a connected launch. A spec that says
|
|
@@ -14,15 +20,26 @@ import type { Feature } from '@jbrowse/core/util'
|
|
|
14
20
|
//
|
|
15
21
|
// gets its `connectedFeature` -- the exon model every genome<->column mapping
|
|
16
22
|
// runs through -- looked up here, from the gene tracks the connected genome
|
|
17
|
-
// view has open, and its query row translated from that transcript.
|
|
18
|
-
// this the spec had to carry the transcript's CDS list itself, ~1.5kB of
|
|
19
|
-
// coordinates a person cannot type and a tutorial cannot show.
|
|
23
|
+
// view has open, and its query row translated from that transcript.
|
|
20
24
|
//
|
|
21
25
|
// jbrowse-plugin-protein3d does the same for its `transcriptId` shorthand,
|
|
22
26
|
// against a `connectedView` spec of its own; here the genome view already
|
|
23
27
|
// exists (the spec pinned its id), so its tracks and regions are read off the
|
|
24
28
|
// live model instead.
|
|
25
29
|
|
|
30
|
+
interface Region {
|
|
31
|
+
assemblyName: string
|
|
32
|
+
refName: string
|
|
33
|
+
start: number
|
|
34
|
+
end: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface GenomeView {
|
|
38
|
+
tracks: { type: string }[]
|
|
39
|
+
displayedRegions: Region[]
|
|
40
|
+
dynamicBlocks?: { contentBlocks?: Region[] }
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
function stripVersion(id: string) {
|
|
27
44
|
return id.replace(/\.\d+$/, '')
|
|
28
45
|
}
|
|
@@ -37,6 +54,35 @@ function transcriptMatches(transcript: Feature, id: string) {
|
|
|
37
54
|
].some(c => typeof c === 'string' && (c === id || stripVersion(c) === target))
|
|
38
55
|
}
|
|
39
56
|
|
|
57
|
+
function plainRegions(regions: Region[]) {
|
|
58
|
+
return regions.map(r => ({
|
|
59
|
+
assemblyName: r.assemblyName,
|
|
60
|
+
refName: r.refName,
|
|
61
|
+
start: Math.floor(r.start),
|
|
62
|
+
end: Math.ceil(r.end),
|
|
63
|
+
}))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The part of the genome on screen, then everything the view can scroll to.
|
|
68
|
+
* `displayedRegions` is usually whole chromosomes -- navigating to a locus
|
|
69
|
+
* displays its parent region -- so it is the fallback, not the first ask.
|
|
70
|
+
*/
|
|
71
|
+
export function searchWindows(view: GenomeView) {
|
|
72
|
+
const visible = plainRegions(view.dynamicBlocks?.contentBlocks ?? [])
|
|
73
|
+
const displayed = plainRegions(view.displayedRegions)
|
|
74
|
+
return visible.length > 0 ? [visible, displayed] : [displayed]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Gene models live on feature tracks. Asking an alignments, variant or
|
|
79
|
+
* quantitative track for a chromosome's worth of features to look for a
|
|
80
|
+
* transcript among them is the slowest possible way to find nothing.
|
|
81
|
+
*/
|
|
82
|
+
export function transcriptTracks<T extends { type: string }>(tracks: T[]) {
|
|
83
|
+
return tracks.filter(t => t.type === 'FeatureTrack')
|
|
84
|
+
}
|
|
85
|
+
|
|
40
86
|
async function findTranscript(
|
|
41
87
|
self: JBrowsePluginMsaViewModel,
|
|
42
88
|
transcriptId: string,
|
|
@@ -44,24 +90,27 @@ async function findTranscript(
|
|
|
44
90
|
const view = self.connectedView!
|
|
45
91
|
const session = getSession(self)
|
|
46
92
|
const sessionId = 'msaview-connectedTranscript'
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const hit = getTranscriptFeatures(feat).find(t =>
|
|
61
|
-
transcriptMatches(t, transcriptId),
|
|
93
|
+
const tracks = transcriptTracks(view.tracks)
|
|
94
|
+
for (const regions of searchWindows(view)) {
|
|
95
|
+
for (const track of tracks) {
|
|
96
|
+
// a named object keeps sessionId, which v4 hosts read from the args
|
|
97
|
+
const args = {
|
|
98
|
+
adapterConfig: getConf(track, 'adapter'),
|
|
99
|
+
sessionId,
|
|
100
|
+
regions,
|
|
101
|
+
}
|
|
102
|
+
const feats = await session.rpcManager.call(
|
|
103
|
+
sessionId,
|
|
104
|
+
'CoreGetFeatures',
|
|
105
|
+
args,
|
|
62
106
|
)
|
|
63
|
-
|
|
64
|
-
|
|
107
|
+
for (const feat of feats) {
|
|
108
|
+
const hit = getTranscriptFeatures(feat).find(t =>
|
|
109
|
+
transcriptMatches(t, transcriptId),
|
|
110
|
+
)
|
|
111
|
+
if (hit) {
|
|
112
|
+
return hit
|
|
113
|
+
}
|
|
65
114
|
}
|
|
66
115
|
}
|
|
67
116
|
}
|
|
@@ -75,57 +124,59 @@ async function translate(self: JBrowsePluginMsaViewModel, transcript: Feature) {
|
|
|
75
124
|
refName: string
|
|
76
125
|
}
|
|
77
126
|
const assemblyName = self.connectedView!.assemblyNames[0]!
|
|
78
|
-
const seq = await fetchSeq({
|
|
127
|
+
const { seq, assemblyGeneticCodeId } = await fetchSeq({
|
|
79
128
|
start,
|
|
80
129
|
end,
|
|
81
130
|
refName,
|
|
82
131
|
assemblyName,
|
|
83
132
|
session: getSession(self),
|
|
84
133
|
})
|
|
85
|
-
return getProteinSequenceFromFeature({
|
|
134
|
+
return getProteinSequenceFromFeature({
|
|
135
|
+
seq,
|
|
136
|
+
feature: transcript,
|
|
137
|
+
assemblyGeneticCodeId,
|
|
138
|
+
})
|
|
86
139
|
}
|
|
87
140
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`Looking up ${connectedTranscript} in the genome view...`,
|
|
141
|
+
/** the latest lookup per view; an older one finishing late writes nothing */
|
|
142
|
+
const latestLookup = new WeakMap<object, number>()
|
|
143
|
+
|
|
144
|
+
async function lookUp({
|
|
145
|
+
self,
|
|
146
|
+
transcriptId,
|
|
147
|
+
blastParams,
|
|
148
|
+
orthologParams,
|
|
149
|
+
isCurrent,
|
|
150
|
+
}: {
|
|
151
|
+
self: JBrowsePluginMsaViewModel
|
|
152
|
+
transcriptId: string
|
|
153
|
+
blastParams: BlastParams | undefined
|
|
154
|
+
orthologParams: OrthologParams | undefined
|
|
155
|
+
isCurrent: () => boolean
|
|
156
|
+
}) {
|
|
157
|
+
const trackCount = transcriptTracks(self.connectedView!.tracks).length
|
|
158
|
+
try {
|
|
159
|
+
self.setProgress(`Looking up ${transcriptId} in the genome view...`)
|
|
160
|
+
const transcript = await findTranscript(self, transcriptId)
|
|
161
|
+
if (!transcript) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`Transcript "${transcriptId}" was not found in the ${trackCount} feature track(s) open in the connected genome view. Open the gene track that carries it, and make sure the view is on its locus.`,
|
|
112
164
|
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const { blastParams, orthologParams } = self
|
|
165
|
+
}
|
|
166
|
+
const proteinSequence = await translate(self, transcript)
|
|
167
|
+
if (!proteinSequence) {
|
|
168
|
+
throw new Error(`Transcript "${transcriptId}" has no CDS to translate`)
|
|
169
|
+
}
|
|
170
|
+
if (!isCurrent()) {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
// toJSON, not the Feature: the params are frozen snapshot properties, so
|
|
174
|
+
// an instance in one is gone the moment the session reloads
|
|
175
|
+
const transcriptJson = transcript.toJSON()
|
|
176
|
+
// one transaction, so the launchers wake once, on a request that already
|
|
177
|
+
// carries its query
|
|
178
|
+
transaction(() => {
|
|
179
|
+
self.setProgress('')
|
|
129
180
|
if (blastParams && !blastParams.proteinSequence) {
|
|
130
181
|
self.setBlastParams({
|
|
131
182
|
...blastParams,
|
|
@@ -140,11 +191,53 @@ export function resolveConnectedTranscriptIfNeeded(
|
|
|
140
191
|
proteinSequence,
|
|
141
192
|
})
|
|
142
193
|
}
|
|
143
|
-
// last, because the launchers wake on it
|
|
144
194
|
self.setConnectedFeature(transcriptJson)
|
|
145
|
-
}
|
|
146
|
-
|
|
195
|
+
})
|
|
196
|
+
} catch (e) {
|
|
197
|
+
console.error(e)
|
|
198
|
+
if (isCurrent()) {
|
|
199
|
+
self.setProgress('')
|
|
147
200
|
self.setError(e)
|
|
148
201
|
}
|
|
149
|
-
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Resolve `connectedTranscript` into `connectedFeature` once the connected
|
|
207
|
+
* genome view has its assembly and tracks, then hand the translation to
|
|
208
|
+
* whichever launch is waiting for a query.
|
|
209
|
+
*
|
|
210
|
+
* The autorun re-fires on the view's readiness (a track opened after a miss)
|
|
211
|
+
* and on the request itself, which is what `retryLaunch` re-states. The lookup
|
|
212
|
+
* runs untracked, so scrolling the genome view is not a reason to look again.
|
|
213
|
+
*/
|
|
214
|
+
export function resolveConnectedTranscriptIfNeeded(
|
|
215
|
+
self: JBrowsePluginMsaViewModel,
|
|
216
|
+
) {
|
|
217
|
+
const {
|
|
218
|
+
connectedTranscript,
|
|
219
|
+
connectedFeature,
|
|
220
|
+
connectedView,
|
|
221
|
+
blastParams,
|
|
222
|
+
orthologParams,
|
|
223
|
+
} = self
|
|
224
|
+
if (
|
|
225
|
+
!connectedTranscript ||
|
|
226
|
+
connectedFeature ||
|
|
227
|
+
!connectedView?.initialized ||
|
|
228
|
+
connectedView.tracks.length === 0
|
|
229
|
+
) {
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
const lookup = (latestLookup.get(self) ?? 0) + 1
|
|
233
|
+
latestLookup.set(self, lookup)
|
|
234
|
+
untracked(() => {
|
|
235
|
+
void lookUp({
|
|
236
|
+
self,
|
|
237
|
+
transcriptId: connectedTranscript,
|
|
238
|
+
blastParams,
|
|
239
|
+
orthologParams,
|
|
240
|
+
isCurrent: () => isAlive(self) && latestLookup.get(self) === lookup,
|
|
241
|
+
})
|
|
242
|
+
})
|
|
150
243
|
}
|
|
@@ -137,6 +137,25 @@ describe('a launch that is abandoned', () => {
|
|
|
137
137
|
})
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
+
describe('a launch started while another holds the controller', () => {
|
|
141
|
+
test('aborts the first, so nothing is left polling that Cancel cannot reach', async () => {
|
|
142
|
+
const { model } = makeModel()
|
|
143
|
+
const signals: AbortSignal[] = []
|
|
144
|
+
const launch = (scope: LaunchScope) => {
|
|
145
|
+
signals.push(scope.signal)
|
|
146
|
+
return new Promise<typeof DATA>(() => {})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
runLaunch({ self: model, message: 'a', onLaunched: () => {}, launch })
|
|
150
|
+
runLaunch({ self: model, message: 'b', onLaunched: () => {}, launch })
|
|
151
|
+
await settle()
|
|
152
|
+
|
|
153
|
+
expect(signals.map(s => s.aborted)).toEqual([true, false])
|
|
154
|
+
model.launchController!.abort()
|
|
155
|
+
expect(signals[1]!.aborted).toBe(true)
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
140
159
|
describe('a launch that fails', () => {
|
|
141
160
|
test('records the error and stops the spinner', async () => {
|
|
142
161
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSession } from '@jbrowse/core/util'
|
|
2
|
+
import { untracked } from 'mobx'
|
|
2
3
|
|
|
3
4
|
import { isAbortError } from '../utils/fetch'
|
|
4
5
|
|
|
@@ -64,11 +65,26 @@ interface LaunchedData {
|
|
|
64
65
|
* Run one launch attempt, owning the AbortController that ties it to the view.
|
|
65
66
|
*
|
|
66
67
|
* The controller lives on the model because two things end a launch and neither
|
|
67
|
-
* is here: the Cancel button, and the disposer `afterCreate` registers.
|
|
68
|
-
* it
|
|
69
|
-
*
|
|
68
|
+
* is here: the Cancel button, and the disposer `afterCreate` registers. A launch
|
|
69
|
+
* already holding it is aborted first, so a re-fired autorun replaces the
|
|
70
|
+
* attempt rather than orphaning one that nothing can cancel.
|
|
71
|
+
*
|
|
72
|
+
* Runs untracked: it is called from the launch autoruns, and whatever the
|
|
73
|
+
* launch body reads before its first await would otherwise become a reason to
|
|
74
|
+
* launch again.
|
|
70
75
|
*/
|
|
71
|
-
export function runLaunch({
|
|
76
|
+
export function runLaunch(args: {
|
|
77
|
+
self: JBrowsePluginMsaViewModel
|
|
78
|
+
message: string
|
|
79
|
+
launch: (scope: LaunchScope) => Promise<LaunchedData>
|
|
80
|
+
onLaunched: () => void
|
|
81
|
+
}) {
|
|
82
|
+
untracked(() => {
|
|
83
|
+
startLaunch(args)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function startLaunch({
|
|
72
88
|
self,
|
|
73
89
|
message,
|
|
74
90
|
launch,
|
|
@@ -79,6 +95,7 @@ export function runLaunch({
|
|
|
79
95
|
launch: (scope: LaunchScope) => Promise<LaunchedData>
|
|
80
96
|
onLaunched: () => void
|
|
81
97
|
}) {
|
|
98
|
+
self.launchController?.abort()
|
|
82
99
|
const controller = new AbortController()
|
|
83
100
|
const { signal } = controller
|
|
84
101
|
self.setLaunchController(controller)
|
|
@@ -61,6 +61,9 @@ function makeModel(over: Record<string, unknown> = {}) {
|
|
|
61
61
|
setTreeMetadata(arg: string) {
|
|
62
62
|
model.data.treeMetadata = arg
|
|
63
63
|
},
|
|
64
|
+
setGFF(arg: string) {
|
|
65
|
+
model.data.gff = arg
|
|
66
|
+
},
|
|
64
67
|
...over,
|
|
65
68
|
}
|
|
66
69
|
return model as typeof model & JBrowsePluginMsaViewModel
|
|
@@ -100,6 +103,17 @@ describe('restoring a view from IndexedDB', () => {
|
|
|
100
103
|
expect(mockStore).not.toHaveBeenCalled()
|
|
101
104
|
})
|
|
102
105
|
|
|
106
|
+
// react-msaview drops a GFF over 50kB from the snapshot too
|
|
107
|
+
test('a stored GFF comes back with the alignment', async () => {
|
|
108
|
+
mockRetrieve.mockResolvedValue({ msa: MSA, gff: 'a\t.\tdomain\t1\t2' })
|
|
109
|
+
const model = makeModel({ dataStoreId: 'msa-1' })
|
|
110
|
+
|
|
111
|
+
loadStoredData(model)
|
|
112
|
+
await settle()
|
|
113
|
+
|
|
114
|
+
expect(model.data.gff).toBe('a\t.\tdomain\t1\t2')
|
|
115
|
+
})
|
|
116
|
+
|
|
103
117
|
// the row expires or the user clears site data, and the view used to reopen as
|
|
104
118
|
// a blank import form with no hint that anything had been lost
|
|
105
119
|
test('a row that is gone says so, in words that name the policy', async () => {
|
|
@@ -115,6 +129,32 @@ describe('restoring a view from IndexedDB', () => {
|
|
|
115
129
|
// to import form" lands straight back on this error
|
|
116
130
|
expect(model.dataStoreId).toBeUndefined()
|
|
117
131
|
})
|
|
132
|
+
|
|
133
|
+
// the request that built the alignment is kept past the launch, so the view
|
|
134
|
+
// can offer to run it again instead of sending the user back to the gene
|
|
135
|
+
test('a row that is gone points at Retry when the search is still known', async () => {
|
|
136
|
+
mockRetrieve.mockResolvedValue(undefined)
|
|
137
|
+
const model = makeModel({
|
|
138
|
+
dataStoreId: 'msa-1',
|
|
139
|
+
blastParams: { blastDatabase: 'uniprotkb_swissprot' },
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
loadStoredData(model)
|
|
143
|
+
await settle()
|
|
144
|
+
|
|
145
|
+
expect((model.error as Error).message).toMatch(/Retry/)
|
|
146
|
+
expect(model.blastParams).toBeDefined()
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('a row that is gone with no search behind it says to relaunch', async () => {
|
|
150
|
+
mockRetrieve.mockResolvedValue(undefined)
|
|
151
|
+
const model = makeModel({ dataStoreId: 'msa-1' })
|
|
152
|
+
|
|
153
|
+
loadStoredData(model)
|
|
154
|
+
await settle()
|
|
155
|
+
|
|
156
|
+
expect((model.error as Error).message).toMatch(/Relaunch it from the gene/)
|
|
157
|
+
})
|
|
118
158
|
})
|
|
119
159
|
|
|
120
160
|
describe('keeping IndexedDB up to date', () => {
|
|
@@ -155,6 +195,24 @@ describe('keeping IndexedDB up to date', () => {
|
|
|
155
195
|
expect(model.dataStoreId).toBe('msa-1')
|
|
156
196
|
})
|
|
157
197
|
|
|
198
|
+
test('a GFF loaded after the first write updates the row', async () => {
|
|
199
|
+
const model = makeModel({
|
|
200
|
+
dataStoreId: 'msa-1',
|
|
201
|
+
rows: [['a', 'MK']],
|
|
202
|
+
data: { msa: MSA },
|
|
203
|
+
lastStoredData: { msa: MSA },
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
model.data.gff = 'a\t.\tdomain\t1\t2'
|
|
207
|
+
storeDataToIndexedDB(model)
|
|
208
|
+
await settle()
|
|
209
|
+
|
|
210
|
+
expect(mockStore).toHaveBeenCalledWith(
|
|
211
|
+
'msa-1',
|
|
212
|
+
expect.objectContaining({ gff: 'a\t.\tdomain\t1\t2' }),
|
|
213
|
+
)
|
|
214
|
+
})
|
|
215
|
+
|
|
158
216
|
// a browser that refuses IndexedDB (private mode) answers every write the same
|
|
159
217
|
// way, and the autorun reruns on its own isStoringData flag -- so a write that
|
|
160
218
|
// is not recorded as attempted is a write that retries forever
|
|
@@ -22,17 +22,18 @@ const mafRegion = {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// A model wired through the real genomeToMSA path: a connected genome view
|
|
25
|
-
// over a maf region, with
|
|
25
|
+
// over a maf region, with the column converters as identity so the asserted
|
|
26
26
|
// column equals the ungapped offset into the region.
|
|
27
27
|
function makeModel() {
|
|
28
28
|
const calls: (number | undefined)[] = []
|
|
29
29
|
const model = {
|
|
30
30
|
querySeqName: 'hg38.chr1',
|
|
31
|
-
|
|
31
|
+
rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
|
|
32
32
|
transcriptToMsaMap: undefined,
|
|
33
33
|
mafRegion,
|
|
34
34
|
connectedView: { initialized: true, assemblyNames: ['hg38'] },
|
|
35
35
|
seqPosToVisibleCol: (_name: string, pos: number) => pos,
|
|
36
|
+
visibleColToSeqPos: (_name: string, col: number) => col,
|
|
36
37
|
setMousePos: (col?: number) => {
|
|
37
38
|
calls.push(col)
|
|
38
39
|
},
|
|
@@ -40,16 +41,25 @@ function makeModel() {
|
|
|
40
41
|
return { model, calls }
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
// this config spells the refName the same way on both sides, so the assembly
|
|
45
|
+
// hands every name straight back
|
|
46
|
+
const assemblyManager = {
|
|
47
|
+
get: () => ({ getCanonicalRefName: (refName: string) => refName }),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mockSession(hovered: unknown) {
|
|
44
51
|
mockGetSession.mockReturnValue({
|
|
45
|
-
|
|
52
|
+
assemblyManager,
|
|
53
|
+
hovered,
|
|
46
54
|
} as unknown as ReturnType<typeof getSession>)
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
function hoverGenome(coord: number) {
|
|
58
|
+
mockSession({ hoverFeature: {}, hoverPosition: { coord, refName: 'chr1' } })
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
function clearGenomeHover() {
|
|
50
|
-
|
|
51
|
-
hovered: null,
|
|
52
|
-
} as unknown as ReturnType<typeof getSession>)
|
|
62
|
+
mockSession(null)
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
describe('syncGenomeHoverToMsaColumn (real genomeToMSA mapping)', () => {
|
package/src/MsaViewPanel/util.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Whether `querySeqName` names a row this alignment actually has.
|
|
2
|
+
* Whether `querySeqName` names a row this alignment actually has. The name
|
|
3
|
+
* defaults to `QUERY`, which an uploaded alignment has no reason to carry, so
|
|
4
|
+
* the genome and structure hovers ask this before mapping anything.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* name is wrong more often than it looks: it defaults to `QUERY`, which an
|
|
8
|
-
* uploaded alignment has no reason to carry, and the manual panel leaves it
|
|
9
|
-
* empty when it cannot match the protein to a row.
|
|
10
|
-
*
|
|
11
|
-
* The other direction has no such hole: msaCoordToGenomeRegions needs the query
|
|
12
|
-
* row's sequence to map a column at all, so a missing row is already nothing
|
|
13
|
-
* there.
|
|
6
|
+
* `rowMap`, not `rows`: `rows` is what is on screen, and a query row folded
|
|
7
|
+
* into a collapsed clade is still in the alignment, still has its columns, and
|
|
8
|
+
* still maps.
|
|
14
9
|
*/
|
|
15
|
-
export function hasQueryRow(model: {
|
|
16
|
-
|
|
10
|
+
export function hasQueryRow(model: {
|
|
11
|
+
rowMap: ReadonlyMap<string, string>
|
|
12
|
+
querySeqName: string
|
|
13
|
+
}) {
|
|
14
|
+
return model.rowMap.has(model.querySeqName)
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
/**
|
|
@@ -53,9 +51,8 @@ export function transcriptName(transcript?: TranscriptRef) {
|
|
|
53
51
|
return str(fields.name) ?? str(fields.id)
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
interface QueryRowColumns {
|
|
57
55
|
querySeqName: string
|
|
58
|
-
querySeqOffset: number
|
|
59
56
|
seqPosToVisibleCol: (rowName: string, seqPos: number) => number | undefined
|
|
60
57
|
visibleColToSeqPos: (
|
|
61
58
|
rowName: string,
|
|
@@ -63,24 +60,24 @@ export interface QueryRowModel {
|
|
|
63
60
|
) => number | undefined
|
|
64
61
|
}
|
|
65
62
|
|
|
63
|
+
export interface QueryRowModel extends QueryRowColumns {
|
|
64
|
+
querySeqOffset: number
|
|
65
|
+
}
|
|
66
|
+
|
|
66
67
|
/**
|
|
67
|
-
* The visible column showing residue `
|
|
68
|
-
*
|
|
69
|
-
*
|
|
68
|
+
* The visible column showing residue `seqPos` (0-based) of the query row, or
|
|
69
|
+
* undefined when the row does not carry that residue or react-msaview is
|
|
70
|
+
* hiding its column.
|
|
70
71
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* past the end for a position it does not have, which would light the last
|
|
75
|
-
* column for every residue beyond the row, so the round trip back through
|
|
76
|
-
* visibleColToSeqPos is what rejects those.
|
|
72
|
+
* react-msaview answers one column past the end for a position the row does not
|
|
73
|
+
* have, which would light the last column for every residue beyond the row, so
|
|
74
|
+
* the round trip back through visibleColToSeqPos is what rejects those.
|
|
77
75
|
*/
|
|
78
|
-
export function
|
|
79
|
-
model:
|
|
80
|
-
|
|
76
|
+
export function querySeqPosToVisibleCol(
|
|
77
|
+
model: QueryRowColumns,
|
|
78
|
+
seqPos: number,
|
|
81
79
|
) {
|
|
82
|
-
const { querySeqName
|
|
83
|
-
const seqPos = proteinPos - querySeqOffset
|
|
80
|
+
const { querySeqName } = model
|
|
84
81
|
if (seqPos < 0) {
|
|
85
82
|
return undefined
|
|
86
83
|
}
|
|
@@ -91,6 +88,18 @@ export function transcriptPosToVisibleCol(
|
|
|
91
88
|
: undefined
|
|
92
89
|
}
|
|
93
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The visible column showing residue `proteinPos` (0-based) of the transcript.
|
|
93
|
+
* `querySeqOffset` is what separates the two: a pasted BLAST alignment carries
|
|
94
|
+
* the aligned region, not the whole protein.
|
|
95
|
+
*/
|
|
96
|
+
export function transcriptPosToVisibleCol(
|
|
97
|
+
model: QueryRowModel,
|
|
98
|
+
proteinPos: number,
|
|
99
|
+
) {
|
|
100
|
+
return querySeqPosToVisibleCol(model, proteinPos - model.querySeqOffset)
|
|
101
|
+
}
|
|
102
|
+
|
|
94
103
|
export function hasHoverPosition(
|
|
95
104
|
hovered: unknown,
|
|
96
105
|
): hovered is { hoverPosition: { coord: number; refName: string } } {
|
package/src/index.ts
CHANGED
|
@@ -28,16 +28,25 @@ export default class MsaViewPlugin extends Plugin {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
configure(pluginManager: PluginManager) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
// a throw here error-pages the whole app, and a menu entry is not worth a
|
|
32
|
+
// session: every released host's File menu already throws on append
|
|
33
|
+
try {
|
|
34
|
+
if (isAbstractMenuManager(pluginManager.rootModel)) {
|
|
35
|
+
pluginManager.rootModel.appendToSubMenu(['Add'], {
|
|
36
|
+
label: 'Multiple sequence alignment view',
|
|
37
|
+
icon: GridOn,
|
|
38
|
+
onClick: (session: AbstractSessionModel) => {
|
|
39
|
+
// stacked, by default: nothing was launched from, so there is no
|
|
40
|
+
// connected view for it to sit beside
|
|
41
|
+
launchMsaView(session, {})
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.error(
|
|
47
|
+
'jbrowse-plugin-msaview: could not add the Add menu item',
|
|
48
|
+
e,
|
|
49
|
+
)
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
|