@apollo-annotation/jbrowse-plugin-apollo 0.1.0 → 0.1.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 (61) hide show
  1. package/dist/index.esm.js +3096 -2525
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +3095 -2524
  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 +2974 -2103
  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 +8 -9
  12. package/src/ApolloInternetAccount/model.ts +9 -9
  13. package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +5 -2
  14. package/src/BackendDrivers/BackendDriver.ts +6 -3
  15. package/src/BackendDrivers/CollaborationServerDriver.ts +12 -6
  16. package/src/BackendDrivers/DesktopFileDriver.ts +13 -15
  17. package/src/BackendDrivers/InMemoryFileDriver.ts +9 -3
  18. package/src/ChangeManager.ts +6 -3
  19. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +86 -0
  20. package/src/FeatureDetailsWidget/Attributes.tsx +374 -0
  21. package/src/FeatureDetailsWidget/BasicInformation.tsx +178 -0
  22. package/src/FeatureDetailsWidget/NumberTextField.tsx +75 -0
  23. package/src/FeatureDetailsWidget/RelatedFeature.tsx +87 -0
  24. package/src/FeatureDetailsWidget/Sequence.tsx +88 -0
  25. package/src/FeatureDetailsWidget/StringTextField.tsx +60 -0
  26. package/src/FeatureDetailsWidget/index.ts +2 -0
  27. package/src/FeatureDetailsWidget/model.ts +67 -0
  28. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +5 -2
  29. package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +12 -4
  30. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +1 -1
  31. package/src/LinearApolloDisplay/glyphs/Glyph.ts +21 -2
  32. package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +18 -5
  33. package/src/LinearApolloDisplay/stateModel/base.ts +1 -1
  34. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +1 -1
  35. package/src/LinearApolloDisplay/stateModel/glyphs.ts +1 -1
  36. package/src/LinearApolloDisplay/stateModel/layouts.ts +1 -1
  37. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +1 -1
  38. package/src/LinearApolloDisplay/stateModel/rendering.ts +35 -3
  39. package/src/OntologyManager/util.ts +33 -0
  40. package/src/SixFrameFeatureDisplay/stateModel.ts +1 -1
  41. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
  42. package/src/TabularEditor/HybridGrid/Feature.tsx +3 -3
  43. package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +1 -1
  44. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +1 -1
  45. package/src/components/AddAssembly.tsx +5 -5
  46. package/src/components/AddChildFeature.tsx +13 -29
  47. package/src/components/AddFeature.tsx +1 -1
  48. package/src/components/CopyFeature.tsx +5 -2
  49. package/src/components/DeleteAssembly.tsx +1 -1
  50. package/src/components/DeleteFeature.tsx +2 -2
  51. package/src/components/DownloadGFF3.tsx +2 -2
  52. package/src/components/ImportFeatures.tsx +1 -1
  53. package/src/components/ManageUsers.tsx +1 -1
  54. package/src/components/ModifyFeatureAttribute.tsx +2 -2
  55. package/src/components/ViewChangeLog.tsx +7 -5
  56. package/src/extensions/annotationFromPileup.ts +2 -2
  57. package/src/index.ts +26 -8
  58. package/src/makeDisplayComponent.tsx +1 -2
  59. package/src/session/ClientDataStore.ts +6 -6
  60. package/src/session/session.ts +6 -3
  61. package/src/util/loadAssemblyIntoClient.ts +6 -3
