jbrowse-plugin-msaview 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/AddHighlightModel/GenomeMouseoverHighlight.d.ts +1 -1
  2. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -1
  3. package/dist/AddHighlightModel/HighlightComponents.d.ts +1 -1
  4. package/dist/AddHighlightModel/HighlightComponents.js +0 -1
  5. package/dist/AddHighlightModel/HighlightComponents.js.map +1 -1
  6. package/dist/AddHighlightModel/MsaToGenomeHighlight.d.ts +1 -1
  7. package/dist/AddHighlightModel/MsaToGenomeHighlight.js +0 -1
  8. package/dist/AddHighlightModel/MsaToGenomeHighlight.js.map +1 -1
  9. package/dist/AddHighlightModel/index.js +0 -1
  10. package/dist/AddHighlightModel/index.js.map +1 -1
  11. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.d.ts +8 -0
  12. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js +81 -0
  13. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js.map +1 -0
  14. package/dist/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeLaunchView.d.ts +13 -0
  15. package/dist/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeLaunchView.js +18 -0
  16. package/dist/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeLaunchView.js.map +1 -0
  17. package/dist/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeUtils.d.ts +5 -0
  18. package/dist/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeUtils.js +35 -0
  19. package/dist/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeUtils.js.map +1 -0
  20. package/dist/LaunchMsaView/components/EnsemblGeneTree/fetchGeneList.d.ts +1 -0
  21. package/dist/LaunchMsaView/components/EnsemblGeneTree/fetchGeneList.js +12 -0
  22. package/dist/LaunchMsaView/components/EnsemblGeneTree/fetchGeneList.js.map +1 -0
  23. package/dist/LaunchMsaView/components/EnsemblGeneTree/util.d.ts +4 -0
  24. package/dist/LaunchMsaView/components/EnsemblGeneTree/util.js +38 -0
  25. package/dist/LaunchMsaView/components/EnsemblGeneTree/util.js.map +1 -0
  26. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +20 -15
  27. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -1
  28. package/dist/LaunchMsaView/components/MSALoader/MSALoader.d.ts +8 -0
  29. package/dist/LaunchMsaView/components/MSALoader/MSALoader.js +94 -0
  30. package/dist/LaunchMsaView/components/MSALoader/MSALoader.js.map +1 -0
  31. package/dist/LaunchMsaView/components/MSALoader/fetchGeneList.d.ts +1 -0
  32. package/dist/LaunchMsaView/components/MSALoader/fetchGeneList.js +12 -0
  33. package/dist/LaunchMsaView/components/MSALoader/fetchGeneList.js.map +1 -0
  34. package/dist/LaunchMsaView/components/MSALoader/preCalculatedLaunchView.d.ts +9 -0
  35. package/dist/LaunchMsaView/components/MSALoader/preCalculatedLaunchView.js +36 -0
  36. package/dist/LaunchMsaView/components/MSALoader/preCalculatedLaunchView.js.map +1 -0
  37. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.js +6 -5
  38. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.js.map +1 -1
  39. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.d.ts +4 -12
  40. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.js +2 -3
  41. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.js.map +1 -1
  42. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/fetchSeq.d.ts +8 -0
  43. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/fetchSeq.js +23 -0
  44. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/fetchSeq.js.map +1 -0
  45. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.d.ts +2 -2
  46. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.js.map +1 -1
  47. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/types.d.ts +10 -0
  48. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/types.js +2 -0
  49. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/types.js.map +1 -0
  50. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.d.ts +2 -6
  51. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.js +1 -22
  52. package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.js.map +1 -1
  53. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +8 -11
  54. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -1
  55. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +1 -1
  56. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js.map +1 -1
  57. package/dist/LaunchMsaView/components/TabPanel.d.ts +6 -0
  58. package/dist/LaunchMsaView/components/TabPanel.js +6 -0
  59. package/dist/LaunchMsaView/components/TabPanel.js.map +1 -0
  60. package/dist/LaunchMsaView/index.js +0 -2
  61. package/dist/LaunchMsaView/index.js.map +1 -1
  62. package/dist/LaunchMsaView/util.js +2 -2
  63. package/dist/MsaViewPanel/components/LoadingBLAST.js +0 -1
  64. package/dist/MsaViewPanel/components/LoadingBLAST.js.map +1 -1
  65. package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -1
  66. package/dist/MsaViewPanel/components/RIDLink.js +0 -1
  67. package/dist/MsaViewPanel/components/RIDLink.js.map +1 -1
  68. package/dist/MsaViewPanel/index.js +0 -1
  69. package/dist/MsaViewPanel/index.js.map +1 -1
  70. package/dist/MsaViewPanel/model.d.ts +24 -11
  71. package/dist/MsaViewPanel/model.js +9 -1
  72. package/dist/MsaViewPanel/model.js.map +1 -1
  73. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -1
  74. package/dist/OpenInNewIcon.d.ts +1 -1
  75. package/dist/OpenInNewIcon.js.map +1 -1
  76. package/dist/TextField2.d.ts +1 -1
  77. package/dist/TextField2.js.map +1 -1
  78. package/dist/config.json +8 -0
  79. package/dist/index.js +0 -2
  80. package/dist/index.js.map +1 -1
  81. package/dist/jbrowse-plugin-msaview.umd.production.min.js +52 -40
  82. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  83. package/package.json +2 -2
  84. package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +2 -1
  85. package/src/AddHighlightModel/HighlightComponents.tsx +2 -2
  86. package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +2 -3
  87. package/src/AddHighlightModel/index.tsx +2 -2
  88. package/src/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.tsx +143 -0
  89. package/src/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeLaunchView.ts +36 -0
  90. package/src/LaunchMsaView/components/EnsemblGeneTree/ensemblGeneTreeUtils.ts +81 -0
  91. package/src/LaunchMsaView/components/EnsemblGeneTree/fetchGeneList.ts +13 -0
  92. package/src/LaunchMsaView/components/EnsemblGeneTree/util.ts +45 -0
  93. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +30 -21
  94. package/src/LaunchMsaView/components/MSALoader/MSALoader.tsx +130 -0
  95. package/src/LaunchMsaView/components/MSALoader/fetchGeneList.ts +13 -0
  96. package/src/LaunchMsaView/components/MSALoader/preCalculatedLaunchView.ts +55 -0
  97. package/src/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.tsx +8 -6
  98. package/src/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.ts +6 -20
  99. package/src/LaunchMsaView/components/NewNCBIBlastQuery/fetchSeq.ts +37 -0
  100. package/src/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.ts +2 -3
  101. package/src/LaunchMsaView/components/NewNCBIBlastQuery/types.ts +11 -0
  102. package/src/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.ts +5 -41
  103. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +12 -18
  104. package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +2 -1
  105. package/src/LaunchMsaView/components/TabPanel.tsx +19 -0
  106. package/src/LaunchMsaView/index.ts +1 -4
  107. package/src/LaunchMsaView/util.ts +2 -2
  108. package/src/MsaViewPanel/components/LoadingBLAST.tsx +0 -2
  109. package/src/MsaViewPanel/components/MsaViewPanel.tsx +0 -1
  110. package/src/MsaViewPanel/components/RIDLink.tsx +0 -1
  111. package/src/MsaViewPanel/index.ts +0 -1
  112. package/src/MsaViewPanel/model.ts +14 -4
  113. package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +0 -1
  114. package/src/OpenInNewIcon.tsx +3 -1
  115. package/src/TextField2.tsx +3 -1
  116. package/src/index.ts +0 -3
  117. package/dist/LaunchMsaView/components/TabUtils.d.ts +0 -8
  118. package/dist/LaunchMsaView/components/TabUtils.js +0 -7
  119. package/dist/LaunchMsaView/components/TabUtils.js.map +0 -1
  120. package/dist/LaunchMsaView/components/tabUtil.d.ts +0 -4
  121. package/dist/LaunchMsaView/components/tabUtil.js +0 -7
  122. package/dist/LaunchMsaView/components/tabUtil.js.map +0 -1
  123. package/src/LaunchMsaView/components/TabUtils.tsx +0 -25
  124. package/src/LaunchMsaView/components/tabUtil.ts +0 -6
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.0.5",
2
+ "version": "2.1.0",
3
3
  "license": "MIT",
