@apollo-annotation/jbrowse-plugin-apollo 1.0.0 → 1.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 (172) hide show
  1. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts +8 -2
  2. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts.map +1 -1
  3. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts +8 -3
  4. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts.map +1 -1
  5. package/dist/ApolloInternetAccount/configSchema.d.ts +44 -0
  6. package/dist/ApolloInternetAccount/configSchema.d.ts.map +1 -0
  7. package/dist/ApolloInternetAccount/model.d.ts +174 -0
  8. package/dist/ApolloInternetAccount/model.d.ts.map +1 -0
  9. package/dist/ApolloJobModel.d.ts.map +1 -1
  10. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts +8 -0
  11. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts.map +1 -0
  12. package/dist/ApolloSequenceAdapter/configSchema.d.ts +8 -0
  13. package/dist/ApolloSequenceAdapter/configSchema.d.ts.map +1 -0
  14. package/dist/ApolloTextSearchAdapter/configSchema.d.ts +20 -0
  15. package/dist/ApolloTextSearchAdapter/configSchema.d.ts.map +1 -0
  16. package/dist/BackendDrivers/CollaborationServerDriver.d.ts +1 -1
  17. package/dist/BackendDrivers/CollaborationServerDriver.d.ts.map +1 -1
  18. package/dist/BackendDrivers/LocalDriver/LocalDriver.d.ts.map +1 -1
  19. package/dist/FeatureDetailsWidget/ApolloFeatureDetailsWidget.d.ts.map +1 -1
  20. package/dist/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.d.ts.map +1 -1
  21. package/dist/FeatureDetailsWidget/Attributes.d.ts.map +1 -1
  22. package/dist/FeatureDetailsWidget/DefaultAttributeEditor.d.ts.map +1 -1
  23. package/dist/FeatureDetailsWidget/DefaultAttributeViewer.d.ts.map +1 -1
  24. package/dist/FeatureDetailsWidget/NumberTextField.d.ts.map +1 -1
  25. package/dist/FeatureDetailsWidget/StringTextField.d.ts.map +1 -1
  26. package/dist/FeatureDetailsWidget/TranscriptSequence.d.ts.map +1 -1
  27. package/dist/FeatureDetailsWidget/TranscriptWidgetEditLocation.d.ts.map +1 -1
  28. package/dist/FeatureDetailsWidget/Translation.d.ts +14 -0
  29. package/dist/FeatureDetailsWidget/Translation.d.ts.map +1 -0
  30. package/dist/LinearApolloDisplay/components/CheckResultWarnings.d.ts.map +1 -1
  31. package/dist/LinearApolloDisplay/configSchema.d.ts +2 -0
  32. package/dist/LinearApolloDisplay/configSchema.d.ts.map +1 -0
  33. package/dist/LinearApolloDisplay/glyphs/ExonGlyph.d.ts.map +1 -1
  34. package/dist/LinearApolloDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  35. package/dist/LinearApolloDisplay/glyphs/TranscriptGlyph.d.ts.map +1 -1
  36. package/dist/LinearApolloDisplay/stateModel/base.d.ts +1 -18
  37. package/dist/LinearApolloDisplay/stateModel/base.d.ts.map +1 -1
  38. package/dist/LinearApolloDisplay/stateModel/index.d.ts +1 -17
  39. package/dist/LinearApolloDisplay/stateModel/index.d.ts.map +1 -1
  40. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts +2 -19
  41. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts.map +1 -1
  42. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts +2 -34
  43. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  44. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts +2 -19
  45. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts.map +1 -1
  46. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts +2 -0
  47. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts.map +1 -0
  48. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts +1 -1
  49. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts.map +1 -1
  50. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/index.d.ts +1 -1
  51. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/rendering.d.ts +1 -1
  52. package/dist/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.d.ts.map +1 -1
  53. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts +2 -0
  54. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts.map +1 -0
  55. package/dist/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  56. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts +1 -1
  57. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts.map +1 -1
  58. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts +1 -1
  59. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts.map +1 -1
  60. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts +2 -3
  61. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts.map +1 -1
  62. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts +2 -2
  63. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  64. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts +2 -3
  65. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts.map +1 -1
  66. package/dist/OntologyManager/OntologyStore/fulltext.d.ts.map +1 -1
  67. package/dist/OntologyManager/index.d.ts +734 -0
  68. package/dist/OntologyManager/index.d.ts.map +1 -0
  69. package/dist/TabularEditor/HybridGrid/NumberCell.d.ts.map +1 -1
  70. package/dist/components/AddCodingSequence.d.ts +14 -0
  71. package/dist/components/AddCodingSequence.d.ts.map +1 -0
  72. package/dist/components/AddRefSeqAliases.d.ts.map +1 -1
  73. package/dist/components/CopyFeature.d.ts.map +1 -1
  74. package/dist/components/CreateApolloAnnotation.d.ts.map +1 -1
  75. package/dist/components/DeleteFeature.d.ts.map +1 -1
  76. package/dist/components/DownloadGFF3.d.ts.map +1 -1
  77. package/dist/components/DuplicateFeatureDialog.d.ts +12 -0
  78. package/dist/components/DuplicateFeatureDialog.d.ts.map +1 -0
  79. package/dist/components/ImportAnnotations.d.ts +9 -0
  80. package/dist/components/ImportAnnotations.d.ts.map +1 -0
  81. package/dist/components/ManageUsers.d.ts.map +1 -1
  82. package/dist/components/OntologyTermMultiSelect.d.ts.map +1 -1
  83. package/dist/components/ViewChangeLog.d.ts.map +1 -1
  84. package/dist/components/index.d.ts +2 -0
  85. package/dist/components/index.d.ts.map +1 -1
  86. package/dist/extensions/annotationFromPileup.d.ts.map +1 -1
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.esm.js +830 -434
  89. package/dist/index.esm.js.map +1 -1
  90. package/dist/jbrowse-plugin-apollo.cjs.development.js +825 -429
  91. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  92. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  93. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  94. package/dist/jbrowse-plugin-apollo.umd.development.js +863 -468
  95. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  96. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  97. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  98. package/dist/session/ClientDataStore.d.ts +2194 -0
  99. package/dist/session/ClientDataStore.d.ts.map +1 -0
  100. package/dist/session/handleApolloFeaturesUrlParam.d.ts +8 -0
  101. package/dist/session/handleApolloFeaturesUrlParam.d.ts.map +1 -0
  102. package/dist/session/session.d.ts +6788 -0
  103. package/dist/session/session.d.ts.map +1 -0
  104. package/dist/util/displayUtils.d.ts +36 -0
  105. package/dist/util/displayUtils.d.ts.map +1 -0
  106. package/dist/util/sequenceUtils.d.ts +2 -0
  107. package/dist/util/sequenceUtils.d.ts.map +1 -0
  108. package/package.json +14 -14
  109. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +73 -38
  110. package/src/ApolloInternetAccount/components/LoginButtons.tsx +31 -10
  111. package/src/ApolloInternetAccount/model.ts +8 -5
  112. package/src/ApolloJobModel.ts +3 -8
  113. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +1 -1
  114. package/src/BackendDrivers/CollaborationServerDriver.ts +7 -11
  115. package/src/BackendDrivers/LocalDriver/LocalDriver.ts +13 -4
  116. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +1 -0
  117. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +4 -2
  118. package/src/FeatureDetailsWidget/Attributes.tsx +1 -0
  119. package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +1 -0
  120. package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +1 -0
  121. package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -0
  122. package/src/FeatureDetailsWidget/StringTextField.tsx +1 -0
  123. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +2 -1
  124. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +17 -354
  125. package/src/FeatureDetailsWidget/Translation.tsx +358 -0
  126. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +4 -1
  127. package/src/LinearApolloDisplay/glyphs/ExonGlyph.ts +2 -5
  128. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +4 -6
  129. package/src/LinearApolloDisplay/glyphs/TranscriptGlyph.ts +39 -6
  130. package/src/LinearApolloDisplay/stateModel/base.ts +47 -23
  131. package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -5
  132. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +4 -1
  133. package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -5
  134. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +10 -10
  135. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +5 -3
  136. package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +1 -1
  137. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +2 -3
  138. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +12 -20
  139. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +10 -6
  140. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +38 -45
  141. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +1 -4
  142. package/src/OntologyManager/OntologyStore/fulltext.ts +0 -2
  143. package/src/OntologyManager/OntologyStore/index.ts +1 -1
  144. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
  145. package/src/TabularEditor/HybridGrid/NumberCell.tsx +2 -1
  146. package/src/components/AddAssemblyAliases.tsx +1 -1
  147. package/src/components/AddCodingSequence.tsx +255 -0
  148. package/src/components/AddFeature.tsx +1 -1
  149. package/src/components/AddRefSeqAliases.tsx +3 -2
  150. package/src/components/CopyFeature.tsx +9 -7
  151. package/src/components/CreateApolloAnnotation.tsx +28 -23
  152. package/src/components/DeleteAssembly.tsx +2 -2
  153. package/src/components/DeleteFeature.tsx +1 -4
  154. package/src/components/DownloadGFF3.tsx +4 -3
  155. package/src/components/DuplicateFeatureDialog.tsx +154 -0
  156. package/src/components/DuplicateTranscript.tsx +1 -1
  157. package/src/components/FilterTranscripts.tsx +1 -1
  158. package/src/components/ImportAnnotations.tsx +108 -0
  159. package/src/components/ManageUsers.tsx +1 -2
  160. package/src/components/OntologyTermAutocomplete.tsx +1 -3
  161. package/src/components/OntologyTermMultiSelect.tsx +3 -1
  162. package/src/components/ViewChangeLog.tsx +2 -1
  163. package/src/components/index.ts +2 -0
  164. package/src/extensions/annotationFromPileup.ts +34 -26
  165. package/src/index.ts +9 -8
  166. package/src/session/ClientDataStore.ts +16 -21
  167. package/src/session/handleApolloFeaturesUrlParam.ts +122 -0
  168. package/src/session/session.ts +49 -9
  169. package/src/util/annotationFeatureUtils.ts +1 -1
  170. package/src/util/displayUtils.ts +1 -1
  171. package/src/util/glyphUtils.ts +3 -3
  172. package/src/util/sequenceUtils.ts +29 -0