@@ -0,0 +1,178 @@
1
+ import { AnnotationFeatureI } from '@apollo-annotation/apollo-mst'
2
+ import {
3
+ LocationEndChange,
4
+ LocationStartChange,
5
+ StrandChange,
6
+ TypeChange,
7
+ } from '@apollo-annotation/apollo-shared'
8
+ import { AbstractSessionModel } from '@jbrowse/core/util'
9
+ import {
10
+ FormControl,
11
+ FormControlLabel,
12
+ FormLabel,
13
+ Radio,
14
+ RadioGroup,
15
+ TextField,
16
+ Typography,
17
+ } from '@mui/material'
18
+ import { observer } from 'mobx-react'
19
+ import React, { useState } from 'react'
20
+
21
+ import { OntologyTermAutocomplete } from '../components/OntologyTermAutocomplete'
22
+ import { isOntologyClass } from '../OntologyManager'
23
+ import OntologyStore from '../OntologyManager/OntologyStore'
24
+ import { fetchValidDescendantTerms } from '../OntologyManager/util'
25
+ import { ApolloSessionModel } from '../session'
26
+ import { NumberTextField } from './NumberTextField'
27
+
28
+ export const BasicInformation = observer(function BasicInformation({
29
+ assembly,
30
+ feature,
31
+ session,
32
+ }: {
33
+ feature: AnnotationFeatureI
34
+ session: ApolloSessionModel
35
+ assembly: string
36
+ }) {
37
+ const [errorMessage, setErrorMessage] = useState('')
38
+ const [typeWarningText, setTypeWarningText] = useState('')
39
+
40
+ const { _id, assemblyId, end, start, strand, type } = feature
41
+
42
+ const notifyError = (e: Error) =>
43
+ (session as unknown as AbstractSessionModel).notify(e.message, 'error')
44
+
45
+ const { changeManager } = session.apolloDataStore
46
+ function handleTypeChange(newType: string) {
47
+ setErrorMessage('')
48
+ const featureId = _id
49
+ const change = new TypeChange({
50
+ typeName: 'TypeChange',
51
+ changedIds: [featureId],
52
+ featureId,
53
+ oldType: type,
54
+ newType,
55
+ assembly: assemblyId,
56
+ })
57
+ return changeManager.submit(change)
58
+ }
59
+
60
+ function handleStrandChange(event: React.ChangeEvent<HTMLInputElement>) {
61
+ const { value } = event.target
62
+ const newStrand = value ? (Number(value) as 1 | -1) : undefined
63
+ const change = new StrandChange({
64
+ typeName: 'StrandChange',
65
+ changedIds: [_id],
66
+ featureId: _id,
67
+ oldStrand: strand,
68
+ newStrand,
69
+ assembly,
70
+ })
71
+ return changeManager.submit(change)
72
+ }
73
+
74
+ function handleStartChange(newStart: number) {
75
+ newStart--
76
+ const change = new LocationStartChange({
77
+ typeName: 'LocationStartChange',
78
+ changedIds: [_id],
79
+ featureId: _id,
80
+ oldStart: start,
81
+ newStart,
82
+ assembly,
83
+ })
84
+ return changeManager.submit(change)
85
+ }
86
+
87
+ function handleEndChange(newEnd: number) {
88
+ const change = new LocationEndChange({
89
+ typeName: 'LocationEndChange',
90
+ changedIds: [_id],
91
+ featureId: _id,
92
+ oldEnd: end,
93
+ newEnd,
94
+ assembly,
95
+ })
96
+ return changeManager.submit(change)
97
+ }
98
+
99
+ async function fetchValidTerms(
100
+ parentFeature: AnnotationFeatureI | undefined,
101
+ ontologyStore: OntologyStore,
102
+ _signal: AbortSignal,
103
+ ) {
104
+ const terms = await fetchValidDescendantTerms(
105
+ parentFeature,
106
+ ontologyStore,
107
+ _signal,
108
+ )
109
+ if (!terms) {
110
+ setTypeWarningText(
111
+ `Type "${parentFeature?.type}" does not have any children in the ontology`,
112
+ )
113
+ return
114
+ }
115
+ return terms
116
+ }
117
+
118
+ return (
119
+ <>
120
+ <Typography variant="h4">Basic information</Typography>
121
+ <NumberTextField
122
+ margin="dense"
123
+ id="start"
124
+ label="Start"
125
+ fullWidth
126
+ variant="outlined"
127
+ value={start + 1}
128
+ onChangeCommitted={handleStartChange}
129
+ />
130
+ <NumberTextField
131
+ margin="dense"
132
+ id="end"
133
+ label="End"
134
+ fullWidth
135
+ variant="outlined"
136
+ value={end}
137
+ onChangeCommitted={handleEndChange}
138
+ />
139
+ <OntologyTermAutocomplete
140
+ session={session}
141
+ ontologyName="Sequence Ontology"
142
+ value={type}
143
+ filterTerms={isOntologyClass}
144
+ fetchValidTerms={fetchValidTerms.bind(null, feature)}
145
+ renderInput={(params) => (
146
+ <TextField
147
+ {...params}
148
+ label="Type"
149
+ variant="outlined"
150
+ fullWidth
151
+ error={Boolean(typeWarningText)}
152
+ helperText={typeWarningText}
153
+ />
154
+ )}
155
+ onChange={(oldValue, newValue) => {
156
+ if (newValue) {
157
+ handleTypeChange(newValue).catch(notifyError)
158
+ }
159
+ }}
160
+ />
161
+ <FormControl>
162
+ <FormLabel>Strand</FormLabel>
163
+ <RadioGroup row value={strand ?? ''} onChange={handleStrandChange}>
164
+ <FormControlLabel value="1" control={<Radio />} label="Forward (+)" />
165
+ <FormControlLabel
166
+ value="-1"
167
+ control={<Radio />}
168
+ label="Reverse (-)"
169
+ />
170
+ <FormControlLabel value="" control={<Radio />} label="None" />
171
+ </RadioGroup>
172
+ </FormControl>
173
+ {errorMessage ? (
174
+ <Typography color="error">{errorMessage}</Typography>
175
+ ) : null}
176
+ </>
177
+ )
178
+ })
@@ -0,0 +1,75 @@
1
+ import { TextField, TextFieldProps } from '@mui/material'
2
+ import { observer } from 'mobx-react'
3
+ import React, { useEffect, useState } from 'react'
4
+
5
+ interface NumberTextFieldProps
6
+ extends Omit<
7
+ TextFieldProps,
8
+ | 'type'
9
+ | 'onChange'
10
+ | 'onKeyDown'
11
+ | 'onBlur'
12
+ | 'ref'
13
+ | 'error'
14
+ | 'helperText'
15
+ > {
16
+ onChangeCommitted(newValue: number): void
17
+ value: unknown
18
+ }
19
+
20
+ export const NumberTextField = observer(function NumberTextField({
21
+ onChangeCommitted,
22
+ value: initialValue,
23
+ ...props
24
+ }: NumberTextFieldProps) {
25
+ const [value, setValue] = useState(String(initialValue))
26
+ const [blur, setBlur] = useState(false)
27
+ const [inputNode, setInputNode] = useState<HTMLDivElement | null>(null)
28
+
29
+ useEffect(() => {
30
+ setValue(String(initialValue))
31
+ }, [initialValue])
32
+
33
+ useEffect(() => {
34
+ if (blur) {
35
+ inputNode?.blur()
36
+ setBlur(false)
37
+ }
38
+ }, [blur, inputNode])
39
+
40
+ function onChange(event: React.ChangeEvent<HTMLInputElement>) {
41
+ setValue(event.target.value)
42
+ }
43
+
44
+ const error = Number.isNaN(Number(value))
45
+
46
+ return (
47
+ <TextField
48
+ {...props}
49
+ type="text"
50
+ onChange={onChange}
51
+ value={value}
52
+ onKeyDown={(event) => {
53
+ if (event.key === 'Enter') {
54
+ inputNode?.blur()
55
+ } else if (event.key === 'Escape') {
56
+ setValue(String(initialValue))
57
+ setBlur(true)
58
+ }
59
+ }}
60
+ onBlur={() => {
61
+ const valueAsNumber = Number(value)
62
+ if (value !== String(initialValue)) {
63
+ if (Number.isNaN(valueAsNumber)) {
64
+ setValue(String(initialValue))
65
+ } else {
66
+ onChangeCommitted(valueAsNumber)
67
+ }
68
+ }
69
+ }}
70
+ inputRef={(node) => setInputNode(node)}
71
+ error={error}
72
+ helperText={error ? 'Not a valid number' : undefined}
73
+ />
74
+ )
75
+ })
@@ -0,0 +1,87 @@
1
+ import {
2
+ AnnotationFeature,
3
+ AnnotationFeatureI,
4
+ } from '@apollo-annotation/apollo-mst'
5
+ import { SessionWithWidgets } from '@jbrowse/core/util'
6
+ import { Button, Paper, Typography } from '@mui/material'
7
+ import { observer } from 'mobx-react'
8
+ import { IMSTMap } from 'mobx-state-tree'
9
+ import React from 'react'
10
+ import { makeStyles } from 'tss-react/mui'
11
+
12
+ import { ApolloSessionModel } from '../session'
13
+
14
+ const useStyles = makeStyles()((theme) => ({
15
+ paper: {
16
+ margin: theme.spacing(2),
17
+ padding: theme.spacing(2),
18
+ display: 'flex',
19
+ flexDirection: 'column',
20
+ },
21
+ }))
22
+
23
+ export const RelatedFeatures = observer(function RelatedFeatures({
24
+ assembly,
25
+ feature,
26
+ refName,
27
+ session,
28
+ }: {
29
+ feature: AnnotationFeatureI
30
+ refName: string
31
+ session: ApolloSessionModel
32
+ assembly: string
33
+ }) {
34
+ const { classes } = useStyles()
35
+ const { parent } = feature
36
+ const { children } = feature as {
37
+ children?: IMSTMap<typeof AnnotationFeature>
38
+ }
39
+
40
+ const onButtonClick = (newFeature: AnnotationFeatureI) => {
41
+ if (parent) {
42
+ const apolloFeatureWidget = (
43
+ session as unknown as SessionWithWidgets
44
+ ).addWidget('ApolloFeatureDetailsWidget', 'apolloFeatureDetailsWidget', {
45
+ feature: newFeature,
46
+ assembly,
47
+ refName,
48
+ })
49
+ ;(session as unknown as SessionWithWidgets).showWidget?.(
50
+ apolloFeatureWidget,
51
+ )
52
+ }
53
+ }
54
+
55
+ if (!(parent || (children && children.size > 0))) {
56
+ return null
57
+ }
58
+ return (
59
+ <>
60
+ <Typography variant="h4">Related features</Typography>
61
+ {parent && (
62
+ <>
63
+ <Typography variant="h5">Parent</Typography>
64
+ <Paper elevation={6} className={classes.paper}>
65
+ {`Start: ${parent.start}, End: ${parent.end}, Type: ${parent.type}`}
66
+ <Button variant="contained" onClick={() => onButtonClick(parent)}>
67
+ Go to parent
68
+ </Button>
69
+ </Paper>
70
+ </>
71
+ )}
72
+ {children && children.size > 0 && (
73
+ <>
74
+ <Typography variant="h5">Children</Typography>
75
+ {[...children.values()].map((child) => (
76
+ <Paper elevation={6} className={classes.paper} key={child._id}>
77
+ {`Start: ${child.start}, End: ${child.end}, Type: ${child.type}`}
78
+ <Button variant="contained" onClick={() => onButtonClick(child)}>
79
+ Go to child
80
+ </Button>
81
+ </Paper>
82
+ ))}
83
+ </>
84
+ )}
85
+ </>
86
+ )
87
+ })
@@ -0,0 +1,88 @@
1
+ import { AnnotationFeatureI } from '@apollo-annotation/apollo-mst'
2
+ import { splitStringIntoChunks } from '@apollo-annotation/apollo-shared'
3
+ import { Button, Typography } from '@mui/material'
4
+ import { observer } from 'mobx-react'
5
+ import React, { useState } from 'react'
6
+ import { makeStyles } from 'tss-react/mui'
7
+
8
+ import { ApolloSessionModel } from '../session'
9
+
10
+ function formatSequence(
11
+ seq: string,
12
+ refName: string,
13
+ start: number,
14
+ end: number,
15
+ wrap?: number,
16
+ ) {
17
+ const header = `>${refName}:${start + 1}–${end}\n`
18
+ const body =
19
+ wrap === undefined ? seq : splitStringIntoChunks(seq, wrap).join('\n')
20
+ return `${header}${body}`
21
+ }
22
+
23
+ const useStyles = makeStyles()({
24
+ sequence: {
25
+ width: '100%',
26
+ resize: 'vertical',
27
+ },
28
+ })
29
+
30
+ export const Sequence = observer(function Sequence({
31
+ assembly,
32
+ feature,
33
+ refName,
34
+ session,
35
+ }: {
36
+ assembly: string
37
+ feature: AnnotationFeatureI
38
+ refName: string
39
+ session: ApolloSessionModel
40
+ }) {
41
+ const currentAssembly = session.apolloDataStore.assemblies.get(assembly)
42
+ const [showSequence, setShowSequence] = useState(false)
43
+ const { classes } = useStyles()
44
+
45
+ const onButtonClick = () => {
46
+ setShowSequence(!showSequence)
47
+ }
48
+
49
+ if (!(feature && currentAssembly)) {
50
+ return null
51
+ }
52
+ const refSeq = currentAssembly.getByRefName(refName)
53
+ if (!refSeq) {
54
+ return null
55
+ }
56
+ const { end, start } = feature
57
+ let sequence = ''
58
+ if (showSequence) {
59
+ sequence = refSeq.getSequence(start, end)
60
+ if (sequence) {
61
+ sequence = formatSequence(sequence, refName, start, end)
62
+ } else {
63
+ void session.apolloDataStore.loadRefSeq([
64
+ { assemblyName: assembly, refName, start, end },
65
+ ])
66
+ }
67
+ }
68
+
69
+ return (
70
+ <>
71
+ <Typography variant="h4">Sequence</Typography>
72
+ <Button variant="contained" onClick={onButtonClick}>
73
+ {showSequence ? 'Hide sequence' : 'Show sequence'}
74
+ </Button>
75
+ <div>
76
+ {showSequence && (
77
+ <textarea
78
+ readOnly
79
+ rows={20}
80
+ className={classes.sequence}
81
+ value={sequence}
82
+ />
83
+ )}
84
+ </div>
85
+ </>
86
+ )
87
+ })
88
+ export default Sequence
@@ -0,0 +1,60 @@
1
+ import { TextField, TextFieldProps } from '@mui/material'
2
+ import { observer } from 'mobx-react'
3
+ import React, { useEffect, useState } from 'react'
4
+
5
+ interface StringTextFieldProps
6
+ extends Omit<
7
+ TextFieldProps,
8
+ 'type' | 'onChange' | 'onKeyDown' | 'onBlur' | 'ref'
9
+ > {
10
+ onChangeCommitted(newValue: string): void
11
+ value: unknown
12
+ }
13
+
14
+ export const StringTextField = observer(function StringTextField({
15
+ onChangeCommitted,
16
+ value: initialValue,
17
+ ...props
18
+ }: StringTextFieldProps) {
19
+ const [value, setValue] = useState(String(initialValue))
20
+ const [blur, setBlur] = useState(false)
21
+ const [inputNode, setInputNode] = useState<HTMLDivElement | null>(null)
22
+
23
+ useEffect(() => {
24
+ setValue(String(initialValue))
25
+ }, [initialValue])
26
+
27
+ useEffect(() => {
28
+ if (blur) {
29
+ inputNode?.blur()
30
+ setBlur(false)
31
+ }
32
+ }, [blur, inputNode])
33
+
34
+ function onChange(event: React.ChangeEvent<HTMLInputElement>) {
35
+ setValue(event.target.value)
36
+ }
37
+
38
+ return (
39
+ <TextField
40
+ {...props}
41
+ type="text"
42
+ onChange={onChange}
43
+ value={value}
44
+ onKeyDown={(event) => {
45
+ if (event.key === 'Enter') {
46
+ inputNode?.blur()
47
+ } else if (event.key === 'Escape') {
48
+ setValue(String(initialValue))
49
+ setBlur(true)
50
+ }
51
+ }}
52
+ onBlur={() => {
53
+ if (value !== String(initialValue)) {
54
+ onChangeCommitted(value)
55
+ }
56
+ }}
57
+ inputRef={(node) => setInputNode(node)}
58
+ />
59
+ )
60
+ })
@@ -0,0 +1,2 @@
1
+ export { ApolloFeatureDetailsWidget } from './ApolloFeatureDetailsWidget'
2
+ export { ApolloFeatureDetailsWidgetModel } from './model'
@@ -0,0 +1,67 @@
1
+ import {
2
+ AnnotationFeature,
3
+ AnnotationFeatureI,
4
+ } from '@apollo-annotation/apollo-mst'
5
+ import { getSession } from '@jbrowse/core/util'
6
+ import { ElementId } from '@jbrowse/core/util/types/mst'
7
+ import { autorun } from 'mobx'
8
+ import { Instance, SnapshotIn, addDisposer, types } from 'mobx-state-tree'
9
+
10
+ import { ApolloSessionModel } from '../session'
11
+
12
+ export const ApolloFeatureDetailsWidgetModel = types
13
+ .model('ApolloFeatureDetailsWidget', {
14
+ id: ElementId,
15
+ type: types.literal('ApolloFeatureDetailsWidget'),
16
+ feature: types.maybe(
17
+ types.reference(AnnotationFeature, {
18
+ onInvalidated(ev) {
19
+ ev.parent.setTryReload(ev.invalidId)
20
+ ev.removeRef()
21
+ },
22
+ }),
23
+ ),
24
+ assembly: types.string,
25
+ refName: types.string,
26
+ })
27
+ .volatile(() => ({
28
+ tryReload: undefined as string | undefined,
29
+ }))
30
+ .actions((self) => ({
31
+ setFeature(feature: AnnotationFeatureI) {
32
+ self.feature = feature
33
+ },
34
+ setTryReload(featureId?: string) {
35
+ self.tryReload = featureId
36
+ },
37
+ }))
38
+ .actions((self) => ({
39
+ afterAttach() {
40
+ addDisposer(
41
+ self,
42
+ autorun((reaction) => {
43
+ if (!self.tryReload) {
44
+ return
45
+ }
46
+ const session = getSession(self) as unknown as ApolloSessionModel
47
+ const { apolloDataStore } = session
48
+ if (!apolloDataStore) {
49
+ return
50
+ }
51
+ const feature = apolloDataStore.getFeature(self.tryReload)
52
+ if (feature) {
53
+ self.setFeature(feature)
54
+ self.setTryReload()
55
+ reaction.dispose()
56
+ }
57
+ }),
58
+ )
59
+ },
60
+ }))
61
+
62
+ export type ApolloFeatureDetailsWidget = Instance<
63
+ typeof ApolloFeatureDetailsWidgetModel
64
+ >
65
+ export type ApolloFeatureDetailsWidgetSnapshot = SnapshotIn<
66
+ typeof ApolloFeatureDetailsWidgetModel
67
+ >
@@ -1,6 +1,9 @@
1
+ import { AnnotationFeatureI } from '@apollo-annotation/apollo-mst'
2
+ import {
3
+ LocationEndChange,
4
+ LocationStartChange,
5
+ } from '@apollo-annotation/apollo-shared'
1
6
  import { Theme, alpha } from '@mui/material'
