@apollo-annotation/jbrowse-plugin-apollo 0.3.8 → 0.3.9

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 (48) hide show
  1. package/dist/index.esm.js +10932 -10932
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +10845 -10846
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +18619 -21342
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +7 -7
  12. package/src/ApolloInternetAccount/model.ts +81 -63
  13. package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +4 -4
  14. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +9 -7
  15. package/src/BackendDrivers/CollaborationServerDriver.ts +49 -18
  16. package/src/BackendDrivers/DesktopFileDriver.ts +2 -2
  17. package/src/ChangeManager.ts +3 -1
  18. package/src/FeatureDetailsWidget/BasicInformation.tsx +6 -4
  19. package/src/FeatureDetailsWidget/NumberTextField.tsx +5 -2
  20. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +39 -203
  21. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +92 -0
  22. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +6 -102
  23. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +31 -230
  24. package/src/LinearApolloDisplay/glyphs/util.ts +19 -0
  25. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +181 -0
  26. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +218 -0
  27. package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +62 -386
  28. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +6 -0
  29. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +122 -70
  30. package/src/components/AddAssembly.tsx +33 -37
  31. package/src/components/AddFeature.tsx +21 -18
  32. package/src/components/AddRefSeqAliases.tsx +56 -42
  33. package/src/components/CopyFeature.tsx +1 -1
  34. package/src/components/CreateApolloAnnotation.tsx +22 -10
  35. package/src/components/DeleteAssembly.tsx +2 -9
  36. package/src/components/DownloadGFF3.tsx +2 -2
  37. package/src/components/ManageChecks.tsx +2 -9
  38. package/src/components/ManageUsers.tsx +23 -22
  39. package/src/components/OntologyTermAutocomplete.tsx +1 -8
  40. package/src/components/ViewChangeLog.tsx +25 -50
  41. package/src/components/ViewCheckResults.tsx +1 -7
  42. package/src/config.ts +3 -3
  43. package/src/index.ts +17 -16
  44. package/src/makeDisplayComponent.tsx +9 -13
  45. package/src/session/ClientDataStore.ts +32 -14
  46. package/src/session/session.ts +19 -27
  47. package/src/util/glyphUtils.ts +178 -1
  48. package/src/util/loadAssemblyIntoClient.ts +3 -2
@@ -1,153 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
2
2
  import type PluginManager from '@jbrowse/core/PluginManager'
3
3
  import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
4
- import {
5
- type Frame,
6
- defaultCodonTable,
7
- getFrame,
8
- revcom,
9
- } from '@jbrowse/core/util'
10
4
  import { type Theme, createTheme } from '@mui/material'
11
5
  import { autorun } from 'mobx'
12
6
  import { type Instance, addDisposer } from 'mobx-state-tree'
13
7
 
14
- import { type ApolloSessionModel } from '../../session'
8
+ import { drawSequenceOverlay } from '../drawSequenceOverlay'
9
+ import { drawSequenceTrack } from '../drawSequenceTrack'
15
10
 
16
11
  import { baseModelFactory } from './base'
17
12
 
