jbrowse-plugin-msaview 2.2.3 → 2.2.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 (162) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +229 -0
  3. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +23 -18
  4. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -1
  5. package/dist/AddHighlightModel/MsaToGenomeHighlight.js +23 -13
  6. package/dist/AddHighlightModel/MsaToGenomeHighlight.js.map +1 -1
  7. package/dist/AddHighlightModel/index.js +8 -1
  8. package/dist/AddHighlightModel/index.js.map +1 -1
  9. package/dist/AddHighlightModel/util.d.ts +2 -2
  10. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +2 -2
  11. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js +5 -11
  12. package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js.map +1 -1
  13. package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js +5 -1
  14. package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js.map +1 -1
  15. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +16 -16
  16. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -1
  17. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +38 -46
  18. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js.map +1 -1
  19. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +4 -3
  20. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +4 -3
  21. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js.map +1 -1
  22. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.d.ts +9 -0
  23. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js +76 -0
  24. package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js.map +1 -0
  25. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js +35 -13
  26. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js.map +1 -1
  27. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js +6 -12
  28. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js.map +1 -1
  29. package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.d.ts +6 -0
  30. package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js +15 -0
  31. package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js.map +1 -1
  32. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +12 -34
  33. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -1
  34. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.d.ts +1 -0
  35. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js +1 -0
  36. package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js.map +1 -1
  37. package/dist/LaunchMsaView/components/TabPanel.d.ts +2 -2
  38. package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +2 -2
  39. package/dist/LaunchMsaView/components/TranscriptSelector.js +3 -6
  40. package/dist/LaunchMsaView/components/TranscriptSelector.js.map +1 -1
  41. package/dist/LaunchMsaView/components/useSWRFeatureSequence.js +6 -4
  42. package/dist/LaunchMsaView/components/useSWRFeatureSequence.js.map +1 -1
  43. package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +16 -0
  44. package/dist/LaunchMsaView/components/useTranscriptSelection.js +31 -0
  45. package/dist/LaunchMsaView/components/useTranscriptSelection.js.map +1 -0
  46. package/dist/LaunchMsaView/components/util.d.ts +3 -1
  47. package/dist/LaunchMsaView/components/util.js +12 -2
  48. package/dist/LaunchMsaView/components/util.js.map +1 -1
  49. package/dist/LaunchMsaView/util.d.ts +2 -0
  50. package/dist/LaunchMsaView/util.js +16 -4
  51. package/dist/LaunchMsaView/util.js.map +1 -1
  52. package/dist/LaunchMsaViewExtensionPoint/index.d.ts +2 -0
  53. package/dist/LaunchMsaViewExtensionPoint/index.js +31 -0
  54. package/dist/LaunchMsaViewExtensionPoint/index.js.map +1 -0
  55. package/dist/MsaViewPanel/components/ConnectStructureDialog.d.ts +7 -0
  56. package/dist/MsaViewPanel/components/ConnectStructureDialog.js +56 -0
  57. package/dist/MsaViewPanel/components/ConnectStructureDialog.js.map +1 -0
  58. package/dist/MsaViewPanel/components/MsaViewPanel.js +4 -2
  59. package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -1
  60. package/dist/MsaViewPanel/doLaunchBlast.d.ts +1 -0
  61. package/dist/MsaViewPanel/doLaunchBlast.js +65 -19
  62. package/dist/MsaViewPanel/doLaunchBlast.js.map +1 -1
  63. package/dist/MsaViewPanel/genomeToMSA.d.ts +6 -0
  64. package/dist/MsaViewPanel/genomeToMSA.js +38 -8
  65. package/dist/MsaViewPanel/genomeToMSA.js.map +1 -1
  66. package/dist/MsaViewPanel/genomeToMSA.test.d.ts +1 -0
  67. package/dist/MsaViewPanel/genomeToMSA.test.js +244 -0
  68. package/dist/MsaViewPanel/genomeToMSA.test.js.map +1 -0
  69. package/dist/MsaViewPanel/model.d.ts +719 -226
  70. package/dist/MsaViewPanel/model.js +467 -39
  71. package/dist/MsaViewPanel/model.js.map +1 -1
  72. package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +7 -2
  73. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +26 -27
  74. package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -1
  75. package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.d.ts +1 -0
  76. package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js +240 -0
  77. package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js.map +1 -0
  78. package/dist/MsaViewPanel/msaDataStore.d.ts +14 -0
  79. package/dist/MsaViewPanel/msaDataStore.js +197 -0
  80. package/dist/MsaViewPanel/msaDataStore.js.map +1 -0
  81. package/dist/MsaViewPanel/pairwiseAlignment.d.ts +27 -0
  82. package/dist/MsaViewPanel/pairwiseAlignment.js +776 -0
  83. package/dist/MsaViewPanel/pairwiseAlignment.js.map +1 -0
  84. package/dist/MsaViewPanel/pairwiseAlignment.test.d.ts +1 -0
  85. package/dist/MsaViewPanel/pairwiseAlignment.test.js +112 -0
  86. package/dist/MsaViewPanel/pairwiseAlignment.test.js.map +1 -0
  87. package/dist/MsaViewPanel/structureConnection.d.ts +27 -0
  88. package/dist/MsaViewPanel/structureConnection.js +46 -0
  89. package/dist/MsaViewPanel/structureConnection.js.map +1 -0
  90. package/dist/MsaViewPanel/structureConnection.test.d.ts +1 -0
  91. package/dist/MsaViewPanel/structureConnection.test.js +122 -0
  92. package/dist/MsaViewPanel/structureConnection.test.js.map +1 -0
  93. package/dist/MsaViewPanel/types.d.ts +13 -0
  94. package/dist/MsaViewPanel/types.js +2 -0
  95. package/dist/MsaViewPanel/types.js.map +1 -0
  96. package/dist/MsaViewPanel/util.d.ts +7 -0
  97. package/dist/MsaViewPanel/util.js +10 -0
  98. package/dist/MsaViewPanel/util.js.map +1 -1
  99. package/dist/index.d.ts +5 -5
  100. package/dist/index.js +3 -1
  101. package/dist/index.js.map +1 -1
  102. package/dist/jbrowse-plugin-msaview.umd.production.min.js +39 -90
  103. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  104. package/dist/utils/blastCache.d.ts +34 -0
  105. package/dist/utils/blastCache.js +58 -0
  106. package/dist/utils/blastCache.js.map +1 -0
  107. package/dist/utils/fetch.d.ts +1 -1
  108. package/dist/utils/fetch.js +1 -1
  109. package/dist/utils/fetch.js.map +1 -1
  110. package/dist/utils/ncbiBlast.d.ts +1 -5
  111. package/dist/utils/taxonomyNames.d.ts +5 -0
  112. package/dist/utils/taxonomyNames.js +79 -0
  113. package/dist/utils/taxonomyNames.js.map +1 -0
  114. package/dist/utils/types.d.ts +8 -5
  115. package/package.json +50 -54
  116. package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +37 -21
  117. package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +38 -17
  118. package/src/AddHighlightModel/index.tsx +9 -4
  119. package/src/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.tsx +13 -13
  120. package/src/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.ts +6 -0
  121. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +30 -23
  122. package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +64 -51
  123. package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +9 -6
  124. package/src/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.tsx +146 -0
  125. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx +53 -22
  126. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.tsx +8 -13
  127. package/src/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.ts +25 -0
  128. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +27 -47
  129. package/src/LaunchMsaView/components/PreLoadedMSA/consts.ts +1 -0
  130. package/src/LaunchMsaView/components/TabPanel.tsx +2 -2
  131. package/src/LaunchMsaView/components/TranscriptSelector.tsx +13 -20
  132. package/src/LaunchMsaView/components/useSWRFeatureSequence.ts +5 -5
  133. package/src/LaunchMsaView/components/useTranscriptSelection.ts +48 -0
  134. package/src/LaunchMsaView/components/util.ts +17 -2
  135. package/src/LaunchMsaView/index.ts +1 -1
  136. package/src/LaunchMsaView/util.ts +25 -6
  137. package/src/LaunchMsaViewExtensionPoint/index.ts +74 -0
  138. package/src/MsaViewPanel/components/ConnectStructureDialog.tsx +156 -0
  139. package/src/MsaViewPanel/components/MsaViewPanel.tsx +6 -1
  140. package/src/MsaViewPanel/doLaunchBlast.ts +83 -23
  141. package/src/MsaViewPanel/genomeToMSA.test.ts +281 -0
  142. package/src/MsaViewPanel/genomeToMSA.ts +43 -10
  143. package/src/MsaViewPanel/model.ts +590 -43
  144. package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +256 -0
  145. package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +43 -29
  146. package/src/MsaViewPanel/msaDataStore.ts +236 -0
  147. package/src/MsaViewPanel/pairwiseAlignment.test.ts +140 -0
  148. package/src/MsaViewPanel/pairwiseAlignment.ts +818 -0
  149. package/src/MsaViewPanel/structureConnection.test.ts +143 -0
  150. package/src/MsaViewPanel/structureConnection.ts +72 -0
  151. package/src/MsaViewPanel/types.ts +14 -0
  152. package/src/MsaViewPanel/util.ts +11 -0
  153. package/src/index.ts +3 -1
  154. package/src/utils/blastCache.ts +114 -0
  155. package/src/utils/fetch.ts +1 -1
  156. package/src/utils/taxonomyNames.ts +111 -0
  157. package/src/utils/types.ts +9 -1
  158. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.d.ts +0 -5
  159. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js +0 -16
  160. package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js.map +0 -1
  161. package/dist/out.js +0 -55381
  162. package/src/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.ts +0 -25