@@ -0,0 +1,358 @@
1
+ import type {
2
+ AnnotationFeature,
3
+ ApolloRefSeqI,
4
+ TranscriptPartCoding,
5
+ } from '@apollo-annotation/mst'
6
+ import styled from '@emotion/styled'
7
+ import {
8
+ type AbstractSessionModel,
9
+ defaultCodonTable,
10
+ revcom,
11
+ } from '@jbrowse/core/util'
12
+ import ContentCopyIcon from '@mui/icons-material/ContentCopy'
13
+ import ContentCutIcon from '@mui/icons-material/ContentCut'
14
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
15
+ import {
16
+ Accordion,
17
+ AccordionDetails,
18
+ AccordionSummary,
19
+ Tooltip,
20
+ Typography,
21
+ } from '@mui/material'
22
+ import React, { useRef } from 'react'
23
+
24
+ import type { ApolloSessionModel } from '../session'
25
+ import { copyToClipboard } from '../util/copyToClipboard'
26
+
27
+ const SequenceContainer = styled('div')({
28
+ display: 'flex',
29
+ justifyContent: 'center',
30
+ alignItems: 'center',
31
+ textAlign: 'left',
32
+ width: '100%',
33
+ overflowWrap: 'break-word',
34
+ wordWrap: 'break-word',
35
+ wordBreak: 'break-all',
36
+ '& span': {
37
+ fontSize: 12,
38
+ },
39
+ })
40
+
41
+ const StyledAccordionSummary = styled(AccordionSummary)(() => ({
42
+ minHeight: 30,
43
+ maxHeight: 30,
44
+ '&.Mui-expanded': {
45
+ minHeight: 30,
46
+ maxHeight: 30,
47
+ },
48
+ }))
49
+
50
+ export function Translation({
51
+ changeInProgress,
52
+ cdsLocations,
53
+ refData,
54
+ strand,
55
+ // eslint-disable-next-line @typescript-eslint/unbound-method
56
+ updateCDSLocation,
57
+ cdsMin,
58
+ cdsMax,
59
+ feature,
60
+ session,
61
+ }: {
62
+ changeInProgress: boolean
63
+ cdsLocations: TranscriptPartCoding[][]
64
+ refData: ApolloRefSeqI
65
+ strand: 1 | -1 | undefined
66
+ updateCDSLocation(
67
+ oldLocation: number,
68
+ newLocation: number,
69
+ feature: AnnotationFeature,
70
+ isMin: boolean,
71
+ onComplete?: () => void,
72
+ ): boolean
73
+ cdsMin: number
74
+ cdsMax: number
75
+ feature: AnnotationFeature
76
+ session: ApolloSessionModel
77
+ }) {
78
+ const seqRef = useRef<HTMLDivElement>(null)
79
+ const { notify } = session as unknown as AbstractSessionModel
80
+
81
+ const cdsSequences: string[] = []
82
+ const [firstLocation] = cdsLocations
83
+ for (const loc of firstLocation) {
84
+ const seq = refData.getSequence(loc.min, loc.max)
85
+ cdsSequences.push(strand === -1 ? revcom(seq) : seq)
86
+ }
87
+ const cdsSequence = cdsSequences.join('')
88
+
89
+ const proteinSequence: string[] = []
90
+ for (
91
+ let codonGenomicPos = 0;
92
+ codonGenomicPos < cdsSequence.length;
93
+ codonGenomicPos += 3
94
+ ) {
95
+ const codonSeq = cdsSequence
96
+ .slice(codonGenomicPos, codonGenomicPos + 3)
97
+ .toUpperCase()
98
+ const protein =
99
+ defaultCodonTable[codonSeq as keyof typeof defaultCodonTable] || '&'
100
+ proteinSequence.push(protein)
101
+ }
102
+
103
+ const onCopyClick = () => {
104
+ const seqDiv = seqRef.current
105
+ if (!seqDiv) {
106
+ return
107
+ }
108
+ void copyToClipboard(seqDiv)
109
+ }
110
+
111
+ // Codon position is the index of the start codon in the CDS genomic sequence
112
+ // Calculate the genomic location of the start codon based on the codon position in the CDS
113
+ const getCodonGenomicLocation = (codonGenomicPosition: number) => {
114
+ const [firstLocation] = cdsLocations
115
+ let cdsLen = 0
116
+ const sortedCDSLocations = firstLocation.toSorted(
117
+ ({ min: a }, { min: b }) => a - b,
118
+ )
119
+
120
+ // Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
121
+ // and codonGenomicPosition is 25
122
+ // ((10 - 0) + (30 - 20) + (50 - 40)) > 25
123
+ // So, start codon is in (40, 50)
124
+ // 40 + (25-20) = 45 is the genomic location of the start codon
125
+ if (strand === 1) {
126
+ for (const loc of sortedCDSLocations) {
127
+ const locLength = loc.max - loc.min
128
+ if (cdsLen + locLength > codonGenomicPosition) {
129
+ return loc.min + (codonGenomicPosition - cdsLen)
130
+ }
131
+ cdsLen += locLength
132
+ }
133
+ } else if (strand === -1) {
134
+ for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
135
+ const loc = sortedCDSLocations[i]
136
+ const locLength = loc.max - loc.min
137
+ if (cdsLen + locLength > codonGenomicPosition) {
138
+ return loc.max - (codonGenomicPosition - cdsLen)
139
+ }
140
+ cdsLen += locLength
141
+ }
142
+ }
143
+
144
+ if (strand === 1) {
145
+ return cdsMin
146
+ }
147
+
148
+ return cdsMax
149
+ }
150
+ const trimTranslationSequence = () => {
151
+ if (
152
+ (proteinSequence.at(0) === 'M' && proteinSequence.at(-1) === '*') ||
153
+ proteinSequence.length === 0
154
+ ) {
155
+ return
156
+ }
157
+
158
+ // Trim any sequence before first start codon and after stop codon
159
+ const startCodonIndex = proteinSequence.indexOf('M')
160
+ const stopCodonIndex = proteinSequence.lastIndexOf('*')
161
+
162
+ const startCodonPos = startCodonIndex * 3
163
+ const stopCodonPos = stopCodonIndex * 3
164
+
165
+ const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos)
166
+ let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos)
167
+
168
+ if (strand === 1) {
169
+ if (startCodonGenomicLoc > stopCodonGenomicLoc) {
170
+ notify(
171
+ 'Start codon genomic location should be less than stop codon genomic location',
172
+ 'error',
173
+ )
174
+ return
175
+ }
176
+ let promise
177
+ stopCodonGenomicLoc += 3 // move to end of stop codon
178
+ if (startCodonGenomicLoc !== cdsMin) {
179
+ promise = new Promise((resolve) => {
180
+ updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
181
+ resolve(true)
182
+ })
183
+ })
184
+ }
185
+
186
+ if (stopCodonGenomicLoc !== cdsMax) {
187
+ if (promise) {
188
+ void promise.then(() => {
189
+ updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false)
190
+ })
191
+ } else {
192
+ updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false)
193
+ }
194
+ }
195
+ }
196
+
197
+ if (strand === -1) {
198
+ // reverse strand
199
+ if (startCodonGenomicLoc < stopCodonGenomicLoc) {
200
+ notify(
201
+ 'Start codon genomic location should be less than stop codon genomic location',
202
+ 'error',
203
+ )
204
+ return
205
+ }
206
+ let promise
207
+ stopCodonGenomicLoc -= 3 // move to end of stop codon
208
+ if (startCodonGenomicLoc !== cdsMax) {
209
+ promise = new Promise((resolve) => {
210
+ updateCDSLocation(
211
+ cdsMax,
212
+ startCodonGenomicLoc,
213
+ feature,
214
+ false,
215
+ () => {
216
+ resolve(true)
217
+ },
218
+ )
219
+ })
220
+ }
221
+
222
+ if (stopCodonGenomicLoc !== cdsMin) {
223
+ if (promise) {
224
+ void promise.then(() => {
225
+ updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true)
226
+ })
227
+ } else {
228
+ updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true)
229
+ }
230
+ }
231
+ }
232
+ notify('Translation sequence trimmed to start and stop codons', 'success')
233
+ }
234
+ return (
235
+ <div>
236
+ <Accordion>
237
+ <StyledAccordionSummary
238
+ expandIcon={<ExpandMoreIcon style={{ color: 'white' }} />}
239
+ aria-controls="panel1-content"
240
+ id="panel1-header"
241
+ >
242
+ <Typography component="span" fontWeight={'bold'}>
243
+ Translation
244
+ </Typography>
245
+ </StyledAccordionSummary>
246
+ <AccordionDetails>
247
+ <SequenceContainer>
248
+ <Typography
249
+ component={'span'}
250
+ ref={seqRef}
251
+ style={{ maxHeight: 120, overflowY: 'scroll' }}
252
+ >
253
+ {proteinSequence.map((protein, idx) => {
254
+ const codonGenomicPos = idx * 3
255
+ if (protein === 'M') {
256
+ return (
257
+ <Typography
258
+ component={'span'}
259
+ style={{
260
+ backgroundColor: changeInProgress
261
+ ? 'lightgray'
262
+ : 'yellow',
263
+ cursor: 'pointer',
264
+ border: '1px solid black',
265
+ }}
266
+ key={codonGenomicPos}
267
+ onClick={() => {
268
+ if (changeInProgress) {
269
+ return
270
+ }
271
+ // NOTE: codonGenomicPos is important here for calculating the genomic location
272
+ // of the start codon. We are using the codonGenomicPos as the key in the typography
273
+ // elements to maintain the genomic postion of the codon start
274
+ const startCodonGenomicLocation =
275
+ getCodonGenomicLocation(codonGenomicPos)
276
+ if (
277
+ startCodonGenomicLocation !== cdsMin &&
278
+ strand === 1
279
+ ) {
280
+ updateCDSLocation(
281
+ cdsMin,
282
+ startCodonGenomicLocation,
283
+ feature,
284
+ true,
285
+ )
286
+ }
287
+ if (
288
+ startCodonGenomicLocation !== cdsMax &&
289
+ strand === -1
290
+ ) {
291
+ updateCDSLocation(
292
+ cdsMax,
293
+ startCodonGenomicLocation,
294
+ feature,
295
+ false,
296
+ )
297
+ }
298
+ }}
299
+ >
300
+ {protein}
301
+ </Typography>
302
+ )
303
+ }
304
+
305
+ if (protein === '*') {
306
+ return (
307
+ <Typography
308
+ style={{ backgroundColor: 'red', color: 'white' }}
309
+ component={'span'}
310
+ // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
311
+ key={codonGenomicPos}
312
+ >
313
+ {protein}
314
+ </Typography>
315
+ )
316
+ }
317
+ // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
318
+ return (
319
+ <Typography component={'span'} key={codonGenomicPos}>
320
+ {protein}
321
+ </Typography>
322
+ )
323
+ })}
324
+ </Typography>
325
+ </SequenceContainer>
326
+ <div
327
+ style={{
328
+ marginTop: 10,
329
+ display: 'flex',
330
+ flexDirection: 'row',
331
+ alignItems: 'center',
332
+ gap: 10,
333
+ }}
334
+ >
335
+ <Tooltip title="Copy">
336
+ <button
337
+ onClick={onCopyClick}
338
+ style={{ border: 'none', background: 'none', padding: 0 }}
339
+ disabled={changeInProgress}
340
+ >
341
+ <ContentCopyIcon style={{ fontSize: 15 }} />
342
+ </button>
343
+ </Tooltip>
344
+ <Tooltip title="Trim">
345
+ <button
346
+ onClick={trimTranslationSequence}
347
+ style={{ border: 'none', background: 'none', padding: 0 }}
348
+ disabled={changeInProgress}
349
+ >
350
+ <ContentCutIcon style={{ fontSize: 15 }} />
351
+ </button>
352
+ </Tooltip>
353
+ </div>
354
+ </AccordionDetails>
355
+ </Accordion>
356
+ </div>
357
+ )
358
+ }
@@ -1,4 +1,5 @@
1
1
  import type { CheckResultI } from '@apollo-annotation/mst'
