@apollo-annotation/jbrowse-plugin-apollo 1.0.1 → 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 +826 -433
  89. package/dist/index.esm.js.map +1 -1
  90. package/dist/jbrowse-plugin-apollo.cjs.development.js +821 -428
  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 +859 -467
  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 +9 -3
  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,108 @@
1
+ /* eslint-disable @typescript-eslint/unbound-method */
2
+ /* eslint-disable @typescript-eslint/no-misused-promises */
3
+ import {
4
+ AddFeatureChange,
5
+ gff3ToAnnotationFeature,
6
+ } from '@apollo-annotation/shared'
7
+ import { GFFTransformer } from '@gmod/gff'
8
+ import {
9
+ Button,
10
+ DialogActions,
11
+ DialogContent,
12
+ DialogContentText,
13
+ LinearProgress,
14
+ } from '@mui/material'
15
+ import React, { useState } from 'react'
16
+
17
+ import type { ChangeManager } from '../ChangeManager'
18
+ import type { ApolloSessionModel } from '../session'
19
+
20
+ import { Dialog } from './Dialog'
21
+
22
+ interface ImportAnnotationsProps {
23
+ session: ApolloSessionModel
24
+ handleClose(): void
25
+ assemblyName: string
26
+ }
27
+
28
+ export function ImportAnnotations({
29
+ assemblyName,
30
+ handleClose,
31
+ session,
32
+ }: ImportAnnotationsProps) {
33
+ const { changeManager } = session.apolloDataStore as {
34
+ changeManager: ChangeManager
35
+ }
36
+ const [file, setFile] = useState<File>()
37
+ const [errorMessage, setErrorMessage] = useState('')
38
+ const [loading, setLoading] = useState(false)
39
+
40
+ function handleChangeFile(e: React.ChangeEvent<HTMLInputElement>) {
41
+ if (e.target.files) {
42
+ setFile(e.target.files[0])
43
+ }
44
+ }
45
+
46
+ async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
47
+ event.preventDefault()
48
+ if (!file) {
49
+ return
50
+ }
51
+ setLoading(true)
52
+ setErrorMessage('')
53
+ try {
54
+ const stream = file
55
+ .stream()
56
+ .pipeThrough(
57
+ new TransformStream(new GFFTransformer({ parseSequences: false })),
58
+ )
59
+ for await (const value of stream) {
60
+ const annotationFeature = gff3ToAnnotationFeature(value)
61
+ const change = new AddFeatureChange({
62
+ changedIds: [annotationFeature._id],
63
+ typeName: 'AddFeatureChange',
64
+ assembly: assemblyName,
65
+ addedFeature: annotationFeature,
66
+ })
67
+ await changeManager.submit(change)
68
+ }
69
+ handleClose()
70
+ } catch (error) {
71
+ setErrorMessage(String(error))
72
+ } finally {
73
+ setLoading(false)
74
+ }
75
+ }
76
+
77
+ return (
78
+ <Dialog open title="Import annotations" handleClose={handleClose}>
79
+ {loading ? <LinearProgress /> : null}
80
+ <form onSubmit={onSubmit}>
81
+ <DialogContent>
82
+ <DialogContentText>
83
+ Import annotations from a GFF3 file into the current Apollo track.
84
+ Features are parsed and stored locally in the browser, so importing
85
+ very large GFF3 files is not recommended.
86
+ </DialogContentText>
87
+ <input
88
+ type="file"
89
+ accept=".gff,.gff3"
90
+ onChange={handleChangeFile}
91
+ disabled={loading}
92
+ />
93
+ {errorMessage ? (
94
+ <DialogContentText color="error">{errorMessage}</DialogContentText>
95
+ ) : null}
96
+ </DialogContent>
97
+ <DialogActions>
98
+ <Button variant="contained" type="submit" disabled={!file || loading}>
99
+ Import
100
+ </Button>
101
+ <Button variant="outlined" onClick={handleClose}>
102
+ Cancel
103
+ </Button>
104
+ </DialogActions>
105
+ </form>
106
+ </Dialog>
107
+ )
108
+ }
@@ -25,7 +25,6 @@ import {
25
25
  type GridRowId,
26
26
  type GridRowModel,
27
27
  type GridRowParams,
28
- GridToolbar,
29
28
  } from '@mui/x-data-grid'