@@ -0,0 +1,143 @@
1
+ import { describe, expect, test } from 'vitest'
2
+
3
+ import {
4
+ gappedToUngappedPosition,
5
+ mapToRecord,
6
+ ungappedToGappedPosition,
7
+ } from './structureConnection'
8
+
9
+ describe('gappedToUngappedPosition', () => {
10
+ test('returns correct ungapped position for non-gap character', () => {
11
+ const seq = 'M-KA-A'
12
+ // 0 12 34 (gapped positions)
13
+ // 0 1 2 (ungapped positions for M, K, A, A)
14
+ expect(gappedToUngappedPosition(seq, 0)).toBe(0) // M -> 0
15
+ expect(gappedToUngappedPosition(seq, 2)).toBe(1) // K -> 1
16
+ expect(gappedToUngappedPosition(seq, 3)).toBe(2) // A -> 2
17
+ expect(gappedToUngappedPosition(seq, 5)).toBe(3) // A -> 3
18
+ })
19
+
20
+ test('returns undefined for gap position', () => {
21
+ const seq = 'M-KA-A'
22
+ expect(gappedToUngappedPosition(seq, 1)).toBeUndefined() // gap
23
+ expect(gappedToUngappedPosition(seq, 4)).toBeUndefined() // gap
24
+ })
25
+
26
+ test('returns undefined for out-of-bounds position', () => {
27
+ const seq = 'MKA'
28
+ expect(gappedToUngappedPosition(seq, -1)).toBeUndefined()
29
+ expect(gappedToUngappedPosition(seq, 3)).toBeUndefined()
30
+ expect(gappedToUngappedPosition(seq, 100)).toBeUndefined()
31
+ })
32
+
33
+ test('handles sequence with no gaps', () => {
34
+ const seq = 'MKAA'
35
+ expect(gappedToUngappedPosition(seq, 0)).toBe(0)
36
+ expect(gappedToUngappedPosition(seq, 1)).toBe(1)
37
+ expect(gappedToUngappedPosition(seq, 2)).toBe(2)
38
+ expect(gappedToUngappedPosition(seq, 3)).toBe(3)
39
+ })
40
+
41
+ test('handles sequence with leading gaps', () => {
42
+ const seq = '--MKA'
43
+ expect(gappedToUngappedPosition(seq, 0)).toBeUndefined()
44
+ expect(gappedToUngappedPosition(seq, 1)).toBeUndefined()
45
+ expect(gappedToUngappedPosition(seq, 2)).toBe(0) // M
46
+ expect(gappedToUngappedPosition(seq, 3)).toBe(1) // K
47
+ })
48
+
49
+ test('handles sequence with trailing gaps', () => {
50
+ const seq = 'MKA--'
51
+ expect(gappedToUngappedPosition(seq, 2)).toBe(2) // A
52
+ expect(gappedToUngappedPosition(seq, 3)).toBeUndefined()
53
+ expect(gappedToUngappedPosition(seq, 4)).toBeUndefined()
54
+ })
55
+
56
+ test('handles empty sequence', () => {
57
+ expect(gappedToUngappedPosition('', 0)).toBeUndefined()
58
+ })
59
+
60
+ test('handles all-gap sequence', () => {
61
+ const seq = '---'
62
+ expect(gappedToUngappedPosition(seq, 0)).toBeUndefined()
63
+ expect(gappedToUngappedPosition(seq, 1)).toBeUndefined()
64
+ expect(gappedToUngappedPosition(seq, 2)).toBeUndefined()
65
+ })
66
+ })
67
+
68
+ describe('ungappedToGappedPosition', () => {
69
+ test('returns correct gapped position', () => {
70
+ const seq = 'M-KA-A'
71
+ // 0 12 34 (gapped)
72
+ // 0 1 23 (ungapped)
73
+ expect(ungappedToGappedPosition(seq, 0)).toBe(0) // M
74
+ expect(ungappedToGappedPosition(seq, 1)).toBe(2) // K
75
+ expect(ungappedToGappedPosition(seq, 2)).toBe(3) // A
76
+ expect(ungappedToGappedPosition(seq, 3)).toBe(5) // A
77
+ })
78
+
79
+ test('returns undefined for out-of-bounds ungapped position', () => {
80
+ const seq = 'M-KA'
81
+ expect(ungappedToGappedPosition(seq, 4)).toBeUndefined()
82
+ expect(ungappedToGappedPosition(seq, 100)).toBeUndefined()
83
+ })
84
+
85
+ test('handles sequence with no gaps', () => {
86
+ const seq = 'MKAA'
87
+ expect(ungappedToGappedPosition(seq, 0)).toBe(0)
88
+ expect(ungappedToGappedPosition(seq, 1)).toBe(1)
89
+ expect(ungappedToGappedPosition(seq, 2)).toBe(2)
90
+ expect(ungappedToGappedPosition(seq, 3)).toBe(3)
91
+ })
92
+
93
+ test('handles sequence with leading gaps', () => {
94
+ const seq = '--MKA'
95
+ expect(ungappedToGappedPosition(seq, 0)).toBe(2) // M
96
+ expect(ungappedToGappedPosition(seq, 1)).toBe(3) // K
97
+ expect(ungappedToGappedPosition(seq, 2)).toBe(4) // A
98
+ })
99
+
100
+ test('handles empty sequence', () => {
101
+ expect(ungappedToGappedPosition('', 0)).toBeUndefined()
102
+ })
103
+
104
+ test('handles all-gap sequence', () => {
105
+ const seq = '---'
106
+ expect(ungappedToGappedPosition(seq, 0)).toBeUndefined()
107
+ })
108
+ })
109
+
110
+ describe('gappedToUngappedPosition and ungappedToGappedPosition are inverses', () => {
111
+ test('round-trip conversion works', () => {
112
+ const seq = 'M-KA--YL-S'
113
+ // For each non-gap position, converting to ungapped and back should return original
114
+ for (let i = 0; i < seq.length; i++) {
115
+ if (seq[i] !== '-') {
116
+ const ungapped = gappedToUngappedPosition(seq, i)
117
+ expect(ungapped).toBeDefined()
118
+ const backToGapped = ungappedToGappedPosition(seq, ungapped!)
119
+ expect(backToGapped).toBe(i)
120
+ }
121
+ }
122
+ })
123
+ })
124
+
125
+ describe('mapToRecord', () => {
126
+ test('converts Map to Record', () => {
127
+ const map = new Map<number, number>([
128
+ [0, 5],
129
+ [1, 10],
130
+ [2, 15],
131
+ ])
132
+ const record = mapToRecord(map)
133
+ expect(record[0]).toBe(5)
134
+ expect(record[1]).toBe(10)
135
+ expect(record[2]).toBe(15)
136
+ })
137
+
138
+ test('handles empty Map', () => {
139
+ const map = new Map<number, number>()
140
+ const record = mapToRecord(map)
141
+ expect(Object.keys(record)).toHaveLength(0)
142
+ })
143
+ })
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Represents a connection between the MSA view and a protein structure
3
+ */
4
+ export interface StructureConnection {
5
+ /** ID of the ProteinView containing the structure */
6
+ proteinViewId: string
7
+ /** Index of the structure within the ProteinView's structures array */
8
+ structureIdx: number
9
+ /** Name of the MSA row that corresponds to this structure */
10
+ msaRowName: string
11
+ /** Map from MSA ungapped position to structure sequence position */
12
+ msaToStructure: Record<number, number>
13
+ /** Map from structure sequence position to MSA ungapped position */
14
+ structureToMsa: Record<number, number>
15
+ }
16
+
17
+ /**
18
+ * Helper to convert gapped MSA column to ungapped position for a specific row
19
+ */
20
+ export function gappedToUngappedPosition(
21
+ sequence: string,
22
+ gappedPosition: number,
23
+ ): number | undefined {
24
+ if (gappedPosition < 0 || gappedPosition >= sequence.length) {
25
+ return undefined
26
+ }
27
+
28
+ let ungapped = 0
29
+ for (let i = 0; i < gappedPosition; i++) {
30
+ if (sequence[i] !== '-') {
31
+ ungapped++
32
+ }
33
+ }
34
+
35
+ // If the position itself is a gap, return undefined
36
+ if (sequence[gappedPosition] === '-') {
37
+ return undefined
38
+ }
39
+
40
+ return ungapped
41
+ }
42
+
43
+ /**
44
+ * Helper to convert ungapped position to gapped MSA column for a specific row
45
+ */
46
+ export function ungappedToGappedPosition(
47
+ sequence: string,
48
+ ungappedPosition: number,
49
+ ): number | undefined {
50
+ let ungapped = 0
51
+ for (let i = 0; i < sequence.length; i++) {
52
+ const element = sequence[i]
53
+ if (element !== '-') {
54
+ if (ungapped === ungappedPosition) {
55
+ return i
56
+ }
57
+ ungapped++
58
+ }
59
+ }
60
+ return undefined
61
+ }
62
+
63
+ /**
64
+ * Convert Map to plain object for MST frozen storage
65
+ */
66
+ export function mapToRecord(map: Map<number, number>): Record<number, number> {
67
+ const record: Record<number, number> = {}
68
+ for (const [key, value] of map) {
69
+ record[key] = value
70
+ }
71
+ return record
72
+ }
@@ -0,0 +1,14 @@
1
+ export interface MsaViewInitState {
2
+ msaData?: string
3
+ msaUrl?: string
4
+ treeData?: string
5
+ treeUrl?: string
6
+ querySeqName?: string
7
+ }
8
+
9
+ export interface MafRegion {
10
+ refName: string
11
+ start: number
12
+ end: number
13
+ assemblyName: string
14
+ }
@@ -11,3 +11,14 @@ export function checkHovered(hovered: unknown): hovered is {
11
11
  'hoverPosition' in hovered
12
12
  )