2
+ import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
2
3
  import { type AbstractSessionModel, doesIntersect2 } from '@jbrowse/core/util'
3
4
  import ErrorIcon from '@mui/icons-material/Error'
4
5
  import { Avatar, Badge, Box, Tooltip } from '@mui/material'
@@ -23,7 +24,9 @@ export const CheckResultWarnings = observer(function CheckResultWarnings({
23
24
  }
24
25
  return lgv.dynamicBlocks.contentBlocks.map((block) => {
25
26
  const widthBp = lgv.bpPerPx * apolloRowHeight
26
- const assembly = assemblyManager.get(block.assemblyName)
27
+ const assembly = assemblyManager.get(block.assemblyName) as
28
+ | Assembly
29
+ | undefined
27
30
  if (!assembly) {
28
31
  return null
29
32
  }
@@ -1,5 +1,4 @@
1
1
  import type { AnnotationFeature } from '@apollo-annotation/mst'
2
- import type { BaseDisplayModel } from '@jbrowse/core/pluggableElementTypes'
3
2
  import type { MenuItem } from '@jbrowse/core/ui'
4
3
  import {
5
4
  type AbstractSessionModel,
@@ -106,14 +105,12 @@ function getContextMenuItems(
106
105
  session,
107
106
  } = display
108
107
  const [region] = regions
109
- const currentAssemblyId = display.getAssemblyId(region.assemblyName)
108
+ const currentAssemblyId = display.getAssemblyId(region.assemblyName) as string
110
109
  const role = internetAccount ? internetAccount.role : 'admin'
111
110
  const admin = role === 'admin'
112
111
  const menuItems: MenuItem[] = []
113
112
  const adjacentExons = getAdjacentExons(feature, display)
114
- const lgv = getContainingView(
115
- display as BaseDisplayModel,
116
- ) as unknown as LinearGenomeViewModel
113
+ const lgv = getContainingView(display) as unknown as LinearGenomeViewModel
117
114
  if (adjacentExons.upstream) {
118
115
  const exon = adjacentExons.upstream
119
116
  menuItems.push({
@@ -30,12 +30,10 @@ function draw(
30
30
  const [top, left, width] = getFeatureBox(display, gene, row, block)
31
31
  const height = getRowCount(display, gene) * apolloRowHeight
32
32
  if (width > 2) {
33
- let selectedColor = readConfObject(
34
- session.getPluginConfiguration(),
35
- 'geneBackgroundColor',
36
- { featureType: gene.type },
37
- ) as string
38
- selectedColor = alpha(theme.palette.background.paper, 0.6)
33
+ const selectedColor =
34
+ (readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', {
35
+ featureType: gene.type,
36
+ }) as string | undefined) ?? alpha(theme.palette.background.paper, 0.6)
39
37
  ctx.fillStyle = selectedColor
40
38
  ctx.fillRect(left, top, width, height)
41
39
  }
@@ -6,7 +6,11 @@ import {
6
6
  } from '@jbrowse/core/util'
7
7
  import type { ContentBlock } from '@jbrowse/core/util/blockTypes'
8
8
 
9
- import { DuplicateTranscript, MergeTranscripts } from '../../components'
9
+ import {
10
+ AddCodingSequence,
11
+ DuplicateTranscript,
12
+ MergeTranscripts,
13
+ } from '../../components'
10
14
  import {
11
15
  isCDSFeature,
12
16
  isExonFeature,
@@ -201,11 +205,11 @@ function getLayout(display: LinearApolloDisplay, feature: AnnotationFeature) {
201
205
 
202
206
  function getContextMenuItems(
203
207
  display: LinearApolloDisplay,
204
- feature: AnnotationFeature,
208
+ transcript: AnnotationFeature,
205
209
  ): MenuItem[] {
206
210
  const { changeManager, regions, selectedFeature, session } = display
207
211
  const [region] = regions
208
- const currentAssemblyId = display.getAssemblyId(region.assemblyName)
212
+ const currentAssemblyId = display.getAssemblyId(region.assemblyName) as string
209
213
  const menuItems: MenuItem[] = []
210
214
  if (isSessionModelWithWidgets(session)) {
211
215
  menuItems.splice(1, 0, {
@@ -215,7 +219,7 @@ function getContextMenuItems(
215
219
  'ApolloTranscriptDetails',
216
220
  'apolloTranscriptDetails',
217
221
  {
218
- feature,
222
+ feature: transcript,
219
223
  assembly: currentAssemblyId,
220
224
  changeManager,
221
225
  refName: region.refName,
@@ -238,7 +242,7 @@ function getContextMenuItems(
238
242
  doneCallback()
239
243
  },
240
244
  changeManager,
241
- sourceFeature: feature,
245
+ sourceFeature: transcript,
242
246
  sourceAssemblyId: currentAssemblyId,
243
247
  selectedFeature,
244
248
  setSelectedFeature: (feature?: AnnotationFeature) => {
@@ -261,7 +265,7 @@ function getContextMenuItems(
261
265
  doneCallback()
262
266
  },
263
267
  changeManager,
264
- sourceFeature: feature,
268
+ sourceFeature: transcript,
265
269
  sourceAssemblyId: currentAssemblyId,
266
270
  selectedFeature,
267
271
  setSelectedFeature: (feature?: AnnotationFeature) => {
@@ -273,6 +277,35 @@ function getContextMenuItems(
273
277
  },
274
278
  },
275
279
  )
280
+ const { children } = transcript
281
+ if (!children) {
282
+ return []
283
+ }
284
+ const cdsChildren = [...children.values()].filter((child) =>
285
+ isCDSFeature(child, session),
286
+ )
287
+ if (cdsChildren.length === 0) {
288
+ menuItems.push({
289
+ label: 'Add coding sequence',
290
+ onClick: () => {
291
+ ;(session as unknown as AbstractSessionModel).queueDialog(
292
+ (doneCallback) => [
293
+ AddCodingSequence,
294
+ {
295
+ session,
296
+ handleClose: () => {
297
+ doneCallback()
298
+ },
299
+ changeManager,
300
+ sourceFeature: transcript,
301
+ sourceAssemblyId: currentAssemblyId,
302
+ refName: region.refName,
303
+ },
304
+ ],
305
+ )
306
+ },
307
+ })
308
+ }
276
309
  return menuItems
277
310
  }
278
311
 
@@ -5,6 +5,7 @@
5
5
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
6
6
  import type { AnnotationFeature } from '@apollo-annotation/mst'
7
7
  import type PluginManager from '@jbrowse/core/PluginManager'
8
+ import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
8
9
  import {
9
10
  type AnyConfigurationSchemaType,
10
11
  ConfigurationReference,
@@ -28,6 +29,7 @@ import {
28
29
  } from '@jbrowse/mobx-state-tree'
29
30
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
30
31
  import FactCheckIcon from '@mui/icons-material/FactCheck'
32
+ import InputIcon from '@mui/icons-material/Input'
31
33
  import TrackChangesIcon from '@mui/icons-material/TrackChanges'
32
34
  import { autorun } from 'mobx'
33
35
 
@@ -37,11 +39,13 @@ import {
37
39
  Export as ExportIcon,
38
40
  } from '../../components/DownloadGFF3'
39
41
  import { FilterFeatures } from '../../components/FilterFeatures'
42
+ import { ImportAnnotations } from '../../components/ImportAnnotations'
40
43
  import { ViewChangeLog } from '../../components/ViewChangeLog'
41
44
  import { ViewCheckResults } from '../../components/ViewCheckResults'
42
45
  import type { ApolloSessionModel, HoveredFeature } from '../../session'
43
46
  import type { ApolloRootModel } from '../../types'
44
47
  import { EditZoomThresholdDialog } from '../../util/displayUtils'
48
+ import { LocalDriver } from '../../BackendDrivers'
45
49
 
46
50
  const minDisplayHeight = 20
47
51
 
@@ -137,7 +141,9 @@ export function baseModelFactory(
137
141
  const { assemblyName } = region
138
142
  const { assemblyManager } =
139
143
  self.session as unknown as AbstractSessionModel
140
- const assembly = assemblyManager.get(assemblyName)
144
+ const assembly = assemblyManager.get(assemblyName) as
145
+ | Assembly
146
+ | undefined
141
147
  if (!assembly) {
142
148
  throw new Error(`No assembly found with name ${assemblyName}`)
143
149
  }
@@ -150,8 +156,7 @@ export function baseModelFactory(
150
156
  ) as ApolloInternetAccountModel | undefined
151
157
  },
152
158
  get changeManager() {
153
- return (self.session as unknown as ApolloSessionModel).apolloDataStore
154
- .changeManager
159
+ return self.session.apolloDataStore.changeManager
155
160
  },
156
161
  getAssemblyId(assemblyName: string) {
157
162
  const { assemblyManager } =
@@ -163,12 +168,10 @@ export function baseModelFactory(
163
168
  return assembly.name
164
169
  },
165
170
  get selectedFeature(): AnnotationFeature | undefined {
166
- return (self.session as unknown as ApolloSessionModel)
167
- .apolloSelectedFeature
171
+ return self.session.apolloSelectedFeature
168
172
  },
169
173
  get hoveredFeature(): HoveredFeature | undefined {
170
- return (self.session as unknown as ApolloSessionModel)
171
- .apolloHoveredFeature
174
+ return self.session.apolloHoveredFeature
172
175
  },
173
176
  }))
174
177
  .actions((self) => ({
@@ -213,8 +216,12 @@ export function baseModelFactory(
213
216
  const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self
214
217
  return {
215
218
  trackMenuItems() {
216
- const { graphical, table, showCheckResults } = self
217
- return [
219
+ const { graphical, table, showCheckResults, session } = self
220
+ const [region] = self.regions
221
+ const { assemblyName } = region
222
+ const backendDriver =
223
+ session.apolloDataStore.getBackendDriver(assemblyName)
224
+ const items = [
218
225
  ...superTrackMenuItems(),
219
226
  {
220
227
  type: 'subMenu',
@@ -266,7 +273,7 @@ export function baseModelFactory(
266
273
  {
267
274
  label: 'Filter features by type',
268
275
  onClick: () => {
269
- const session = self.session as unknown as ApolloSessionModel
276
+ const { session } = self
270
277
  ;(self.session as unknown as AbstractSessionModel).queueDialog(
271
278
  (doneCallback) => [
272
279
  FilterFeatures,
@@ -284,6 +291,28 @@ export function baseModelFactory(
284
291
  )
285
292
  },
286
293
  },
294
+ ]
295
+ if (backendDriver instanceof LocalDriver) {
296
+ items.push({
297
+ label: 'Import annotations',
298
+ icon: InputIcon,
299
+ onClick: () => {
300
+ ;(session as unknown as AbstractSessionModel).queueDialog(
301
+ (doneCallback) => [
302
+ ImportAnnotations,
303
+ {
304
+ session,
305
+ assemblyName,
306
+ handleClose: () => {
307
+ doneCallback()
308
+ },
309
+ },
310
+ ],
311
+ )
312
+ },
313
+ })
314
+ }
315
+ items.push(
287
316
  {
288
317
  label: 'Export annotations',
289
318
  icon: ExportIcon,
@@ -294,7 +323,7 @@ export function baseModelFactory(
294
323
  if (!assembly) {
295
324
  return
296
325
  }
297
- const session = self.session as unknown as ApolloSessionModel
326
+ const { session } = self
298
327
  ;(session as unknown as AbstractSessionModel).queueDialog(
299
328
  (doneCallback) => [
300
329
  DownloadGFF3,
@@ -319,7 +348,7 @@ export function baseModelFactory(
319
348
  if (!assembly) {
320
349
  return
321
350
  }
322
- const session = self.session as unknown as ApolloSessionModel
351
+ const { session } = self
323
352
  ;(session as unknown as AbstractSessionModel).queueDialog(
324
353
  (doneCallback) => [
325
354
  ViewChangeLog,
@@ -344,7 +373,7 @@ export function baseModelFactory(
344
373
  if (!assembly) {
345
374
  return
346
375
  }
347
- const session = self.session as unknown as ApolloSessionModel
376
+ const { session } = self
348
377
  ;(session as unknown as AbstractSessionModel).queueDialog(
349
378
  (doneCallback) => [
350
379
  ViewCheckResults,
@@ -359,20 +388,17 @@ export function baseModelFactory(
359
388
  )
360
389
  },
361
390
  },
362
- ]
391
+ )
392
+ return items
363
393
  },
364
394
  }
365
395
  })
366
396
  .actions((self) => ({
367
397
  setSelectedFeature(feature?: AnnotationFeature) {
368
- ;(
369
- self.session as unknown as ApolloSessionModel
370
- ).apolloSetSelectedFeature(feature)
398
+ self.session.apolloSetSelectedFeature(feature)
371
399
  },
372
400
  setHoveredFeature(hoveredFeature?: HoveredFeature) {
373
- ;(
374
- self.session as unknown as ApolloSessionModel
375
- ).apolloSetHoveredFeature(hoveredFeature)
401
+ self.session.apolloSetHoveredFeature(hoveredFeature)
376
402
  },
377
403
  showFeatureDetailsWidget(
378
404
  feature: AnnotationFeature,
@@ -411,9 +437,7 @@ export function baseModelFactory(
411
437
  return
412
438
  }
413
439
  self.setLoading(true)
414
- void (
415
- self.session as unknown as ApolloSessionModel
416
- ).apolloDataStore
440
+ void self.session.apolloDataStore
417
441
  .loadFeatures(self.regions)
418
442
  .then(() => {
419
443
  setTimeout(() => {