30
29
  import React, { useEffect, useState } from 'react'
31
30
 
@@ -215,7 +214,7 @@ export function ManageUsers({
215
214
  rows={users}
216
215
  columns={gridColumns}
217
216
  getRowId={(row) => row._id}
218
- slots={{ toolbar: GridToolbar }}
217
+ showToolbar
219
218
  getRowHeight={() => 'auto'}
220
219
  isCellEditable={(params: GridCellParams) =>
221
220
  !isCurrentUser(params.id)
@@ -235,8 +235,6 @@ async function getValidTerms(
235
235
  }
236
236
  }
237
237
 
238
- if (!result) {
239
- result = await ontologyStore.getAllTerms()
240
- }
238
+ result ??= await ontologyStore.getAllTerms()
241
239
  return filterTerms ? result.filter((element) => filterTerms(element)) : result
242
240
  }
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @eslint-react/set-state-in-effect */
1
2
  /* eslint-disable @typescript-eslint/unbound-method */
2
3
  /* eslint-disable @typescript-eslint/use-unknown-in-catch-callback-variable */
3
4
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
@@ -78,7 +79,7 @@ function TermTagWithTooltip({
78
79
  .objectStore('nodes')
79
80
  .get(termUrl)
80
81
 
81
- if (term && term.lbl && !signal.aborted) {
82
+ if (term?.lbl && !signal.aborted) {
82
83
  setDescription(term.lbl || 'no label')
83
84
  }
84
85
  }
@@ -304,6 +305,7 @@ function HighlightedText(props: { str: string; search: string }) {
304
305
  <>
305
306
  {parts.map((part, index) => (
306
307
  <Typography
308
+ // eslint-disable-next-line @eslint-react/no-array-index-key
307
309
  key={index}
308
310
  component="span"
309
311
  sx={{ fontWeight: part.highlight ? 'bold' : 'regular' }}
@@ -4,6 +4,7 @@
4
4
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5
5
  /* eslint-disable @typescript-eslint/no-unsafe-return */
6
6
  import { changeRegistry } from '@apollo-annotation/common'
7
+ import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
7
8
  import type { AbstractSessionModel } from '@jbrowse/core/util'
8
9
  import { makeStyles } from '@jbrowse/core/util/tss-react'
9
10
  import {
@@ -102,7 +103,7 @@ export function ViewChangeLog({
102
103
 
103
104
  const { apolloDataStore } = session
104
105
  const { assemblyManager } = session as unknown as AbstractSessionModel
105
- const assembly = assemblyManager.get(assemblyId)
106
+ const assembly = assemblyManager.get(assemblyId) as Assembly | undefined
106
107
  const assemblyName = assembly?.displayName ?? assemblyId
107
108
 
108
109
  const gridColumns: GridColDef[] = [
@@ -1,12 +1,14 @@
1
1
  export * from './AddAssembly'
2
2
  export * from './AddAssemblyAliases'
3
3
  export * from './AddChildFeature'
4
+ export * from './AddCodingSequence'
4
5
  export * from './AddFeature'
5
6
  export * from './ColorFeature'
6
7
  export * from './CopyFeature'
7
8
  export * from './DeleteAssembly'
8
9
  export * from './DeleteFeature'
9
10
  export * from './DownloadGFF3'
11
+ export * from './ImportAnnotations'
10
12
  export * from './ImportFeatures'
11
13
  export * from './LogOut'
12
14
  export * from './ManageChecks'
@@ -8,11 +8,7 @@ import type {
8
8
  DisplayType,
9
9
  PluggableElementType,
10
10
  } from '@jbrowse/core/pluggableElementTypes'
11
- import {
12
- type AbstractSessionModel,
13
- getContainingView,
14
- getSession,
15
- } from '@jbrowse/core/util'
11
+ import { getContainingView, getSession } from '@jbrowse/core/util'
16
12
  import type { Feature } from '@jbrowse/core/util/simpleFeature'
17
13
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
18
14
  import ObjectID from 'bson-objectid'
@@ -59,9 +55,25 @@ export function annotationFromPileup(pluggableElement: PluggableElementType) {
59
55
  getAnnotationFeature(jbrowseFeature: Feature, refSeqId: string) {
60
56
  const start: number = jbrowseFeature.get('start')
61
57
  const end: number = jbrowseFeature.get('end')
62
- const strand = jbrowseFeature.get('strand') as 1 | -1 | undefined
63
58
  const name = jbrowseFeature.get('name') as string | undefined
64
59
 
60
+ let strand: 1 | -1 | undefined
61
+ const tags = jbrowseFeature.get('tags') as Record<
62
+ string,
63
+ string | number | undefined
64
+ >
65
+ const { ts, TS, XS } = tags
66
+ for (const tag of [ts, TS, XS]) {
67
+ if (!strand && tag) {
68
+ if ([1, '1', '+'].includes(tag)) {
69
+ strand = 1
70
+ } else if ([-1, '-1', '-'].includes(tag)) {
71
+ strand = -1
72
+ }
73
+ }
74
+ }
75
+ strand ??= jbrowseFeature.get('strand') as 1 | -1 | undefined
76
+
65
77
  const cigarData = jbrowseFeature.get('CIGAR') as string
66
78
  const ops = parseCigar(cigarData)
67
79
  let position = start
@@ -81,9 +93,7 @@ export function annotationFromPileup(pluggableElement: PluggableElementType) {
81
93
  case 'M':
82
94
  case '=':
83
95
  case 'X': {
84
- if (currentExonStart === undefined) {
85
- currentExonStart = position
86
- }
96
+ currentExonStart ??= position
87
97
  position += len
88
98
  break
89
99
  }
@@ -190,24 +200,22 @@ export function annotationFromPileup(pluggableElement: PluggableElementType) {
190
200
  }
191
201
  refSeqId = backendRefSeqId
192
202
  }
193
- ;(session as unknown as AbstractSessionModel).queueDialog(
194
- (doneCallback) => [
195
- CreateApolloAnnotation,
196
- {
197
- session,
198
- handleClose: () => {
199
- doneCallback()
200
- },
201
- annotationFeature: self.getAnnotationFeature(
202
- jbrowseFeature,
203
- refSeqId,
204
- ),
205
- assembly,
206
- refSeqId,
207
- region,
203
+ session.queueDialog((doneCallback) => [
204
+ CreateApolloAnnotation,
205
+ {
206
+ session,
207
+ handleClose: () => {
208
+ doneCallback()
208
209
  },
209
- ],
210
- )
210
+ annotationFeature: self.getAnnotationFeature(
211
+ jbrowseFeature,
212
+ refSeqId,
213
+ ),
214
+ assembly,
215
+ refSeqId,
216
+ region,
217
+ },
218
+ ])
211
219
  },
212
220
  },
213
221
  ]
package/src/index.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable @typescript-eslint/unbound-method */
2
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
3
2
  /* eslint-disable @typescript-eslint/no-misused-promises */
4
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
5
4
  import { changeRegistry, checkRegistry } from '@apollo-annotation/common'
@@ -78,8 +77,10 @@ import { addTopLevelMenus } from './menus'
78
77
  import { type ApolloSessionModel, extendSession } from './session'
79
78
 
80
79
  interface RpcHandle {
81
- on(event: string, listener: (event: MessageEvent) => void): this
82
- workers: Worker[]
80
+ client: {
81
+ on?(event: string, listener: (event: MessageEvent) => void): void
82
+ }
83
+ worker: Worker
83
84
  }
84
85
 
85
86
  interface ApolloMessageData {
@@ -356,10 +357,10 @@ export default class ApolloPlugin extends Plugin {
356
357
  pluginManager.addToExtensionPoint(
357
358
  'Core-extendWorker',
358
359
  (handle: RpcHandle) => {
359
- if (!('on' in handle && handle.on)) {
360
+ if (!('on' in handle.client && handle.client.on)) {
360
361
  return handle
361
362
  }
362
- handle.on('apollo', async (event: MessageEvent) => {
363
+ handle.client.on('apollo', async (event: MessageEvent) => {
363
364
  if (!isApolloMessageData(event)) {
364
365
  return
365
366
  }
@@ -382,7 +383,7 @@ export default class ApolloPlugin extends Plugin {
382
383
  }
383
384
  const { seq: sequence } =
384
385
  await backendDriver.getSequence(region)
385
- handle.workers[0].postMessage({
386
+ handle.worker.postMessage({
386
387
  apollo,
387
388
  messageId,
388
389
  sequence,
@@ -404,7 +405,7 @@ export default class ApolloPlugin extends Plugin {
404
405
  break
405
406
  }
406
407
  const regions = await backendDriver.getRegions(assembly)
407
- handle.workers[0].postMessage({
408
+ handle.worker.postMessage({
408
409
  apollo,
409
410
  messageId,
410
411
  regions,
@@ -427,7 +428,7 @@ export default class ApolloPlugin extends Plugin {
427
428
  }
428
429
  const refNameAliases =
429
430
  await backendDriver.getRefNameAliases(assembly)
430
- handle.workers[0].postMessage({
431
+ handle.worker.postMessage({
431
432
  apollo,
432
433
  messageId,
433
434
  refNameAliases,
@@ -12,6 +12,7 @@ import {
12
12
  CheckResult,
13
13
  type CheckResultSnapshot,
14
14
  } from '@apollo-annotation/mst'
15
+ import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
15
16
  import {
16
17
  type AnyConfigurationModel,
17
18
  getConf,
@@ -106,7 +107,9 @@ export function clientDataStoreFactory(
106
107
  let ref = apolloAssembly.refSeqs.get(feature.refSeq)
107
108
  if (!ref) {
108
109
  // maybe it's a valid refName that we haven't loaded yet
109
- const assembly = assemblyManager.get(assemblyId)
110
+ const assembly = assemblyManager.get(assemblyId) as
111
+ | Assembly
112
+ | undefined
110
113
  if (!assembly) {
111
114
  throw new Error(
112
115
  `Could not find assembly "${assemblyId}" to add feature "${feature._id}"`,
@@ -291,19 +294,17 @@ export function clientDataStoreFactory(
291
294
  continue
292
295
  }
293
296
  const { assemblyName, refName } = region
294
- let assembly = self.assemblies.get(assemblyName)
295
- if (!assembly) {
296
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} })
297
- }
297
+ const assembly =
298
+ self.assemblies.get(assemblyName) ??
299
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} })
298
300
  const [firstFeature] = features
299
- let ref = assembly.refSeqs.get(firstFeature.refSeq)
300
- if (!ref) {
301
- ref = assembly.refSeqs.put({
301
+ const ref =
302
+ assembly.refSeqs.get(firstFeature.refSeq) ??
303
+ assembly.refSeqs.put({
302
304
  _id: firstFeature.refSeq,
303
305
  name: refName,
304
306
  features: {},
305
307
  })
306
- }
307
308
  for (const feature of features) {
308
309
  if (!ref.features.has(feature._id)) {
309
310
  ref.features.put(feature)
@@ -320,18 +321,12 @@ export function clientDataStoreFactory(
320
321
  }
321
322
  const { refSeq, seq } = yield backendDriver.getSequence(region)
322
323
  const { assemblyName, end, refName, start } = region
323
- let assembly = self.assemblies.get(assemblyName)
324
- if (!assembly) {
325
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} })
326
- }
327
- let ref = assembly.refSeqs.get(refSeq)
328
- if (!ref) {
329
- ref = assembly.refSeqs.put({
330
- _id: refSeq,
331
- name: refName,
332
- sequence: [],
333
- })
334
- }
324
+ const assembly =
325
+ self.assemblies.get(assemblyName) ??
326
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} })
327
+ const ref =
328
+ assembly.refSeqs.get(refSeq) ??
329
+ assembly.refSeqs.put({ _id: refSeq, name: refName, sequence: [] })
335
330
  ref.addSequence({ start, stop: end, sequence: seq })
336
331
  }
337
332
  }),
@@ -0,0 +1,122 @@
1
+ import type { AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
2
+ import { AddFeatureChange } from '@apollo-annotation/shared'
3
+ import type { AbstractSessionModel } from '@jbrowse/core/util'
4
+ import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
5
+ import equal from 'fast-deep-equal/es6'
6
+
7
+ import { LocalDriver } from '../BackendDrivers'
8
+ import { openDb } from '../BackendDrivers/LocalDriver/db'
9
+ import { DuplicateFeatureDialog } from '../components/DuplicateFeatureDialog'
10
+
11
+ import type { ClientDataStoreModel } from './ClientDataStore'
12
+
13
+ export function toUrlSafeBase64(base64: string) {
14
+ return base64
15
+ .replaceAll('+', '-') // Replace + with -
16
+ .replaceAll('/', '_') // Replace / with _
17
+ .replace(/=+$/, '') // Remove padding characters
18
+ }
19
+
20
+ export function fromUrlSafeBase64(urlSafeBase64: string) {
21
+ let base64 = urlSafeBase64.replaceAll('-', '+').replaceAll('_', '/')
22
+ const pad = base64.length % 4
23
+ if (pad) {
24
+ base64 += '='.repeat(4 - pad)
25
+ }
26
+ return base64
27
+ }
28
+
29
+ export async function compress(data: unknown) {
30
+ const json = JSON.stringify(data)
31
+ const bytes = new TextEncoder().encode(json)
32
+ const stream = new Blob([bytes]).stream()
33
+ const compressionStream = new CompressionStream('gzip')
34
+ const compressedStream = stream.pipeThrough(compressionStream)
35
+ const response = new Response(compressedStream)
36
+ const compressed = await response.arrayBuffer()
37
+ // eslint-disable-next-line unicorn/prefer-code-point
38
+ return btoa(String.fromCharCode(...new Uint8Array(compressed)))
39
+ }
40
+
41
+ // Base64 decode + decompress
42
+ export async function decompress(encoded: string): Promise<unknown> {
43
+ const binaryString = atob(encoded)
44
+ // eslint-disable-next-line unicorn/prefer-code-point
45
+ const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0))
46
+ const stream = new Blob([bytes]).stream()
47
+ const decompressionStream = new DecompressionStream('gzip')
48
+ const decompressedStream = stream.pipeThrough(decompressionStream)
49
+ const response = new Response(decompressedStream)
50
+ const decompressed = await response.arrayBuffer()
51
+ return JSON.parse(new TextDecoder().decode(decompressed))
52
+ }
53
+
54
+ export async function handleApolloFeaturesUrlParam(
55
+ encodedFeatures: string,
56
+ apolloDataStore: ClientDataStoreModel,
57
+ assemblyManager: AbstractSessionModel['assemblyManager'],
58
+ session: AbstractSessionModel,
59
+ ): Promise<void> {
60
+ const base64 = fromUrlSafeBase64(encodedFeatures)
61
+ const decompressed = await decompress(base64)
62
+ const featuresData = decompressed as Record<
63
+ string,
64
+ AnnotationFeatureSnapshot[]
65
+ >
66
+ for (const [assemblyName, featureSnapshots] of Object.entries(featuresData)) {
67
+ const backendDriver = apolloDataStore.getBackendDriver(assemblyName)
68
+ if (!(backendDriver instanceof LocalDriver)) {
69
+ continue
70
+ }
71
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName)) as
72
+ | Assembly
73
+ | undefined
74
+ if (!assembly) {
75
+ throw new Error(`Assembly not found: "${assemblyName}"`)
76
+ }
77
+ const { regions } = assembly
78
+ if (!regions) {
79
+ throw new Error(`Assembly not found: "${assemblyName}"`)
80
+ }
81
+ const refNames = regions.map((r) => r.refName)
82
+ const db = await openDb(assemblyName, refNames)
83
+ for (const featureSnapshot of featureSnapshots) {
84
+ const storeName = `features-${featureSnapshot.refSeq}`
85
+ const existing = (await db.get(storeName, featureSnapshot._id)) as
86
+ | AnnotationFeatureSnapshot
87
+ | undefined
88
+ if (existing === undefined) {
89
+ const change = new AddFeatureChange({
90
+ typeName: 'AddFeatureChange',
91
+ assembly: assemblyName,
92
+ changedIds: [featureSnapshot._id],
93
+ addedFeature: featureSnapshot,
94
+ })
95
+ await apolloDataStore.changeManager.submit(change)
96
+ } else {
97
+ // get rid of undefined values in JSON
98
+ // eslint-disable-next-line unicorn/prefer-structured-clone
99
+ const existingFeature = JSON.parse(
100
+ JSON.stringify(existing),
101
+ ) as AnnotationFeatureSnapshot
102
+ if (!equal(featureSnapshot, existingFeature)) {
103
+ await new Promise<void>((resolve) => {
104
+ session.queueDialog((doneCallback) => [
105
+ DuplicateFeatureDialog,
106
+ {
107
+ featureSnapshot,
108
+ existingFeature,
109
+ assemblyName,
110
+ changeManager: apolloDataStore.changeManager,
111
+ handleClose: () => {
112
+ doneCallback()
113
+ resolve()
114
+ },
115
+ },
116
+ ])
117
+ })
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
@@ -18,9 +18,11 @@ import {
18
18
  readConfObject,
19
19
  } from '@jbrowse/core/configuration'
20
20
  import type { BaseTrackConfig } from '@jbrowse/core/pluggableElementTypes'
21
- import type {
22
- AbstractSessionModel,
23
- SessionWithAddTracks,
21
+ import {
22
+ isElectron,
23
+ type AbstractSessionModel,
24
+ type SessionWithAddTracks,
25
+ type SessionWithDrawerWidgets,
24
26
  } from '@jbrowse/core/util'
25
27
  import {
26
28
  type Instance,
@@ -45,6 +47,7 @@ import {
45
47
  type ClientDataStoreModel,
46
48
  clientDataStoreFactory,
47
49
  } from './ClientDataStore'
50
+ import { handleApolloFeaturesUrlParam } from './handleApolloFeaturesUrlParam'
48
51
 
49
52
  export interface ApolloSession extends AbstractSessionModel {
50
53
  apolloDataStore: ClientDataStoreModel
@@ -169,10 +172,7 @@ export function extendSession(
169
172
  for (const block of dynamicBlocks.contentBlocks) {
170
173
  const { assemblyName, end, refName, start } = block
171
174
  const assembly = self.apolloDataStore.assemblies.get(assemblyName)
172
- if (
173
- assembly &&
174
- assembly.backendDriverType === 'CollaborationServerDriver'
175
- ) {
175
+ if (assembly?.backendDriverType === 'CollaborationServerDriver') {
176
176
  locations.push({ assemblyName, refName, start, end })
177
177
  }
178
178
  }
@@ -245,8 +245,8 @@ export function extendSession(
245
245
  const assembly =
246
246
  self.apolloDataStore.assemblies.get(assemblyName)
247
247
  if (
248
- assembly &&
249
- assembly.backendDriverType === 'CollaborationServerDriver'
248
+ assembly?.backendDriverType ===
249
+ 'CollaborationServerDriver'
250
250
  ) {
251
251
  locations.push({ assemblyName, refName, start, end })
252
252
  }
@@ -409,6 +409,46 @@ export function extendSession(
409
409
  )
410
410
  },
411
411
  }))
412
+ .actions((self) => ({
413
+ async afterCreate() {
414
+ if (isElectron) {
415
+ return
416
+ }
417
+ const url = new URL(globalThis.location.href)
418
+ const apolloFeatures = url.searchParams.get('apolloFeatures')
419
+ if (!apolloFeatures) {
420
+ return
421
+ }
422
+ const { assemblyManager } = self as unknown as AbstractSessionModel
423
+ await handleApolloFeaturesUrlParam(
424
+ apolloFeatures,
425
+ self.apolloDataStore,
426
+ assemblyManager,
427
+ self as unknown as AbstractSessionModel,
428
+ )
429
+ await new Promise((resolve) => setTimeout(resolve, 2000))
430
+ const updatedURL = new URL(globalThis.location.href)
431
+ updatedURL.searchParams.delete('apolloFeatures')
432
+ globalThis.history.replaceState(null, '', updatedURL.toString())
433
+ },
434
+ afterAttach() {
435
+ addDisposer(
436
+ self,
437
+ autorun((reaction) => {
438
+ const { focusedViewId, activeWidgets } =
439
+ self as unknown as SessionWithDrawerWidgets
440
+ if (!(focusedViewId && activeWidgets)) {
441
+ return
442
+ }
443
+ const trackSelector = activeWidgets.get('hierarchicalTrackSelector')
444
+ // @ts-expect-error Don't have type for track selector
445
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
446
+ trackSelector?.setView(focusedViewId)
447
+ reaction.dispose()
448
+ }),
449
+ )
450
+ },
451
+ }))
412
452
 
413
453
  .views((self) => {
414
454
  const superTrackActions = (self as unknown as AbstractSessionModel)
@@ -131,7 +131,7 @@ export function removeSkippedAttributes(
131
131
  if (feature.children) {
132
132
  const newChildren: Record<string, AnnotationFeatureSnapshot> = {}
133
133
  for (const [childId, child] of Object.entries(feature.children)) {
134
- const newChild = { ...child } as AnnotationFeatureSnapshot
134
+ const newChild = { ...child }
135
135
  removeSkippedAttributes(newChild, skippedAttributes)
136
136
  newChildren[childId] = newChild
137
137
  }
@@ -171,7 +171,7 @@ export function colorCode(letter: string, theme: Theme) {
171
171
  letterUpper === 'G' ||
172
172
  letterUpper === 'T'
173
173
  ) {
174
- return theme.palette.bases[letterUpper].main.toString()
174
+ return theme.palette.bases[letterUpper].main
175
175
  }
176
176
  return 'lightgray'
177
177
  }
@@ -275,7 +275,7 @@ export function isSelectedFeature(
275
275
  feature: AnnotationFeature,
276
276
  selectedFeature: AnnotationFeature | undefined,
277
277
  ) {
278
- return Boolean(selectedFeature && feature._id === selectedFeature._id)
278
+ return feature._id === selectedFeature?._id
279
279
  }
280
280
 
281
281
  function makeFeatureLabel(feature: AnnotationFeature) {
@@ -314,8 +314,8 @@ export function getContextMenuItemsForFeature(
314
314
  const role = internetAccount ? internetAccount.role : 'admin'
315
315
  const readOnly = !(role && ['admin', 'user'].includes(role))
316
316
  const [region] = regions
317
- const sourceAssemblyId = display.getAssemblyId(region.assemblyName)
318
- const currentAssemblyId = display.getAssemblyId(region.assemblyName)
317
+ const sourceAssemblyId = display.getAssemblyId(region.assemblyName) as string
318
+ const currentAssemblyId = display.getAssemblyId(region.assemblyName) as string
319
319
  menuItems.push(
320
320
  {
321
321
  label: makeFeatureLabel(sourceFeature),