13
13
  }
14
+
15
+ /**
16
+ * Extracts UniProt ID from an AlphaFold URL
17
+ * Examples:
18
+ * - https://alphafold.ebi.ac.uk/files/AF-P12345-F1-model_v6.cif -> P12345
19
+ * - https://alphafold.ebi.ac.uk/files/msa/AF-P12345-F1-msa_v6.a3m -> P12345
20
+ */
21
+ export function getUniprotIdFromAlphaFoldUrl(url: string) {
22
+ const match = /AF-([A-Z0-9]+)-F\d+/.exec(url)
23
+ return match?.[1]
24
+ }
package/src/index.ts CHANGED
@@ -2,13 +2,14 @@ import Plugin from '@jbrowse/core/Plugin'
2
2
  import PluginManager from '@jbrowse/core/PluginManager'
3
3
  import { ConfigurationSchema } from '@jbrowse/core/configuration'
4
4
  import { AbstractSessionModel, isAbstractMenuManager } from '@jbrowse/core/util'
5
+ import { types } from '@jbrowse/mobx-state-tree'
5
6
  import GridOn from '@mui/icons-material/GridOn'
6
- import { types } from 'mobx-state-tree'
7
7
 
8
8
  import { version } from '../package.json'
9
9
  import AddHighlightModelF from './AddHighlightModel'