2
- import { AnnotationFeatureI } from 'apollo-mst'
3
- import { LocationEndChange, LocationStartChange } from 'apollo-shared'
4
7
 
5
8
  import { LinearApolloDisplay } from '../stateModel'
6
9
  import { MousePosition } from '../stateModel/mouseEvents'
@@ -1,17 +1,18 @@
1
- import { alpha } from '@mui/material'
2
- import { AnnotationFeatureI } from 'apollo-mst'
1
+ import { AnnotationFeatureI } from '@apollo-annotation/apollo-mst'
3
2
  import {
4
3
  DiscontinuousLocationEndChange,
5
4
  DiscontinuousLocationStartChange,
6
5
  LocationEndChange,
7
6
  LocationStartChange,
8
- } from 'apollo-shared'
7
+ } from '@apollo-annotation/apollo-shared'
8
+ import { alpha } from '@mui/material'
9
9
 
10
10
  import { LinearApolloDisplay } from '../stateModel'
11
11
  import {
12
12
  CDSDiscontinuousLocation,
13
13
  MousePosition,
14
14
  } from '../stateModel/mouseEvents'
15
+ import { frameColors, getFrame } from '../stateModel/rendering'
15
16
  import { CanvasMouseEvent } from '../types'
16
17
  import { Glyph } from './Glyph'
