jbrowse-plugin-msaview 2.3.2 → 2.3.5

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 (43) hide show
  1. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js +2 -11
  2. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js.map +1 -1
  3. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js +24 -47
  4. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js.map +1 -1
  5. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js +5 -20
  6. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js.map +1 -1
  7. package/dist/LaunchMsaView/components/NCBIBlastQuery/useCachedBlastResults.d.ts +7 -0
  8. package/dist/LaunchMsaView/components/NCBIBlastQuery/useCachedBlastResults.js +29 -0
  9. package/dist/LaunchMsaView/components/NCBIBlastQuery/useCachedBlastResults.js.map +1 -0
  10. package/dist/LaunchMsaView/components/useTranscriptSelection.js +15 -14
  11. package/dist/LaunchMsaView/components/useTranscriptSelection.js.map +1 -1
  12. package/dist/MsaViewPanel/afterCreateAutoruns.js +14 -9
  13. package/dist/MsaViewPanel/afterCreateAutoruns.js.map +1 -1
  14. package/dist/MsaViewPanel/components/ErrorBoundary.d.ts +19 -0
  15. package/dist/MsaViewPanel/components/ErrorBoundary.js +22 -0
  16. package/dist/MsaViewPanel/components/ErrorBoundary.js.map +1 -0
  17. package/dist/MsaViewPanel/components/MsaViewPanel.js +4 -2
  18. package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -1
  19. package/dist/MsaViewPanel/model.d.ts +0 -12
  20. package/dist/MsaViewPanel/model.js +1 -1
  21. package/dist/MsaViewPanel/model.js.map +1 -1
  22. package/dist/MsaViewPanel/pairwiseAlignment.js +9 -4
  23. package/dist/MsaViewPanel/pairwiseAlignment.js.map +1 -1
  24. package/dist/jbrowse-plugin-msaview.umd.production.min.js +26 -26
  25. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  26. package/dist/utils/blastCache.js +2 -3
  27. package/dist/utils/blastCache.js.map +1 -1
  28. package/dist/version.d.ts +1 -1
  29. package/dist/version.js +1 -1
  30. package/dist/version.js.map +1 -1
  31. package/package.json +35 -34
  32. package/src/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.ts +2 -11
  33. package/src/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.tsx +23 -49
  34. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx +6 -20
  35. package/src/LaunchMsaView/components/NCBIBlastQuery/useCachedBlastResults.ts +49 -0
  36. package/src/LaunchMsaView/components/useTranscriptSelection.ts +25 -17
  37. package/src/MsaViewPanel/afterCreateAutoruns.ts +26 -9
  38. package/src/MsaViewPanel/components/ErrorBoundary.tsx +40 -0
  39. package/src/MsaViewPanel/components/MsaViewPanel.tsx +14 -11
  40. package/src/MsaViewPanel/model.ts +32 -21
  41. package/src/MsaViewPanel/pairwiseAlignment.ts +9 -4
  42. package/src/utils/blastCache.ts +2 -3
  43. package/src/version.ts +1 -1
@@ -15,11 +15,10 @@ async function getDB() {
15
15
  });
16
16
  }
17
17
  function createCacheKey(proteinSequence, blastDatabase, blastProgram, transcriptId) {
18
- const seqKey = proteinSequence.slice(0, 100);
19
18
  if (transcriptId) {
20
- return `${blastDatabase}:${blastProgram}:${transcriptId}:${seqKey}`;
19
+ return `${blastDatabase}:${blastProgram}:${transcriptId}:${proteinSequence}`;
21
20
  }
22
- return `${blastDatabase}:${blastProgram}:${seqKey}`;
21
+ return `${blastDatabase}:${blastProgram}:${proteinSequence}`;
23
22
  }