10
10
  import BgzipFastaMsaAdapterF from './BgzipFastaMsaAdapter'
11
11
  import LaunchMsaViewF from './LaunchMsaView'
12
+ import LaunchMsaViewExtensionPointF from './LaunchMsaViewExtensionPoint'
12
13
  import MsaViewF from './MsaViewPanel'
13
14
 
14
15
  export default class MsaViewPlugin extends Plugin {
@@ -18,6 +19,7 @@ export default class MsaViewPlugin extends Plugin {
18
19
  install(pluginManager: PluginManager) {
19
20
  MsaViewF(pluginManager)
20
21
  LaunchMsaViewF(pluginManager)
22
+ LaunchMsaViewExtensionPointF(pluginManager)
21
23
  AddHighlightModelF(pluginManager)
22
24
  BgzipFastaMsaAdapterF(pluginManager)
23
25
  }
@@ -0,0 +1,114 @@
1
+ import { openDB } from 'idb'
2
+
3
+ const DB_NAME = 'jbrowse-msaview-blast-cache'
4
+ const STORE_NAME = 'blast-results'
5
+ const DB_VERSION = 2
6
+
7
+ export interface CachedBlastResult {
8
+ id: string
9
+ proteinSequence: string
10
+ blastDatabase: string
11
+ blastProgram: string
12
+ msaAlgorithm: string
13
+ msa: string
14
+ tree: string
15
+ treeMetadata: string
16
+ rid: string
17
+ timestamp: number
18
+ geneId?: string
19
+ transcriptId?: string
20
+ }
21
+
22
+ async function getDB() {
23
+ return openDB(DB_NAME, DB_VERSION, {
24
+ upgrade(db, oldVersion) {
25
+ if (oldVersion < 2 && db.objectStoreNames.contains(STORE_NAME)) {
26
+ db.deleteObjectStore(STORE_NAME)
27
+ }
28
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
29
+ db.createObjectStore(STORE_NAME, { keyPath: 'id' })
30
+ }
31
+ },
32
+ })
33
+ }
34
+
35
+ function createCacheKey(
36
+ proteinSequence: string,
37
+ blastDatabase: string,
38
+ blastProgram: string,
39
+ ) {
40
+ return `${blastDatabase}:${blastProgram}:${proteinSequence.slice(0, 100)}`
41
+ }
42
+
43
+ export async function getCachedBlastResult({
44
+ proteinSequence,
45
+ blastDatabase,
46
+ blastProgram,
47
+ }: {
48
+ proteinSequence: string
49
+ blastDatabase: string
50
+ blastProgram: string
51
+ }) {
52
+ const db = await getDB()
53
+ const id = createCacheKey(proteinSequence, blastDatabase, blastProgram)
54
+ return db.get(STORE_NAME, id)
55
+ }
56
+
57
+ export async function saveBlastResult({
58
+ proteinSequence,
59
+ blastDatabase,
60
+ blastProgram,
61
+ msaAlgorithm,
62
+ msa,
63
+ tree,
64
+ treeMetadata,
65
+ rid,
66
+ geneId,
67
+ transcriptId,
68
+ }: {
69
+ proteinSequence: string
70
+ blastDatabase: string
71
+ blastProgram: string
72
+ msaAlgorithm: string
73
+ msa: string
74
+ tree: string
75
+ treeMetadata: string
76
+ rid: string
77
+ geneId?: string
78
+ transcriptId?: string
79
+ }) {
80
+ const db = await getDB()
81
+ const id = createCacheKey(proteinSequence, blastDatabase, blastProgram)
82
+ const entry: CachedBlastResult = {
83
+ id,
84
+ proteinSequence,
85
+ blastDatabase,
86
+ blastProgram,
87
+ msaAlgorithm,
88
+ msa,
89
+ tree,
90
+ treeMetadata,
91
+ rid,
92
+ timestamp: Date.now(),
93
+ geneId,
94
+ transcriptId,
95
+ }
96
+ await db.put(STORE_NAME, entry)
97
+ return entry
98
+ }
99
+
100
+ export async function getAllCachedResults() {
101
+ const db = await getDB()
102
+ const results = await db.getAll(STORE_NAME)
103
+ return results.toSorted((a, b) => b.timestamp - a.timestamp)
104
+ }
105
+
106
+ export async function deleteCachedResult(id: string) {
107
+ const db = await getDB()
108
+ await db.delete(STORE_NAME, id)
109
+ }
110
+
111
+ export async function clearAllCachedResults() {
112
+ const db = await getDB()
113
+ await db.clear(STORE_NAME)
114
+ }
@@ -1,4 +1,4 @@
1
- import { ungzip } from 'pako'
1
+ import { ungzip } from 'pako-esm2'
2
2
 
3
3
  export async function handleFetch(url: string, args?: RequestInit) {
4
4
  const response = await fetch(url, args)
@@ -0,0 +1,111 @@
1
+ import { openDB } from 'idb'
2
+
3
+ const DB_NAME = 'jbrowse-msaview-taxonomy-cache'
4
+ const STORE_NAME = 'common-names'
5
+ const DB_VERSION = 1
6
+
7
+ interface CachedTaxonomy {
8
+ taxid: number
9
+ sciname: string
10
+ commonName?: string
11
+ }
12
+
13
+ async function getDB() {
14
+ return openDB(DB_NAME, DB_VERSION, {
15
+ upgrade(db) {
16
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
17
+ db.createObjectStore(STORE_NAME, { keyPath: 'taxid' })
18
+ }
19
+ },
20
+ })
21
+ }
22
+
23
+ async function getCachedCommonName(taxid: number) {
24
+ const db = await getDB()
25
+ return db.get(STORE_NAME, taxid) as Promise<CachedTaxonomy | undefined>
26
+ }
27
+
28
+ async function saveTaxonomyCache(entries: CachedTaxonomy[]) {
29
+ const db = await getDB()
30
+ const tx = db.transaction(STORE_NAME, 'readwrite')
31
+ for (const entry of entries) {
32
+ await tx.store.put(entry)
33
+ }
34
+ await tx.done
35
+ }
36
+
37
+ export interface TaxonomyInfo {
38
+ sciname: string
39
+ commonName?: string
40
+ }
41
+
42
+ export async function fetchTaxonomyInfo(
43
+ taxids: number[],
44
+ ): Promise<Map<number, TaxonomyInfo>> {
45
+ const result = new Map<number, TaxonomyInfo>()
46
+ const uncachedTaxids: number[] = []
47
+
48
+ for (const taxid of taxids) {
49
+ const cached = await getCachedCommonName(taxid)
50
+ if (cached) {
51
+ result.set(taxid, {
52
+ sciname: cached.sciname,
53
+ commonName: cached.commonName,
54
+ })
55
+ } else {
56
+ uncachedTaxids.push(taxid)
57
+ }
58
+ }
59
+
60
+ if (uncachedTaxids.length === 0) {
61
+ return result
62
+ }
63
+
64
+ const batchSize = 100
65
+ const toCache: CachedTaxonomy[] = []
66
+
67
+ for (let i = 0; i < uncachedTaxids.length; i += batchSize) {
68
+ const batch = uncachedTaxids.slice(i, i + batchSize)
69
+ const idsParam = batch.join(',')
70
+
71
+ try {
72
+ const response = await fetch(
73
+ `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=taxonomy&id=${idsParam}&retmode=xml`,
74
+ )
75
+ const text = await response.text()
76
+
77
+ for (const taxid of batch) {
78
+ const taxonRegex = new RegExp(
79
+ `<Taxon>.*?<TaxId>${taxid}</TaxId>.*?</Taxon>`,
80
+ 's',
81
+ )
82
+ const taxonMatch = taxonRegex.exec(text)
83
+
84
+ if (taxonMatch) {
85
+ const taxonXml = taxonMatch[0]
86
+ const genbankCommon =
87
+ /<GenbankCommonName>(.*?)<\/GenbankCommonName>/.exec(taxonXml)
88
+ const commonName = /<CommonName>(.*?)<\/CommonName>/.exec(taxonXml)
89
+ const sciName = /<ScientificName>(.*?)<\/ScientificName>/.exec(
90
+ taxonXml,
91
+ )
92
+ const name = genbankCommon?.[1] ?? commonName?.[1]
93
+
94
+ const sci = sciName?.[1] ?? ''
95
+ result.set(taxid, { sciname: sci, commonName: name })
96
+ toCache.push({ taxid, sciname: sci, commonName: name })
97
+ } else {
98
+ toCache.push({ taxid, sciname: '', commonName: undefined })
99
+ }
100
+ }
101
+ } catch (error) {
102
+ console.error('Failed to fetch taxonomy data:', error)
103
+ }
104
+ }
105
+
106
+ if (toCache.length > 0) {
107
+ await saveTaxonomyCache(toCache)
108
+ }
109
+
110
+ return result
111
+ }
@@ -1,10 +1,18 @@
1
+ export interface BlastHitDescription {
2
+ accession: string
3
+ id: string
4
+ sciname: string
5
+ taxid?: number
6
+ title?: string
7
+ }
8
+
1
9
  export interface BlastResults {
2
10
  BlastOutput2: {
3
11
  report: {
4
12
  results: {
5
13
  search: {
6
14
  hits: {
7
- description: { accession: string; id: string; sciname: string }[]
15
+ description: BlastHitDescription[]
8
16
  hsps: { hseq: string }[]
9
17
  }[]
10
18
  }
@@ -1,5 +0,0 @@
1
- import type { Feature } from '@jbrowse/core/util';
2
- export declare function findValidTranscriptId({ transcriptsList, validMsaList, }: {
3
- transcriptsList: Feature[];
4
- validMsaList?: string[];
5
- }): string | null;
@@ -1,16 +0,0 @@
1
- import { getId } from '../../util';
2
- // Function to find a valid transcript ID that exists in the MSA list
3
- export function findValidTranscriptId({ transcriptsList, validMsaList, }) {
4
- if (!validMsaList || validMsaList.length === 0) {
5
- return null;
6
- }
7
- // Try to find a transcript ID that exists in the MSA list
8
- for (const transcript of transcriptsList) {
9
- const id = getId(transcript);
10
- if (id && validMsaList.includes(id)) {
11
- return id;
12
- }
13
- }
14
- return null;
15
- }
16
- //# sourceMappingURL=findValidTranscriptId.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"findValidTranscriptId.js","sourceRoot":"","sources":["../../../../src/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAIlC,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,EACpC,eAAe,EACf,YAAY,GAIb;IACC,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAA;IACb,CAAC;IAED,0DAA0D;IAC1D,KAAK,MAAM,UAAU,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,CAAA;QAC5B,IAAI,EAAE,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}