17
18
 
@@ -248,10 +249,17 @@ export class CanonicalGeneGlyph extends Glyph {
248
249
  ctx.fillRect(startPx, cdsTop, widthPx, cdsHeight)
249
250
  if (widthPx > 2) {
250
251
  ctx.clearRect(startPx + 1, cdsTop + 1, widthPx - 2, cdsHeight - 2)
252
+ const frame = getFrame(
253
+ cdsLocation.start,
254
+ cdsLocation.end,
255
+ cdsLocation.strand,
256
+ cdsLocation.phase,
257
+ )
258
+ const cdsColorCode = frameColors.at(frame) ?? 'rgb(171,71,188)'
251
259
  ctx.fillStyle =
252
260
  apolloSelectedFeature && cds._id === apolloSelectedFeature._id
253
261
  ? 'rgb(0,0,0)'
254
- : 'rgb(171,71,188)'
262
+ : cdsColorCode
255
263
  ctx.fillRect(startPx + 1, cdsTop + 1, widthPx - 2, cdsHeight - 2)
256
264
  if (forwardFill && backwardFill && strand) {
257
265
  const reversal = reversed ? -1 : 1
@@ -1,4 +1,4 @@
1
- import { AnnotationFeatureI } from 'apollo-mst'
1
+ import { AnnotationFeatureI } from '@apollo-annotation/apollo-mst'
2
2
 
3
3
  import { LinearApolloDisplay } from '../stateModel'
4
4
  import { MousePosition } from '../stateModel/mouseEvents'
@@ -1,7 +1,7 @@
1
+ import { AnnotationFeatureI } from '@apollo-annotation/apollo-mst'
1
2
  import { MenuItem } from '@jbrowse/core/ui'
2
- import { AbstractSessionModel } from '@jbrowse/core/util'
3
+ import { AbstractSessionModel, SessionWithWidgets } from '@jbrowse/core/util'
3
4
  import { alpha } from '@mui/material'
4
- import { AnnotationFeatureI } from 'apollo-mst'
5
5
 
6
6
  import {
7
7
  AddChildFeature,
@@ -375,6 +375,25 @@ export abstract class Glyph {
375
375
  )
376
376
  },
377
377
  },
378
+ {
379
+ label: 'Edit feature details',
380
+ onClick: () => {
381
+ const apolloFeatureWidget = (
382
+ session as unknown as SessionWithWidgets
383
+ ).addWidget(
384
+ 'ApolloFeatureDetailsWidget',
385
+ 'apolloFeatureDetailsWidget',
386
+ {
387
+ feature: sourceFeature,
388
+ assembly: currentAssemblyId,
389
+ refName: region.refName,
390
+ },
391
+ )
392
+ ;(session as unknown as SessionWithWidgets).showWidget?.(
393
+ apolloFeatureWidget,
394
+ )
395
+ },
396
+ },
378
397
  )
379
398
  }