24
23
  export async function getCachedBlastResult({ proteinSequence, blastDatabase, blastProgram, transcriptId, }) {
25
24
  const db = await getDB();
@@ -1 +1 @@
1
- {"version":3,"file":"blastCache.js","sourceRoot":"","sources":["../../src/utils/blastCache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blastCache.js","sourceRoot":"","sources":["../../src/utils/blastCache.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.2.11";
1
+ export declare const version = "2.3.5";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '2.2.11';
1
+ export const version = '2.3.5';
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAA"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA"}
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.3.2",
2
+ "version": "2.3.5",
3
3
  "license": "MIT",
4
4
  "name": "jbrowse-plugin-msaview",
5
5
  "repository": {
@@ -15,65 +15,66 @@
15
15
  "dist",
16
16
  "src"
17
17
  ],
18
- "scripts": {
19
- "clean": "rimraf dist",
20
- "start": "node esbuild.mjs --watch",
21
- "format": "prettier --write .",
22
- "build": "tsc && NODE_ENV=production node esbuild.mjs && cp distconfig.json dist/config.json",
23
- "prebuild": "yarn clean",
24
- "lint": "eslint --report-unused-disable-directives --max-warnings 0",
25
- "pretest": "rm -rf .test-jbrowse && npx @jbrowse/cli create .test-jbrowse --nightly",
26
- "test": "vitest run",
27
- "test:watch": "vitest",
28
- "test:setup": "node scripts/test-versions.mjs setup",
29
- "test:setup:version": "node scripts/test-versions.mjs setup",
30
- "test:versions": "node scripts/test-versions.mjs run",
31
- "test:version": "node scripts/test-versions.mjs run",
32
- "prepack": "yarn build",
33
- "postversion": "node -e \"console.log('export const version = \\'' + require('./package.json').version + '\\'')\" > src/version.ts && git add -A src && git diff --cached --quiet || git commit -m '[skip ci] Bump version.ts' && git push --follow-tags",
34
- "preversion": "yarn lint"
35
- },
36
18
  "dependencies": {
37
19
  "@emotion/styled": "^11.14.1",
38
- "g2p_mapper": "^2.0.0",
20
+ "g2p_mapper": "^2.0.1",
39
21
  "idb": "^8.0.3",
40
22
  "pako-esm2": "^2.0.2",
41
- "react-msaview": "^5.0.13",
23
+ "react-msaview": "^5.0.16",
42
24
  "swr": "^2.4.1"
43
25
  },
44
26
  "devDependencies": {
45
27
  "@emotion/react": "^11.14.0",
46
- "@eslint/js": "^9.39.4",
28
+ "@eslint/js": "^10.0.1",
47
29
  "@fal-works/esbuild-plugin-global-externals": "^2.1.2",
48
- "@jbrowse/core": "^4.1.15",
30
+ "@jbrowse/core": "^4.2.0",
49
31
  "@jbrowse/mobx-state-tree": "^5.6.0",
50
- "@jbrowse/plugin-linear-genome-view": "^4.1.15",
32
+ "@jbrowse/plugin-linear-genome-view": "^4.2.0",
51
33
  "@mui/icons-material": "^7.3.10",
52
34
  "@mui/material": "^7.3.10",
53
35
  "@mui/system": "^7.3.10",
54
36
  "@mui/x-data-grid": "^8.28.2",
55
37
  "@types/node": "^25.6.0",
56
38
  "@types/react": "^19.2.14",
39
+ "@typescript-eslint/eslint-plugin": "^8.59.1",
40
+ "@typescript-eslint/parser": "^8.59.1",
57
41
  "esbuild": "^0.28.0",
58
- "eslint": "^9.39.4",
59
- "eslint-plugin-import": "^2.32.0",
42
+ "eslint": "^10.2.1",
43
+ "eslint-plugin-import-x": "^4.16.2",
60
44
  "eslint-plugin-react": "^7.37.5",
61
- "eslint-plugin-react-hooks": "^7.0.1",
62
- "eslint-plugin-react-refresh": "^0.5.2",
45
+ "eslint-plugin-react-hooks": "^7.1.1",
63
46
  "eslint-plugin-unicorn": "^64.0.0",
64
47
  "mobx": "^6.15.0",
65
48
  "mobx-react": "^9.2.1",
66
- "prettier": "^3.8.2",
49
+ "prettier": "^3.8.3",
67
50
  "pretty-bytes": "^7.1.0",
68
- "puppeteer": "^24.40.0",
51
+ "puppeteer": "^24.42.0",
69
52
  "react": "^19.2.5",
70
53
  "react-dom": "^19.2.5",
71
54
  "rimraf": "^6.1.3",
72
55
  "rxjs": "^7.8.2",
73
56
  "serve": "^14.2.6",
74
57
  "tss-react": "^4.9.20",
75
- "typescript": "^6.0.2",
76
- "typescript-eslint": "^8.58.2",
77
- "vitest": "^4.1.4"
58
+ "typescript": "^6.0.3",
59
+ "typescript-eslint": "^8.59.1",
60
+ "vitest": "^4.1.5"
61
+ },
62
+ "scripts": {
63
+ "clean": "rimraf dist",
64
+ "start": "node esbuild.mjs --watch",
65
+ "format": "pnpm prettier --write .",
66
+ "build": "tsc && NODE_ENV=production node esbuild.mjs && cp distconfig.json dist/config.json",
67
+ "prebuild": "pnpm clean",
68
+ "lint": "eslint src --report-unused-disable-directives --max-warnings 0",
69
+ "pretest": "rm -rf .test-jbrowse && npx @jbrowse/cli create .test-jbrowse --nightly",
70
+ "test": "vitest run",
71
+ "test:watch": "vitest",
72
+ "test:setup": "node scripts/test-versions.mjs setup",
73
+ "test:setup:version": "node scripts/test-versions.mjs setup",
74
+ "test:versions": "node scripts/test-versions.mjs run",
75
+ "test:version": "node scripts/test-versions.mjs run",
76
+ "preversion": "pnpm lint",
77
+ "version": "node -e \"console.log('export const version = \\'' + require('./package.json').version + '\\'')\" > src/version.ts && git add src/version.ts",
78
+ "postversion": "git push --follow-tags"
78
79
  }
79
- }
80
+ }
@@ -42,25 +42,16 @@ export default class BgzipFastaMsaAdapter extends BaseAdapter {
42
42
 
43
43
  async getMSAList() {
44
44
  const refNames = await this.getMSARefs()
45
- const list = new Set<string>()
46
45
  const val = this.getConf('msaRegex')
47
46
  const re = new RegExp(val)
48
- for (let i = 0, l = refNames.length; i < l; i++) {
49
- list.add(refNames[i]!.split(re)[0]!)
50
- }
47
+ const list = new Set(refNames.map(name => name.split(re)[0]!))
51
48
  return [...list]
52
49
  }
53
50
 
54
51
  async getMSA(id: string) {
55
52
  const adapter = await this.configure()
56
53
  const refNames = await adapter.getRefNames()
57
- const rows = []
58
- for (let i = 0, l = refNames.length; i < l; i++) {
59
- const refName = refNames[i]!
60
- if (refName.startsWith(id)) {
61
- rows.push(refName)
62
- }
63
- }
54
+ const rows = refNames.filter(refName => refName.startsWith(id))
64
55
  return firstValueFrom(
65
56
  adapter
66
57
  .getFeaturesInMultipleRegions(
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useState } from 'react'
1
+ import React, { useMemo, useState } from 'react'
2
2
 
3
3
  import { ErrorMessage } from '@jbrowse/core/ui'
4
4
  import { getContainingView } from '@jbrowse/core/util'
@@ -15,11 +15,7 @@ import {
15
15
  import { observer } from 'mobx-react'
16
16
 
17
17
  import { blastLaunchViewFromCache } from './blastLaunchView'
18
- import {
19
- clearAllCachedResults,
20
- deleteCachedResult,
21
- getAllCachedResults,
22
- } from '../../../utils/blastCache'
18
+ import { useCachedBlastResults } from './useCachedBlastResults'
23
19
  import { getGeneIdentifiers } from '../../util'
24
20
 
25
21
  import type { CachedBlastResult } from '../../../utils/blastCache'
@@ -49,44 +45,13 @@ const CachedBlastResults = observer(function ({
49
45
  handleClose: () => void
50
46
  feature: Feature
51
47
  }) {
52
- const [results, setResults] = useState<CachedBlastResult[]>([])
53
- const [loading, setLoading] = useState(true)
54
- const [error, setError] = useState<unknown>()
55
48
  const view = getContainingView(model) as LinearGenomeViewModel
49
+ const [operationError, setOperationError] = useState<unknown>()
56
50
 
57
51
  const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature])
58
52
 
59
- useEffect(() => {
60
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
61
- ;(async () => {
62
- try {
63
- const cached = await getAllCachedResults()
64
- setResults(cached.filter(r => r.geneId && geneIds.includes(r.geneId)))
65
- setLoading(false)
66
- } catch (e) {
67
- console.error(e)
68
- setError(e)
69
- }
70
- })()
71
- }, [geneIds])
72
-
73
- const handleDelete = async (id: string) => {
74
- try {
75
- await deleteCachedResult(id)
76
- setResults(r => r.filter(result => result.id !== id))
77
- } catch (e) {
78
- setError(e)
79
- }
80
- }
81
-
82
- const handleClearAll = async () => {
83
- try {
84
- await clearAllCachedResults()
85
- setResults([])
86
- } catch (e) {
87
- setError(e)
88
- }
89
- }
53
+ const { results, error, isLoading, handleDelete, handleClearAll } =
54
+ useCachedBlastResults(geneIds)
90
55
 
91
56
  const handleUseCached = (cached: CachedBlastResult) => {
92
57
  blastLaunchViewFromCache({
@@ -97,11 +62,12 @@ const CachedBlastResults = observer(function ({
97
62
  handleClose()
98
63
  }
99
64
 
100
- if (error) {
101
- return <ErrorMessage error={error} />
65
+ const displayError = error ?? operationError
66
+ if (displayError) {
67
+ return <ErrorMessage error={displayError} />
102
68
  }
103
69
 
104
- if (loading) {
70
+ if (isLoading) {
105
71
  return <Typography>Loading cached results...</Typography>
106
72
  }
107
73
 
@@ -130,9 +96,13 @@ const CachedBlastResults = observer(function ({
130
96
  <Button
131
97
  size="small"
132
98
  color="error"
133
- onClick={() => {
134
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
135
- handleClearAll()
99
+ onClick={async () => {
100
+ try {
101
+ setOperationError(undefined)
102
+ await handleClearAll()
103
+ } catch (e) {
104
+ setOperationError(e)
105
+ }
136
106
  }}
137
107
  >
138
108
  Clear All
@@ -147,10 +117,14 @@ const CachedBlastResults = observer(function ({
147
117
  <IconButton
148
118
  edge="end"
149
119
  size="small"
150
- onClick={e => {
120
+ onClick={async e => {
151
121
  e.stopPropagation()
152
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
153
- handleDelete(result.id)
122
+ try {
123
+ setOperationError(undefined)
124
+ await handleDelete(result.id)
125
+ } catch (err) {
126
+ setOperationError(err)
127
+ }
154
128
  }}
155
129
  >
156
130
  <DeleteIcon fontSize="small" />
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useState } from 'react'
1
+ import React, { useMemo, useState } from 'react'
2
2
 
3
3
  import { ErrorMessage } from '@jbrowse/core/ui'
4
4
  import { getContainingView } from '@jbrowse/core/util'
@@ -19,8 +19,8 @@ import { makeStyles } from 'tss-react/mui'
19
19
  import CachedBlastResults from './CachedBlastResults'
20
20
  import { blastLaunchView } from './blastLaunchView'
21
21
  import { msaAlgorithms } from './consts'
22
+ import { useCachedBlastResults } from './useCachedBlastResults'
22
23
  import TextField2 from '../../../components/TextField2'
23
- import { getAllCachedResults } from '../../../utils/blastCache'
24
24
  import {
25
25
  getGeneDisplayName,
26
26
  getGeneIdentifiers,
@@ -83,24 +83,10 @@ const NCBIBlastAutomaticPanel = observer(function ({
83
83
  useState<MsaAlgorithm>('clustalo')
84
84
  const [selectedBlastProgram, setSelectedBlastProgram] =
85
85
  useState<blastProgramsT>('quick-blastp')
86
- const [hasCachedResults, setHasCachedResults] = useState(false)
87
- const [error, setError] = useState<unknown>()
88
86
 
89
87
  const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature])
90
- useEffect(() => {
91
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
92
- ;(async () => {
93
- try {
94
- const results = await getAllCachedResults()
95
- setHasCachedResults(
96
- results.some(r => r.geneId && geneIds.includes(r.geneId)),
97
- )
98
- } catch (e) {
99
- console.error(e)
100
- setError(e)
101
- }
102
- })()
103
- }, [geneIds])
88
+ const { results: cachedResults, error: cachedResultsError } =
89
+ useCachedBlastResults(geneIds)
104
90
 
105
91
  const {
106
92
  options,
@@ -110,7 +96,7 @@ const NCBIBlastAutomaticPanel = observer(function ({
110
96
  proteinSequence,
111
97
  error: proteinSequenceError,
112
98
  } = useTranscriptSelection({ feature, view })
113
- const e = proteinSequenceError ?? launchViewError ?? error
99
+ const e = proteinSequenceError ?? launchViewError ?? cachedResultsError
114
100
  return (
115
101
  <>
116
102
  <DialogContent className={classes.dialogContent}>
@@ -205,7 +191,7 @@ const NCBIBlastAutomaticPanel = observer(function ({
205
191
  submitting BLAST yourself and downloading the resulting files
206
192
  </Typography>
207
193
 
208
- {hasCachedResults ? (
194
+ {cachedResults.length > 0 ? (
209
195
  <Accordion className={classes.cachedResultsAccordion}>
210
196
  <AccordionSummary expandIcon={<ExpandMoreIcon />}>
211
197
  <Typography>Previous BLAST Results</Typography>
@@ -0,0 +1,49 @@
1
+ import useSWR from 'swr'
2
+
3
+ import {
4
+ clearAllCachedResults,
5
+ deleteCachedResult,
6
+ getAllCachedResults,
7
+ } from '../../../utils/blastCache'
8
+
9
+ const swrConfig = {
10
+ revalidateOnFocus: false,
11
+ revalidateOnReconnect: false,
12
+ revalidateIfStale: false,
13
+ }
14
+
15
+ export function useCachedBlastResults(geneIds: string[]) {
16
+ const {
17
+ data: results,
18
+ error,
19
+ mutate,
20
+ } = useSWR(
21
+ `cached-blast-${geneIds.join(',')}`,
22
+ async () => {
23
+ const cached = await getAllCachedResults()
24
+ return cached.filter(r => r.geneId && geneIds.includes(r.geneId))
25
+ },
26
+ swrConfig,
27
+ )
28
+
29
+ const handleDelete = async (id: string) => {
30
+ await deleteCachedResult(id)
31
+ await mutate(
32
+ results => results?.filter(result => result.id !== id) ?? [],
33
+ false,
34
+ )
35
+ }
36
+
37
+ const handleClearAll = async () => {
38
+ await clearAllCachedResults()
39
+ await mutate([], false)
40
+ }
41
+
42
+ return {
43
+ results: results ?? [],
44
+ error,
45
+ isLoading: !results && !error,
46
+ handleDelete,
47
+ handleClearAll,
48
+ }
49
+ }
@@ -1,4 +1,4 @@
1
- import { useEffect, useMemo, useState } from 'react'
1
+ import { useMemo, useState } from 'react'
2
2
 
3
3
  import { featureMatchesId, getId, getSortedTranscriptFeatures } from '../util'
4
4
  import { useFeatureSequence } from './useFeatureSequence'
@@ -9,6 +9,24 @@ function featureInValidIds(feature: Feature, validIds: string[]): boolean {
9
9
  return validIds.some(id => featureMatchesId(feature, id))
10
10
  }
11
11
 
12
+ function findValidSelection(
13
+ currentId: string,
14
+ options: Feature[],
15
+ validIds: string[] | undefined,
16
+ ): string | undefined {
17
+ if (!validIds || validIds.length === 0) {
18
+ return undefined
19
+ }
20
+
21
+ const currentFeature = options.find(opt => getId(opt) === currentId)
22
+ if (!currentFeature || featureInValidIds(currentFeature, validIds)) {
23
+ return undefined
24
+ }
25
+
26
+ const validOption = options.find(opt => featureInValidIds(opt, validIds))
27
+ return validOption ? getId(validOption) : undefined
28
+ }
29
+
12
30
  export function useTranscriptSelection({
13
31
  feature,
14
32
  view,
@@ -20,29 +38,19 @@ export function useTranscriptSelection({
20
38
  }) {
21
39
  const options = useMemo(() => getSortedTranscriptFeatures(feature), [feature])
22
40
  const [selectedId, setSelectedId] = useState(() => getId(options[0]))
23
- const selectedTranscript = options.find(val => getId(val) === selectedId)
41
+ const validatedSelectedId =
42
+ findValidSelection(selectedId, options, validIds) || selectedId
43
+ const selectedTranscript = options.find(
44
+ val => getId(val) === validatedSelectedId,
45
+ )
24
46
  const { proteinSequence, error } = useFeatureSequence({
25
47
  view,
26
48
  feature: selectedTranscript,
27
49
  })
28
50
 
29
- useEffect(() => {
30
- if (validIds && validIds.length > 0) {
31
- const currentFeature = options.find(opt => getId(opt) === selectedId)
32
- if (currentFeature && !featureInValidIds(currentFeature, validIds)) {
33
- const validOption = options.find(opt =>
34
- featureInValidIds(opt, validIds),
35
- )
36
- if (validOption) {
37
- setSelectedId(getId(validOption))
38
- }
39
- }
40
- }
41
- }, [validIds, options, selectedId])
42
-
43
51
  return {
44
52
  options,
45
- selectedId,
53
+ selectedId: validatedSelectedId,
46
54
  setSelectedId,
47
55
  selectedTranscript,
48
56
  proteinSequence,
@@ -13,6 +13,22 @@ import { getUniprotIdFromAlphaFoldUrl } from './util'
13
13
 
14
14
  import type { JBrowsePluginMsaViewModel } from './model'
15
15
 
16
+ interface ProteinView {
17
+ type: 'ProteinView'
18
+ id: string
19
+ structures: {
20
+ connectedViewId?: string
21
+ uniprotId?: string
22
+ structureSequences?: unknown[]
23
+ hoverGenomeHighlights?: { start: number; end: number }[]
24
+ }[]
25
+ }
26
+
27
+ function isProteinView(view: unknown): view is ProteinView {
28
+ const v = view as Record<string, unknown>
29
+ return v.type === 'ProteinView' && Array.isArray(v.structures)
30
+ }
31
+
16
32
  export function loadStoredData(self: JBrowsePluginMsaViewModel) {
17
33
  const { dataStoreId, rows } = self
18
34
  if (dataStoreId && rows.length === 0) {
@@ -202,10 +218,10 @@ export function autoConnectStructures(self: JBrowsePluginMsaViewModel) {
202
218
  }
203
219
 
204
220
  for (const view of views) {
205
- const v = view as any
206
- if (v.type !== 'ProteinView' || !v.structures) {
221
+ if (!isProteinView(view)) {
207
222
  continue
208
223
  }
224
+ const v = view
209
225
 
210
226
  for (
211
227
  let structureIdx = 0;
@@ -213,6 +229,9 @@ export function autoConnectStructures(self: JBrowsePluginMsaViewModel) {
213
229
  structureIdx++
214
230
  ) {
215
231
  const structure = v.structures[structureIdx]
232
+ if (!structure) {
233
+ continue
234
+ }
216
235
 
217
236
  if (structure.connectedViewId !== connectedViewId) {
218
237
  continue
@@ -250,13 +269,13 @@ export function observeProteinHighlights(self: JBrowsePluginMsaViewModel) {
250
269
  return
251
270
  }
252
271
 
253
- const columns: number[] = []
272
+ const columns = new Set<number>()
254
273
 
255
274
  for (const view of views) {
256
- const v = view as any
257
- if (v.type !== 'ProteinView' || !v.structures) {
275
+ if (!isProteinView(view)) {
258
276
  continue
259
277
  }
278
+ const v = view
260
279
 
261
280
  for (const structure of v.structures) {
262
281
  if (structure.connectedViewId !== connectedViewId) {
@@ -274,16 +293,14 @@ export function observeProteinHighlights(self: JBrowsePluginMsaViewModel) {
274
293
  const proteinPos = g2p[coord]
275
294
  if (proteinPos !== undefined) {
276
295
  const col = self.seqPosToGlobalCol(querySeqName, proteinPos)
277
- if (!columns.includes(col)) {
278
- columns.push(col)
279
- }
296
+ columns.add(col)
280
297
  }
281
298
  }
282
299
  }
283
300
  }
284
301
  }
285
302
 
286
- const visibleColumns = columns
303
+ const visibleColumns = Array.from(columns)
287
304
  .map(col => self.globalColToVisibleCol(col))
288
305
  .filter((col): col is number => col !== undefined)
289
306
 
@@ -0,0 +1,40 @@
1
+ import type { ReactNode } from 'react'
2
+ import React, { Component } from 'react'
3
+
4
+ import { ErrorMessage } from '@jbrowse/core/ui'
5
+
6
+ interface Props {
7
+ children: ReactNode
8
+ }
9
+
10
+ interface State {
11
+ hasError: boolean
12
+ error: unknown
13
+ }
14
+
15
+ export class ErrorBoundary extends Component<Props, State> {
16
+ constructor(props: Props) {
17
+ super(props)
18
+ this.state = { hasError: false, error: null }
19
+ }
20
+
21
+ static getDerivedStateFromError(error: unknown) {
22
+ return { hasError: true, error }
23
+ }
24
+
25
+ componentDidCatch(error: unknown, info: React.ErrorInfo) {
26
+ console.error('MsaViewPanel error:', error, info.componentStack)
27
+ }
28
+
29
+ render() {
30
+ if (this.state.hasError) {
31
+ return (
32
+ <div style={{ padding: 20 }}>
33
+ <ErrorMessage error={this.state.error} />
34
+ </div>
35
+ )
36
+ }
37
+
38
+ return this.props.children
39
+ }
40
+ }
@@ -4,6 +4,7 @@ import { LoadingEllipses } from '@jbrowse/core/ui'
4
4
  import { observer } from 'mobx-react'
5
5
  import { MSAView } from 'react-msaview'
6
6
 
7
+ import { ErrorBoundary } from './ErrorBoundary'
7
8
  import LoadingBLAST from './LoadingBLAST'
8
9
 
9
10
  import type { JBrowsePluginMsaViewModel } from '../model'
@@ -15,17 +16,19 @@ const MsaViewPanel = observer(function MsaViewPanel2({
15
16
  }) {
16
17
  const { blastParams, loadingStoredData } = model
17
18
  return (
18
- <div>
19
- {blastParams ? (
20
- <LoadingBLAST model={model} baseUrl={blastParams.baseUrl} />
21
- ) : loadingStoredData ? (
22
- <div style={{ padding: 20 }}>
23
- <LoadingEllipses message="Loading MSA data" variant="h6" />
24
- </div>
25
- ) : (
26
- <MSAView model={model} />
27
- )}
28
- </div>
19
+ <ErrorBoundary>
20
+ <div>
21
+ {blastParams ? (
22
+ <LoadingBLAST model={model} baseUrl={blastParams.baseUrl} />
23
+ ) : loadingStoredData ? (
24
+ <div style={{ padding: 20 }}>
25
+ <LoadingEllipses message="Loading MSA data" variant="h6" />
26
+ </div>
27
+ ) : (
28
+ <MSAView model={model} />
29
+ )}
30
+ </div>
31
+ </ErrorBoundary>
29
32
  )
30
33
  })
31
34