4
4
  "name": "jbrowse-plugin-msaview",
5
5
  "keywords": [
@@ -26,7 +26,7 @@
26
26
  "clean": "rimraf dist",
27
27
  "start": "node esbuild-watch.mjs",
28
28
  "format": "prettier --write .",
29
- "build": "tsc && NODE_ENV=production node esbuild.mjs",
29
+ "build": "tsc && NODE_ENV=production node esbuild.mjs && cp distconfig.json dist/config.json",
30
30
  "prebuild": "npm run clean",
31
31
  "lint": "eslint --report-unused-disable-directives --max-warnings 0",
32
32
  "prepack": "npm run build",
@@ -1,11 +1,12 @@
1
1
  import React from 'react'
2
2
 
3
3
  import { getSession } from '@jbrowse/core/util'
4
- import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
5
4
  import { observer } from 'mobx-react'
6
5
 
7
6
  import { useStyles } from './util'
8
7
 
8
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
9
+
9
10
  const GenomeMouseoverHighlight = observer(function GenomeMouseoverHighlight2({
10
11
  model,
11
12
  }: {
@@ -1,12 +1,12 @@
1
1
  import React from 'react'
2
2
 
3
- import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
4
3
  import { observer } from 'mobx-react'
5
4
 
6
- // locals
7
5
  import GenomeMouseoverHighlight from './GenomeMouseoverHighlight'
8
6
  import MsaToGenomeHighlight from './MsaToGenomeHighlight'
9
7
 
8
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
9
+
10
10
  type LGV = LinearGenomeViewModel
11
11
 
12
12
  const HighlightComponents = observer(function HighlightComponents2({
@@ -2,14 +2,13 @@ import React from 'react'
2
2
 
3
3
  import { Assembly } from '@jbrowse/core/assemblyManager/assembly'
4
4
  import { getSession } from '@jbrowse/core/util'
5
- import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
6
5
  import { observer } from 'mobx-react'
7
6
 
8
-
9
- // locals
10
7
  import { useStyles } from './util'
11
8
  import { JBrowsePluginMsaViewModel } from '../MsaViewPanel/model'
12
9
 
10
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
11
+
13
12
  type LGV = LinearGenomeViewModel
14
13
 
15
14
  function getCanonicalName(assembly: Assembly, s: string) {
@@ -1,11 +1,11 @@
1
1
  import React from 'react'
2
2
 
3
3
  import PluginManager from '@jbrowse/core/PluginManager'
4
- import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
5
4
 
6
- // locals
7
5
  import HighlightComponents from './HighlightComponents'
8
6
 
7
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
8
+
9
9
  export default function AddHighlightComponentsModelF(
10
10
  pluginManager: PluginManager,
11
11
  ) {
@@ -0,0 +1,143 @@
1
+ import React, { useEffect, useState } from 'react'
2
+
3
+ import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'
4
+ import {
5
+ AbstractTrackModel,
6
+ Feature,
7
+ getContainingView,
8
+ getSession,
9
+ } from '@jbrowse/core/util'
10
+ import {
11
+ Button,
12
+ DialogActions,
13
+ DialogContent,
14
+ MenuItem,
15
+ TextField,
16
+ Typography,
17
+ } from '@mui/material'
18
+ import { observer } from 'mobx-react'
19
+ import { makeStyles } from 'tss-react/mui'
20
+
21
+ import { ensemblGeneTreeLaunchView } from './ensemblGeneTreeLaunchView'
22
+ import { geneTreeFetcher } from './ensemblGeneTreeUtils'
23
+ import {
24
+ getGeneDisplayName,
25
+ getId,
26
+ getTranscriptDisplayName,
27
+ getTranscriptFeatures,
28
+ } from '../../util'
29
+
30
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
31
+
32
+ const useStyles = makeStyles()({
33
+ dialogContent: {
34
+ width: '80em',
35
+ },
36
+ })
37
+
38
+ type Ret = Awaited<ReturnType<typeof geneTreeFetcher>>
39
+
40
+ const EnsemblGeneTree = observer(function ({
41
+ model,
42
+ feature,
43
+ handleClose,
44
+ }: {
45
+ model: AbstractTrackModel
46
+ feature: Feature
47
+ handleClose: () => void
48
+ }) {
49
+ const session = getSession(model)
50
+ const view = getContainingView(model) as LinearGenomeViewModel
51
+ const { classes } = useStyles()
52
+ const [error, setError] = useState<unknown>()
53
+ const [treeData, setTreeData] = useState<Ret>()
54
+ const [isTreeLoading, setIsTreeLoading] = useState(false)
55
+ const [treeError, setTreeError] = useState<unknown>()
56
+ const options = getTranscriptFeatures(feature)
57
+ const [userSelection, setUserSelection] = useState(getId(options[0]))
58
+
59
+ useEffect(() => {
60
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
61
+ ;(async () => {
62
+ try {
63
+ setIsTreeLoading(true)
64
+ const result = await geneTreeFetcher(userSelection)
65
+ setTreeData(result)
66
+ } catch (e) {
67
+ console.error(e)
68
+ setTreeError(e)
69
+ } finally {
70
+ setIsTreeLoading(false)
71
+ }
72
+ })()
73
+ }, [userSelection])
74
+
75
+ const loadingMessage = isTreeLoading
76
+ ? 'Loading tree data from Ensembl GeneTree'
77
+ : undefined
78
+ const e = treeError ?? error
79
+
80
+ return (
81
+ <>
82
+ <DialogContent className={classes.dialogContent}>
83
+ {e ? <ErrorMessage error={e} /> : null}
84
+ {loadingMessage ? <LoadingEllipses message={loadingMessage} /> : null}
85
+ <Typography>Load data from Ensembl GeneTree</Typography>
86
+ <TextField
87
+ select
88
+ label="Choose isoform to view MSA for"
89
+ value={userSelection}
90
+ onChange={event => {
91
+ setUserSelection(event.target.value)
92
+ }}
93
+ >
94
+ {options.map(val => (
95
+ <MenuItem value={getId(val)} key={val.id()}>
96
+ {getTranscriptDisplayName(val)}
97
+ </MenuItem>
98
+ ))}
99
+ </TextField>
100
+ </DialogContent>
101
+
102
+ <DialogActions>
103
+ <Button
104
+ color="primary"
105
+ variant="contained"
106
+ onClick={() => {
107
+ try {
108
+ if (!treeData) {
109
+ return
110
+ }
111
+ setError(undefined)
112
+
113
+ ensemblGeneTreeLaunchView({
114
+ feature,
115
+ view,
116
+ session,
117
+ newViewTitle: getGeneDisplayName(feature),
118
+ data: treeData,
119
+ })
120
+ handleClose()
121
+ } catch (e) {
122
+ console.error(e)
123
+ setError(e)
124
+ }
125
+ }}
126
+ >
127
+ Submit
128
+ </Button>
129
+ <Button
130
+ color="secondary"
131
+ variant="contained"
132
+ onClick={() => {
133
+ handleClose()
134
+ }}
135
+ >
136
+ Cancel
137
+ </Button>
138
+ </DialogActions>
139
+ </>
140
+ )
141
+ })
142
+
143
+ export default EnsemblGeneTree
@@ -0,0 +1,36 @@
1
+ import type { AbstractSessionModel, Feature } from '@jbrowse/core/util'
2
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
3
+
4
+ export function ensemblGeneTreeLaunchView({
5
+ session,
6
+ newViewTitle,
7
+ view,
8
+ feature,
9
+ data,
10
+ }: {
11
+ session: AbstractSessionModel
12
+ newViewTitle: string
13
+ view: LinearGenomeViewModel
14
+ feature: Feature
15
+ data: {
16
+ tree: string
17
+ msa: string
18
+ treeMetadata: string
19
+ }
20
+ }) {
21
+ session.addView('MsaView', {
22
+ type: 'MsaView',
23
+ displayName: newViewTitle,
24
+ treeAreaWidth: 200,
25
+ treeWidth: 100,
26
+ drawNodeBubbles: false,
27
+ labelsAlignRight: true,
28
+ showBranchLen: false,
29
+ colWidth: 10,
30
+ rowHeight: 12,
31
+ colorSchemeName: 'percent_identity_dynamic',
32
+ data,
33
+ connectedViewId: view.id,
34
+ connectedFeature: feature.toJSON(),
35
+ })
36
+ }
@@ -0,0 +1,81 @@
1
+ import { fetchWithLocalStorageCache, jsonfetch, textfetch } from './util'
2
+
3
+ interface TreeNodeSequence {
4
+ mol_seq: {
5
+ seq: string
6
+ location?: string
7
+ }
8
+ id: {
9
+ accession: string
10
+ }[]
11
+ }
12
+
13
+ interface TreeNodeTaxonomy {
14
+ common_name: string
15
+ scientific_name: string
16
+ }
17
+
18
+ // This is a self-referential tree data structure
19
+ interface TreeNode {
20
+ children?: TreeNode[]
21
+ sequence?: TreeNodeSequence
22
+ taxonomy: TreeNodeTaxonomy
23
+ }
24
+
25
+ interface TreeRow {
26
+ id: string
27
+ seq: string
28
+ species: string
29
+ genomicLocString?: string
30
+ }
31
+
32
+ function gatherSequencesFromTree(tree: TreeNode, arr: TreeRow[] = []) {
33
+ if (tree.children) {
34
+ for (const child of tree.children) {
35
+ if (child.sequence) {
36
+ const id = child.sequence.id[0]?.accession
37
+ if (id) {
38
+ arr.push({
39
+ id,
40
+ seq: child.sequence.mol_seq.seq,
41
+ species:
42
+ child.taxonomy.common_name || child.taxonomy.scientific_name,
43
+ genomicLocString: child.sequence.mol_seq.location,
44
+ })
45
+ }
46
+ }
47
+ gatherSequencesFromTree(child, arr)
48
+ }
49
+ }
50
+ return arr
51
+ }
52
+
53
+ const base = 'https://rest.ensembl.org'
54
+
55
+ export async function geneTreeFetcher(id2: string) {
56
+ const id = id2.replace(/\..*/, '')
57
+ const { species } = await fetchWithLocalStorageCache(`${id}-ensembl`, () =>
58
+ jsonfetch<{ species: string }>(
59
+ `${base}/lookup/id/${id}?content-type=application/json`,
60
+ ),
61
+ )
62
+ const treeBase = `${base}/genetree/member/id/${species}/${id}`
63
+ const msa = await fetchWithLocalStorageCache(`${id}-msa`, () =>
64
+ jsonfetch<{ tree: TreeNode }>(
65
+ `${treeBase}?content-type=application/json;aligned=1;sequence=pep`,
66
+ ),
67
+ )
68
+
69
+ const tree = await fetchWithLocalStorageCache<string>(`${id}-tree`, () =>
70
+ textfetch(`${treeBase}?nh_format=simple;content-type=text/x-nh`),
71
+ )
72
+
73
+ const res = gatherSequencesFromTree(msa.tree)
74
+ return {
75
+ tree,
76
+ msa: res.map(r => `>${r.id}\n${r.seq}`).join('\n'),
77
+ treeMetadata: JSON.stringify(
78
+ Object.fromEntries(res.map(r => [r.id, { genome: r.species }] as const)),
79
+ ),
80
+ }
81
+ }
@@ -0,0 +1,13 @@
1
+ export async function fetchGeneList() {
2
+ const res = await fetch(
3
+ 'https://jbrowse.org/demos/msaview/knownCanonical/list.txt',
4
+ )
5
+ if (!res.ok) {
6
+ throw new Error(`HTTP ${res.status} fetching list ${await res.text()}`)
7
+ }
8
+ const result = await res.text()
9
+ return result
10
+ .split('\n')
11
+ .map(f => f.trim())
12
+ .filter(f => !!f)
13
+ }
@@ -0,0 +1,45 @@
1
+ import { ungzip } from 'pako'
2
+
3
+ export async function jsonfetch<T>(url: string, arg?: RequestInit) {
4
+ const res = await fetch(url, arg)
5
+ if (!res.ok) {
6
+ throw new Error(`HTTP ${res.status} from ${url}: ${await res.text()}`)
7
+ }
8
+ return res.json() as Promise<T>
9
+ }
10
+
11
+ export async function textfetch(url: string, arg?: RequestInit) {
12
+ const res = await fetch(url, arg)
13
+ if (!res.ok) {
14
+ throw new Error(`HTTP ${res.status} from ${url}`)
15
+ }
16
+ return res.text()
17
+ }
18
+
19
+ export async function fetchWithLocalStorageCache<T>(
20
+ key: string,
21
+ fetchFn: () => Promise<T>,
22
+ ): Promise<T> {
23
+ const cachedData = localStorage.getItem(key)
24
+
25
+ if (cachedData) {
26
+ try {
27
+ return JSON.parse(cachedData) as T
28
+ } catch (error) {
29
+ console.error(`Error parsing cached data for ${key}:`, error)
30
+ // Continue to fetch fresh data if parsing fails
31
+ }
32
+ }
33
+
34
+ const data = await fetchFn()
35
+ localStorage.setItem(key, JSON.stringify(data))
36
+ return data
37
+ }
38
+
39
+ export async function unzipfetch(url: string, arg?: RequestInit) {
40
+ const res = await fetch(url, arg)
41
+ if (!res.ok) {
42
+ throw new Error(`HTTP ${res.status} from ${url}: ${await res.text()}`)
43
+ }
44
+ return ungzip(await res.arrayBuffer(), { to: 'string' })
45
+ }
@@ -2,14 +2,13 @@ import React, { useState } from 'react'
2
2
 
3
3
  import { Dialog } from '@jbrowse/core/ui'
4
4
  import { AbstractTrackModel, Feature } from '@jbrowse/core/util'
5
- import { Box, Tab, Tabs } from '@mui/material'
6
-
7
- // locals
5
+ import { Tab, Tabs } from '@mui/material'
8
6
 
7
+ import EnsemblGeneTree from './EnsemblGeneTree/EnsemblGeneTree'
8
+ import MSALoader from './MSALoader/MSALoader'
9
9
  import NewNcbiBlastQueryPanel from './NewNCBIBlastQuery'
10
10
  import PreLoadedMSA from './PreLoadedMSA/PreLoadedMSADataPanel'
11
- import CustomTabPanel from './TabUtils'
12
- import { a11yProps } from './tabUtil'
11
+ import TabPanel from './TabPanel'
13
12
 
14
13
  export default function LaunchProteinViewDialog({
15
14
  handleClose,
@@ -26,36 +25,46 @@ export default function LaunchProteinViewDialog({
26
25
  <Dialog
27
26
  maxWidth="xl"
28
27
  title="Launch MSA view"
28
+ open
29
29
  onClose={() => {
30
30
  handleClose()
31
31
  }}
32
- open
33
32
  >
34
- <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
35
- <Tabs
36
- value={value}
37
- onChange={(_, val) => {
38
- setValue(val)
39
- }}
40
- >
41
- <Tab label="NCBI BLAST query" {...a11yProps(0)} />
42
- <Tab label="UCSC 100-way dataset" {...a11yProps(1)} />
43
- </Tabs>
44
- </Box>
45
- <CustomTabPanel value={value} index={0}>
33
+ <Tabs
34
+ value={value}
35
+ onChange={(_, val) => {
36
+ setValue(val)
37
+ }}
38
+ >
39
+ <Tab label="NCBI BLAST query" value={0} />
40
+ <Tab label="UCSC 100-way dataset" value={1} />
41
+ <Tab label="Ensembl GeneTree" value={2} />
42
+ <Tab label="Manually open MSA" value={3} />
43
+ </Tabs>
44
+ <TabPanel value={value} index={0}>
46
45
  <NewNcbiBlastQueryPanel
47
46
  handleClose={handleClose}
48
47
  feature={feature}
49
48
  model={model}
50
49
  />
51
- </CustomTabPanel>
52
- <CustomTabPanel value={value} index={1}>
50
+ </TabPanel>
51
+ <TabPanel value={value} index={1}>
53
52
  <PreLoadedMSA
54
53
  model={model}
55
54
  feature={feature}
56
55
  handleClose={handleClose}
57
56
  />
58
- </CustomTabPanel>
57
+ </TabPanel>
58
+ <TabPanel value={value} index={2}>
59
+ <EnsemblGeneTree
60
+ model={model}
61
+ feature={feature}
62
+ handleClose={handleClose}
63
+ />
64
+ </TabPanel>
65
+ <TabPanel value={value} index={3}>
66
+ <MSALoader model={model} feature={feature} handleClose={handleClose} />
67
+ </TabPanel>
59
68
  </Dialog>
60
69
  )
61
70
  }
@@ -0,0 +1,130 @@
1
+ import React, { useEffect, useState } from 'react'
2
+
3
+ import { FileSelector } from '@jbrowse/core/ui'
4
+ import {
5
+ AbstractTrackModel,
6
+ Feature,
7
+ FileLocation,
8
+ getContainingView,
9
+ getSession,
10
+ } from '@jbrowse/core/util'
11
+ import { openLocation } from '@jbrowse/core/util/io'
12
+ import { Button, DialogActions, DialogContent } from '@mui/material'
13
+ import { observer } from 'mobx-react'
14
+ import { makeStyles } from 'tss-react/mui'
15
+
16
+ import { fetchGeneList } from './fetchGeneList'
17
+ import { preCalculatedLaunchView } from './preCalculatedLaunchView'
18
+ import { getGeneDisplayName, getId, getTranscriptFeatures } from '../../util'
19
+
20
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
21
+
22
+ const useStyles = makeStyles()({
23
+ dialogContent: {
24
+ width: '80em',
25
+ },
26
+ })
27
+
28
+ const PreLoadedMSA = observer(function PreLoadedMSA2({
29
+ model,
30
+ feature,
31
+ handleClose,
32
+ }: {
33
+ model: AbstractTrackModel
34
+ feature: Feature
35
+ handleClose: () => void
36
+ }) {
37
+ const session = getSession(model)
38
+ const view = getContainingView(model) as LinearGenomeViewModel
39
+ const { classes } = useStyles()
40
+ const [, setError] = useState<unknown>()
41
+ const [geneNameList, setGeneNameList] = useState<string[]>()
42
+ const [fileLocation, setFileLocation] = useState<FileLocation>()
43
+
44
+ useEffect(() => {
45
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
46
+ ;(async () => {
47
+ try {
48
+ const data = await fetchGeneList()
49
+ setGeneNameList(data)
50
+ const set = new Set(data)
51
+ const options = getTranscriptFeatures(feature)
52
+ const ret = options.find(val => set.has(getId(val)))
53
+ if (ret) {
54
+ setUserSelection(getId(ret))
55
+ }
56
+ } catch (e) {
57
+ console.error(e)
58
+ setError(e)
59
+ }
60
+ })()
61
+ }, [feature])
62
+
63
+ useEffect(() => {
64
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
65
+ ;(async () => {
66
+ try {
67
+ if (fileLocation) {
68
+ await openLocation(fileLocation).readFile()
69
+ }
70
+ } catch (e) {
71
+ console.error(e)
72
+ setError(e)
73
+ }
74
+ })()
75
+ }, [fileLocation, feature])
76
+
77
+ const set = new Set(geneNameList)
78
+ const options = getTranscriptFeatures(feature)
79
+ const ret = options.find(val => set.has(getId(val)))
80
+ const [userSelection, setUserSelection] = useState(getId(options[0]))
81
+
82
+ return (
83
+ <>
84
+ <DialogContent className={classes.dialogContent}>
85
+ <FileSelector location={fileLocation} setLocation={setFileLocation} />
86
+ </DialogContent>
87
+
88
+ <DialogActions>
89
+ <Button
90
+ color="primary"
91
+ variant="contained"
92
+ onClick={() => {
93
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
94
+ ;(async () => {
95
+ try {
96
+ if (!ret) {
97
+ return
98
+ }
99
+ await preCalculatedLaunchView({
100
+ userSelection,
101
+ session,
102
+ newViewTitle: getGeneDisplayName(ret),
103
+ view,
104
+ feature: ret,
105
+ })
106
+ handleClose()
107
+ } catch (e) {
108
+ console.error(e)
109
+ setError(e)
110
+ }
111
+ })()
112
+ }}
113
+ >
114
+ Submit
115
+ </Button>
116
+ <Button
117
+ color="secondary"
118
+ variant="contained"
119
+ onClick={() => {
120
+ handleClose()
121
+ }}
122
+ >
123
+ Cancel
124
+ </Button>
125
+ </DialogActions>
126
+ </>
127
+ )
128
+ })
129
+
130
+ export default PreLoadedMSA
@@ -0,0 +1,13 @@
1
+ export async function fetchGeneList() {
2
+ const res = await fetch(
3
+ 'https://jbrowse.org/demos/msaview/knownCanonical/list.txt',
4
+ )
5
+ if (!res.ok) {
6
+ throw new Error(`HTTP ${res.status} fetching list ${await res.text()}`)
7
+ }
8
+ const result = await res.text()
9
+ return result
10
+ .split('\n')
11
+ .map(f => f.trim())
12
+ .filter(f => !!f)
13
+ }
@@ -0,0 +1,55 @@
1
+ import { AbstractSessionModel, Feature } from '@jbrowse/core/util'
2
+ import { ungzip } from 'pako'
3
+
4
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
5
+
6
+ async function myfetch(url: string) {
7
+ const res = await fetch(url)
8
+ if (!res.ok) {
9
+ throw new Error(`HTTP ${res.status} fetching ${await res.text()}`)
10
+ }
11
+ const data = await res.arrayBuffer()
12
+ return new TextDecoder().decode(ungzip(data))
13
+ }
14
+
15
+ export async function preCalculatedLaunchView({
16
+ userSelection,
17
+ session,
18
+ newViewTitle,
19
+ view,
20
+ feature,
21
+ }: {
22
+ session: AbstractSessionModel
23
+ userSelection: string
24
+ newViewTitle: string
25
+ view: LinearGenomeViewModel
26
+ feature: Feature
27
+ }) {
28
+ const d = await myfetch(
29
+ `https://jbrowse.org/demos/msaview/knownCanonical/${userSelection}.mfa.gz`,
30
+ )
31
+
32
+ session.addView('MsaView', {
33
+ type: 'MsaView',
34
+ displayName: newViewTitle,
35
+ treeAreaWidth: 200,
36
+ treeWidth: 100,
37
+ drawNodeBubbles: false,
38
+ labelsAlignRight: true,
39
+ showBranchLen: false,
40
+ colWidth: 10,
41
+ rowHeight: 12,
42
+ colorSchemeName: 'percent_identity_dynamic',
43
+ treeFilehandle: {
44
+ uri: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/multiz100way/hg38.100way.nh',
45
+ },
46
+ treeMetadataFilehandle: {
47
+ uri: 'https://s3.amazonaws.com/jbrowse.org/demos/app/species.json',
48
+ },
49
+ data: {
50
+ msa: d,
51
+ },
52
+ connectedViewId: view.id,
53
+ connectedFeature: feature.toJSON(),
54
+ })
55
+ }