380
399
  return menuItems
@@ -1,9 +1,13 @@
1
+ import { AnnotationFeatureI } from '@apollo-annotation/apollo-mst'
2
+ import {
3
+ LocationEndChange,
4
+ LocationStartChange,
5
+ } from '@apollo-annotation/apollo-shared'
1
6
  import { alpha } from '@mui/material'
2
- import { AnnotationFeatureI } from 'apollo-mst'
3
- import { LocationEndChange, LocationStartChange } from 'apollo-shared'
4
7
 
5
8
  import { LinearApolloDisplay } from '../stateModel'
6
9
  import { MousePosition } from '../stateModel/mouseEvents'
10
+ import { frameColors, getFrame } from '../stateModel/rendering'
7
11
  import { CanvasMouseEvent } from '../types'
8
12
  import { Glyph } from './Glyph'
9
13
 
@@ -126,13 +130,22 @@ export class ImplicitExonGeneGlyph extends Glyph {
126
130
  ctx.fillRect(startPx, cdsOrUTRTop, widthPx, height)
127
131
  if (widthPx > 2) {
128
132
  ctx.clearRect(startPx + 1, cdsOrUTRTop + 1, widthPx - 2, height - 2)
133
+ let colorCode = 'rgb(211,211,211)'
134
+ if (isCDS) {
135
+ const frame = getFrame(
136
+ cdsOrUTR.start,
137
+ cdsOrUTR.end,
138
+ cdsOrUTR.strand,
139
+ cdsOrUTR.phase,
140
+ )
141
+ const color = frameColors.at(frame)
142
+ colorCode = color ?? 'rgb(171,71,188)'
143
+ }
129
144
  ctx.fillStyle =
130
145
  apolloSelectedFeature &&
131
146
  cdsOrUTR._id === apolloSelectedFeature._id
132
147
  ? 'rgb(0,0,0)'
133
- : isCDS
134
- ? 'rgb(171,71,188)'
135
- : 'rgb(211,211,211)'
148
+ : colorCode
136
149
  ctx.fillRect(startPx + 1, cdsOrUTRTop + 1, widthPx - 2, height - 2)
137
150
  if (forwardFill && backwardFill && strand) {
138
151
  const reversal = reversed ? -1 : 1