18
- function colorCode(letter: string, theme: Theme) {
19
- return (
20
- theme.palette.bases[
21
- letter.toUpperCase() as keyof Theme['palette']['bases']
22
- ].main.toString() ?? 'lightgray'
23
- )
24
- }
25
-
26
- function codonColorCode(letter: string, highContrast?: boolean) {
27
- const colorMap: Record<string, string | undefined> = {
28
- M: '#33ee33',
29
- '*': highContrast ? '#000000' : '#f44336',
30
- }
31
-
32
- return colorMap[letter.toUpperCase()]
33
- }
34
-
35
- function reverseCodonSeq(seq: string): string {
36
- // disable because sequence is all ascii
37
- // eslint-disable-next-line @typescript-eslint/no-misused-spread
38
- return [...seq]
39
- .map((c) => revcom(c))
40
- .reverse()
41
- .join('')
42
- }
43
-
44
- function drawLetter(
45
- seqTrackctx: CanvasRenderingContext2D,
46
- startPx: number,
47
- widthPx: number,
48
- letter: string,
49
- textY: number,
50
- ) {
51
- const fontSize = Math.min(widthPx, 10)
52
- seqTrackctx.fillStyle = '#000'
53
- seqTrackctx.font = `${fontSize}px`
54
- const textWidth = seqTrackctx.measureText(letter).width
55
- const textX = startPx + (widthPx - textWidth) / 2
56
- seqTrackctx.fillText(letter, textX, textY + 10)
57
- }
58
-
59
- function drawTranslation(
60
- seqTrackctx: CanvasRenderingContext2D,
61
- bpPerPx: number,
62
- trnslStartPx: number,
63
- trnslY: number,
64
- trnslWidthPx: number,
65
- sequenceRowHeight: number,
66
- seq: string,
67
- i: number,
68
- reverse: boolean,
69
- showStartCodons: boolean,
70
- showStopCodons: boolean,
71
- highContrast: boolean,
72
- ) {
73
- let codonSeq: string = seq.slice(i, i + 3).toUpperCase()
74
- if (reverse) {
75
- codonSeq = reverseCodonSeq(codonSeq)
76
- }
77
- const codonLetter =
78
- defaultCodonTable[codonSeq as keyof typeof defaultCodonTable]
79
- if (!codonLetter) {
80
- return
81
- }
82
- const fillColor = codonColorCode(codonLetter, highContrast)
83
- if (
84
- fillColor &&
85
- ((showStopCodons && codonLetter == '*') ||
86
- (showStartCodons && codonLetter != '*'))
87
- ) {
88
- seqTrackctx.fillStyle = fillColor
89
- seqTrackctx.fillRect(trnslStartPx, trnslY, trnslWidthPx, sequenceRowHeight)
90
- }
91
- if (bpPerPx <= 0.1) {
92
- seqTrackctx.rect(trnslStartPx, trnslY, trnslWidthPx, sequenceRowHeight)
93
- seqTrackctx.stroke()
94
- drawLetter(seqTrackctx, trnslStartPx, trnslWidthPx, codonLetter, trnslY)
95
- }
96
- }
97
-
98
- function getTranslationRow(frame: Frame, bpPerPx: number) {
99
- const offset = bpPerPx <= 1 ? 2 : 0
100
- switch (frame) {
101
- case 3: {
102
- return 0
103
- }
104
- case 2: {
105
- return 1
106
- }
107
- case 1: {
108
- return 2
109
- }
110
- case -1: {
111
- return 3 + offset
112
- }
113
- case -2: {
114
- return 4 + offset
115
- }
116
- case -3: {
117
- return 5 + offset
118
- }
119
- }
120
- }
121
-
122
- function getSeqRow(
123
- strand: 1 | -1 | undefined,
124
- bpPerPx: number,
125
- ): number | undefined {
126
- if (bpPerPx > 1 || strand === undefined) {
127
- return
128
- }
129
- return strand === 1 ? 3 : 4
130
- }
131
-
132
- function highlightSeq(
133
- seqTrackOverlayctx: CanvasRenderingContext2D,
134
- theme: Theme,
135
- startPx: number,
136
- sequenceRowHeight: number,
137
- row: number | undefined,
138
- widthPx: number,
139
- ) {
140
- if (row !== undefined) {
141
- seqTrackOverlayctx.fillStyle = theme.palette.action.focus
142
- seqTrackOverlayctx.fillRect(
143
- startPx,
144
- sequenceRowHeight * row,
145
- widthPx,
146
- sequenceRowHeight,
147
- )
148
- }
149
- }
150
-
151
13
  export function renderingModelFactory(
152
14
  pluginManager: PluginManager,
153
15
  configSchema: AnyConfigurationSchemaType,
@@ -180,181 +42,45 @@ export function renderingModelFactory(
180
42
  self,
181
43
  autorun(
182
44
  () => {
183
- const { theme } = self
184
- if (!self.lgv.initialized || self.regionCannotBeRendered()) {
45
+ const {
46
+ lgv,
47
+ seqTrackCanvas,
48
+ theme,
49
+ highContrast,
50
+ showStartCodons,
51
+ showStopCodons,
52
+ sequenceRowHeight,
53
+ session,
54
+ } = self
55
+ if (
56
+ !lgv.initialized ||
57
+ self.regionCannotBeRendered() ||
58
+ !seqTrackCanvas
59
+ ) {
185
60
  return
186
61
  }
187
- const trnslWidthPx = 3 / self.lgv.bpPerPx
62
+
63
+ const trnslWidthPx = 3 / lgv.bpPerPx
188
64
  if (trnslWidthPx < 1) {
189
65
  return
190
66
  }
191
- const seqTrackctx = self.seqTrackCanvas?.getContext('2d')
192
- if (!seqTrackctx) {
193
- return
194
- }
195
67
 
196
- seqTrackctx.clearRect(
197
- 0,
198
- 0,
199
- self.lgv.dynamicBlocks.totalWidthPx,
200
- self.height,
68
+ const { bpPerPx, offsetPx, dynamicBlocks } = lgv
69
+ // we have to be really explicit about passing in individual
70
+ // variables here and not just e.g. "lgv" so that the autorun
71
+ // tracks the variables correctly
72
+ drawSequenceTrack(
73
+ seqTrackCanvas,
74
+ theme,
75
+ bpPerPx,
76
+ offsetPx,
77
+ dynamicBlocks,
78
+ highContrast,
79
+ showStartCodons,
80
+ showStopCodons,
81
+ sequenceRowHeight,
82
+ session,
201
83
  )
202
- const frames =
203
- self.lgv.bpPerPx <= 1
204
- ? [3, 2, 1, 0, 0, -1, -2, -3]
205
- : [3, 2, 1, -1, -2, -3]
206
- let height = 0
207
- if (theme) {
208
- for (const frame of frames) {
209
- let frameColor = theme.palette.framesCDS.at(frame)?.main
210
- if (frameColor) {
211
- let offsetPx = 0
212
- if (self.highContrast) {
213
- frameColor = 'white'
214
- offsetPx = 1
215
- // eslint-disable-next-line prefer-destructuring
216
- seqTrackctx.fillStyle = theme.palette.grey[200]
217
- seqTrackctx.fillRect(
218
- 0,
219
- height,
220
- self.lgv.dynamicBlocks.totalWidthPx,
221
- self.sequenceRowHeight,
222
- )
223
- }
224
- seqTrackctx.fillStyle = frameColor
225
- seqTrackctx.fillRect(
226
- 0 + offsetPx,
227
- height + offsetPx,
228
- self.lgv.dynamicBlocks.totalWidthPx - 2 * offsetPx,
229
- self.sequenceRowHeight - 2 * offsetPx,
230
- )
231
- }
232
- height += self.sequenceRowHeight
233
- }
234
- }
235
-
236
- for (const [idx, region] of self.regions.entries()) {
237
- const { apolloDataStore } =
238
- self.session as unknown as ApolloSessionModel
239
- const assembly = apolloDataStore.assemblies.get(
240
- region.assemblyName,
241
- )
242
- const ref = assembly?.getByRefName(region.refName)
243
- const seq = ref?.getSequence(region.start, region.end)
244
- if (!seq) {
245
- return
246
- }
247
- // disable because sequence is all ascii
248
- // eslint-disable-next-line @typescript-eslint/no-misused-spread
249
- for (const [i, letter] of [...seq].entries()) {
250
- const trnslXOffset =
251
- (self.lgv.bpToPx({
252
- refName: region.refName,
253
- coord: region.start + i,
254
- regionNumber: idx,
255
- })?.offsetPx ?? 0) - self.lgv.offsetPx
256
- const trnslStartPx = self.lgv.displayedRegions[idx].reversed
257
- ? trnslXOffset - trnslWidthPx
258
- : trnslXOffset
259
-
260
- // Draw translation forward
261
- for (let j = 2; j >= 0; j--) {
262
- if ((region.start + i) % 3 === j) {
263
- drawTranslation(
264
- seqTrackctx,
265
- self.lgv.bpPerPx,
266
- trnslStartPx,
267
- self.sequenceRowHeight * (2 - j),
268
- trnslWidthPx,
269
- self.sequenceRowHeight,
270
- seq,
271
- i,
272
- false,
273
- self.showStartCodons,
274
- self.showStopCodons,
275
- self.highContrast,
276
- )
277
- }
278
- }
279
-
280
- if (self.lgv.bpPerPx <= 1) {
281
- const xOffset =
282
- (self.lgv.bpToPx({
283
- refName: region.refName,
284
- coord: region.start + i,
285
- regionNumber: idx,
286
- })?.offsetPx ?? 0) - self.lgv.offsetPx
287
- const widthPx = 1 / self.lgv.bpPerPx
288
- const startPx = self.lgv.displayedRegions[idx].reversed
289
- ? xOffset - widthPx
290
- : xOffset
291
-
292
- // Draw forward
293
- seqTrackctx.beginPath()
294
- seqTrackctx.fillStyle = colorCode(letter, self.theme)
295
- seqTrackctx.rect(
296
- startPx,
297
- self.sequenceRowHeight * 3,
298
- widthPx,
299
- self.sequenceRowHeight,
300
- )
301
- seqTrackctx.fill()
302
- if (self.lgv.bpPerPx <= 0.1) {
303
- seqTrackctx.stroke()
304
- drawLetter(
305
- seqTrackctx,
306
- startPx,
307
- widthPx,
308
- letter,
309
- self.sequenceRowHeight * 3,
310
- )
311
- }
312
-
313
- // Draw reverse
314
- const revLetter = revcom(letter)
315
- seqTrackctx.beginPath()
316
- seqTrackctx.fillStyle = colorCode(revLetter, self.theme)
317
- seqTrackctx.rect(
318
- startPx,
319
- self.sequenceRowHeight * 4,
320
- widthPx,
321
- self.sequenceRowHeight,
322
- )
323
- seqTrackctx.fill()
324
- if (self.lgv.bpPerPx <= 0.1) {
325
- seqTrackctx.stroke()
326
- drawLetter(
327
- seqTrackctx,
328
- startPx,
329
- widthPx,
330
- revLetter,
331
- self.sequenceRowHeight * 4,
332
- )
333
- }
334
- }
335
-
336
- // Draw translation reverse
337
- for (let k = 0; k <= 2; k++) {
338
- const rowOffset = self.lgv.bpPerPx <= 1 ? 5 : 3
339
- if ((region.start + i) % 3 === k) {
340
- drawTranslation(
341
- seqTrackctx,
342
- self.lgv.bpPerPx,
343
- trnslStartPx,
344
- self.sequenceRowHeight * (rowOffset + k),
345
- trnslWidthPx,
346
- self.sequenceRowHeight,
347
- seq,
348
- i,
349
- true,
350
- self.showStartCodons,
351
- self.showStopCodons,
352
- self.highContrast,
353
- )
354
- }
355
- }
356
- }
357
- }
358
84
  },
359
85
  { name: 'LinearApolloReferenceSequenceDisplayRenderSequence' },
360
86
  ),
@@ -363,11 +89,15 @@ export function renderingModelFactory(
363
89
  self,
364
90
  autorun(
365
91
  () => {
366
- if (!self.lgv.initialized || self.regionCannotBeRendered()) {
92
+ const { seqTrackOverlayCanvas } = self
93
+ if (
94
+ !self.lgv.initialized ||
95
+ self.regionCannotBeRendered() ||
96
+ !seqTrackOverlayCanvas
97
+ ) {
367
98
  return
368
99
  }
369
- const seqTrackOverlayctx =
370
- self.seqTrackOverlayCanvas?.getContext('2d')
100
+ const seqTrackOverlayctx = seqTrackOverlayCanvas.getContext('2d')
371
101
  if (!seqTrackOverlayctx) {
372
102
  return
373
103
  }
@@ -381,90 +111,36 @@ export function renderingModelFactory(
381
111
 
382
112
  const {
383
113
  hoveredFeature,
114
+ selectedFeature,
384
115
  lgv,
385
- regions,
386
116
  sequenceRowHeight,
387
117
  session,
388
118
  theme,
389
119
  } = self
390
120
 
391
- if (!hoveredFeature) {
121
+ if (!(hoveredFeature || selectedFeature)) {
392
122
  return
393
123
  }
394
- const { feature } = hoveredFeature
395
-
396
- const { featureTypeOntology } =
397
- session.apolloDataStore.ontologyManager
398
- if (!featureTypeOntology) {
399
- throw new Error('featureTypeOntology is undefined')
400
- }
401
- for (const [idx, region] of regions.entries()) {
402
- if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
403
- const parentFeature = feature.parent
404
- if (!parentFeature) {
405
- continue
406
- }
407
- const cdsLocs = parentFeature.cdsLocations.find(
408
- (loc) =>
409
- feature.min === loc.at(0)?.min &&
410
- feature.max === loc.at(-1)?.max,
411
- )
412
- if (!cdsLocs) {
413
- continue
414
- }
415
- for (const dl of cdsLocs) {
416
- const frame = getFrame(
417
- dl.min,
418
- dl.max,
419
- feature.strand ?? 1,
420
- dl.phase,
421
- )
422
- const row = getTranslationRow(frame, lgv.bpPerPx)
423
- const offset =
424
- (lgv.bpToPx({
425
- refName: region.refName,
426
- coord: dl.min,
427
- regionNumber: idx,
428
- })?.offsetPx ?? 0) - lgv.offsetPx
429
- const widthPx = (dl.max - dl.min) / lgv.bpPerPx
430
- const startPx = lgv.displayedRegions[idx].reversed
431
- ? offset - widthPx
432
- : offset
433
-
434
- highlightSeq(
435
- seqTrackOverlayctx,
436
- theme,
437
- startPx,
438
- sequenceRowHeight,
439
- row,
440
- widthPx,
441
- )
442
- }
443
- } else {
444
- const row = getSeqRow(feature.strand, lgv.bpPerPx)
445
- const offset =
446
- (lgv.bpToPx({
447
- refName: region.refName,
448
- coord: feature.min,
449
- regionNumber: idx,
450
- })?.offsetPx ?? 0) - lgv.offsetPx
451
- const widthPx = feature.length / lgv.bpPerPx
452
- const startPx = lgv.displayedRegions[idx].reversed
453
- ? offset - widthPx
454
- : offset
455
-
456
- highlightSeq(
457
- seqTrackOverlayctx,
458
- theme,
459
- startPx,
460
- sequenceRowHeight,
461
- row,
462
- widthPx,
463
- )
464
- }
465
- }
124
+ const { bpPerPx, dynamicBlocks, offsetPx } = lgv
125
+ // we have to be really explicit about passing in individual
126
+ // variables here and not just e.g. "lgv" so that the autorun
127
+ // tracks the variables correctly
128
+ drawSequenceOverlay(
129
+ seqTrackOverlayCanvas,
130
+ seqTrackOverlayctx,
131
+ hoveredFeature,
132
+ selectedFeature,
133
+ sequenceRowHeight,
134
+ theme,
135
+ session,
136
+ bpPerPx,
137
+ offsetPx,
138
+ dynamicBlocks,
139
+ )
140
+ },
141
+ {
142
+ name: 'LinearApolloReferenceSequenceDisplayRenderSequenceHighlight',
466
143
  },
467
- { name: 'LinearApolloDisplayRenderSeqHighlight' },
468
144
  ),
469
145
  )
470
146
  },
@@ -11,6 +11,7 @@ import {
11
11
  } from '@jbrowse/core/util'
12
12
  import { type LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
13
13
  import ErrorIcon from '@mui/icons-material/Error'
14
+ import LockIcon from '@mui/icons-material/Lock'
14
15
  import { Alert, Avatar, Badge, Box, Tooltip, useTheme } from '@mui/material'
15
16
  import { observer } from 'mobx-react'
16
17
  import React, { useEffect, useState } from 'react'
@@ -88,6 +89,11 @@ export const LinearApolloSixFrameDisplay = observer(
88
89
  }
89
90
  }}
90
91
  >
92
+ {session.isLocked ? (
93
+ <div className={classes.locked} data-testid="lock-icon">
94
+ <LockIcon />
95
+ </div>
96
+ ) : null}
91
97
  {message ? (
92
98
  <Alert
93
99
  severity="warning"