@apollo-annotation/jbrowse-plugin-apollo 0.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 (116) hide show
  1. package/README.md +76 -0
  2. package/dist/index.esm.js +10248 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +7 -0
  5. package/dist/jbrowse-plugin-apollo.cjs.development.js +10298 -0
  6. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -0
  7. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +2 -0
  8. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -0
  9. package/dist/jbrowse-plugin-apollo.umd.development.js +46957 -0
  10. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -0
  11. package/dist/jbrowse-plugin-apollo.umd.production.min.js +2 -0
  12. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -0
  13. package/package.json +130 -0
  14. package/src/ApolloInternetAccount/addMenuItems.ts +94 -0
  15. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +121 -0
  16. package/src/ApolloInternetAccount/components/LoginButtons.tsx +62 -0
  17. package/src/ApolloInternetAccount/components/LoginIcons.tsx +74 -0
  18. package/src/ApolloInternetAccount/configSchema.ts +26 -0
  19. package/src/ApolloInternetAccount/index.ts +2 -0
  20. package/src/ApolloInternetAccount/model.ts +448 -0
  21. package/src/ApolloJobModel.ts +117 -0
  22. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +186 -0
  23. package/src/ApolloSequenceAdapter/configSchema.ts +12 -0
  24. package/src/ApolloSequenceAdapter/index.ts +21 -0
  25. package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +12 -0
  26. package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +692 -0
  27. package/src/ApolloSixFrameRenderer/configSchema.ts +7 -0
  28. package/src/ApolloSixFrameRenderer/index.ts +3 -0
  29. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +64 -0
  30. package/src/ApolloTextSearchAdapter/configSchema.ts +24 -0
  31. package/src/ApolloTextSearchAdapter/index.ts +18 -0
  32. package/src/BackendDrivers/BackendDriver.ts +31 -0
  33. package/src/BackendDrivers/CollaborationServerDriver.ts +318 -0
  34. package/src/BackendDrivers/DesktopFileDriver.ts +170 -0
  35. package/src/BackendDrivers/InMemoryFileDriver.ts +76 -0
  36. package/src/BackendDrivers/index.ts +4 -0
  37. package/src/ChangeManager.ts +148 -0
  38. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +248 -0
  39. package/src/LinearApolloDisplay/components/index.ts +1 -0
  40. package/src/LinearApolloDisplay/configSchema.ts +16 -0
  41. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +422 -0
  42. package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +1191 -0
  43. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +151 -0
  44. package/src/LinearApolloDisplay/glyphs/Glyph.ts +382 -0
  45. package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +697 -0
  46. package/src/LinearApolloDisplay/glyphs/index.ts +4 -0
  47. package/src/LinearApolloDisplay/index.ts +2 -0
  48. package/src/LinearApolloDisplay/stateModel/base.ts +146 -0
  49. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +39 -0
  50. package/src/LinearApolloDisplay/stateModel/glyphs.ts +45 -0
  51. package/src/LinearApolloDisplay/stateModel/index.ts +20 -0
  52. package/src/LinearApolloDisplay/stateModel/layouts.ts +230 -0
  53. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +513 -0
  54. package/src/LinearApolloDisplay/stateModel/rendering.ts +441 -0
  55. package/src/LinearApolloDisplay/stateModel/trackHeightMixin.ts +43 -0
  56. package/src/LinearApolloDisplay/types.ts +1 -0
  57. package/src/OntologyManager/OntologyStore/__snapshots__/fulltext.test.ts.snap +208 -0
  58. package/src/OntologyManager/OntologyStore/__snapshots__/index.test.ts.snap +18846 -0
  59. package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +137 -0
  60. package/src/OntologyManager/OntologyStore/fulltext.test.ts +94 -0
  61. package/src/OntologyManager/OntologyStore/fulltext.ts +264 -0
  62. package/src/OntologyManager/OntologyStore/index.test.ts +130 -0
  63. package/src/OntologyManager/OntologyStore/index.ts +526 -0
  64. package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +89 -0
  65. package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +180 -0
  66. package/src/OntologyManager/OntologyStore/obo-graph-json-schema.ts +110 -0
  67. package/src/OntologyManager/OntologyStore/prefixes.ts +35 -0
  68. package/src/OntologyManager/index.ts +173 -0
  69. package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +19 -0
  70. package/src/SixFrameFeatureDisplay/components/index.ts +1 -0
  71. package/src/SixFrameFeatureDisplay/configSchema.ts +21 -0
  72. package/src/SixFrameFeatureDisplay/index.ts +2 -0
  73. package/src/SixFrameFeatureDisplay/stateModel.ts +413 -0
  74. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +88 -0
  75. package/src/TabularEditor/HybridGrid/Feature.tsx +346 -0
  76. package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +34 -0
  77. package/src/TabularEditor/HybridGrid/Highlight.tsx +40 -0
  78. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +138 -0
  79. package/src/TabularEditor/HybridGrid/NumberCell.tsx +77 -0
  80. package/src/TabularEditor/HybridGrid/ToolBar.tsx +59 -0
  81. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +119 -0
  82. package/src/TabularEditor/HybridGrid/index.ts +1 -0
  83. package/src/TabularEditor/TabularEditorPane.tsx +34 -0
  84. package/src/TabularEditor/index.ts +3 -0
  85. package/src/TabularEditor/model.ts +44 -0
  86. package/src/TabularEditor/types.ts +3 -0
  87. package/src/components/AddAssembly.tsx +464 -0
  88. package/src/components/AddChildFeature.tsx +247 -0
  89. package/src/components/AddFeature.tsx +252 -0
  90. package/src/components/CopyFeature.tsx +328 -0
  91. package/src/components/DeleteAssembly.tsx +185 -0
  92. package/src/components/DeleteFeature.tsx +90 -0
  93. package/src/components/Dialog.tsx +47 -0
  94. package/src/components/DownloadGFF3.tsx +213 -0
  95. package/src/components/ImportFeatures.tsx +295 -0
  96. package/src/components/ManageChecks.tsx +280 -0
  97. package/src/components/ManageUsers.tsx +218 -0
  98. package/src/components/ModifyFeatureAttribute.tsx +457 -0
  99. package/src/components/OntologyTermAutocomplete.tsx +240 -0
  100. package/src/components/OntologyTermMultiSelect.tsx +349 -0
  101. package/src/components/OpenLocalFile.tsx +178 -0
  102. package/src/components/ViewChangeLog.tsx +208 -0
  103. package/src/components/ViewCheckResults.tsx +151 -0
  104. package/src/components/index.ts +12 -0
  105. package/src/config.ts +10 -0
  106. package/src/declare.d.ts +3 -0
  107. package/src/extensions/annotationFromPileup.ts +208 -0
  108. package/src/extensions/index.ts +1 -0
  109. package/src/index.ts +394 -0
  110. package/src/makeDisplayComponent.tsx +244 -0
  111. package/src/session/ClientDataStore.ts +282 -0
  112. package/src/session/index.ts +1 -0
  113. package/src/session/session.ts +373 -0
  114. package/src/types.ts +10 -0
  115. package/src/util/index.ts +31 -0
  116. package/src/util/loadAssemblyIntoClient.ts +291 -0
@@ -0,0 +1,422 @@
1
+ import { Theme, alpha } from '@mui/material'
2
+ import { AnnotationFeatureI } from 'apollo-mst'
3
+ import { LocationEndChange, LocationStartChange } from 'apollo-shared'
4
+
5
+ import { LinearApolloDisplay } from '../stateModel'
6
+ import { MousePosition } from '../stateModel/mouseEvents'
7
+ import { CanvasMouseEvent } from '../types'
8
+ import { Glyph } from './Glyph'
9
+
10
+ export class BoxGlyph extends Glyph {
11
+ getRowCount(_feature: AnnotationFeatureI) {
12
+ return 1
13
+ }
14
+
15
+ protected getIsSelectedFeature(
16
+ feature: AnnotationFeatureI,
17
+ selectedFeature: AnnotationFeatureI | undefined,
18
+ ) {
19
+ return Boolean(selectedFeature && feature._id === selectedFeature._id)
20
+ }
21
+
22
+ protected getBackgroundColor(theme: Theme | undefined, selected: boolean) {
23
+ return selected
24
+ ? theme?.palette.text.primary ?? 'black'
25
+ : theme?.palette.background.default ?? 'white'
26
+ }
27
+
28
+ protected getTextColor(theme: Theme | undefined, selected: boolean) {
29
+ return selected
30
+ ? theme?.palette.getContrastText(
31
+ this.getBackgroundColor(theme, selected),
32
+ ) ?? 'white'
33
+ : theme?.palette.text.primary ?? 'black'
34
+ }
35
+
36
+ protected drawBox(
37
+ ctx: CanvasRenderingContext2D,
38
+ x: number,
39
+ y: number,
40
+ width: number,
41
+ height: number,
42
+ color: string,
43
+ ) {
44
+ ctx.fillStyle = color
45
+ ctx.fillRect(x, y, width, height)
46
+ }
47
+
48
+ protected drawBoxOutline(
49
+ ctx: CanvasRenderingContext2D,
50
+ x: number,
51
+ y: number,
52
+ width: number,
53
+ height: number,
54
+ color: string,
55
+ ) {
56
+ this.drawBox(ctx, x, y, width, height, color)
57
+ ctx.clearRect(x + 1, y + 1, width - 2, height - 2)
58
+ }
59
+
60
+ protected drawBoxFill(
61
+ ctx: CanvasRenderingContext2D,
62
+ x: number,
63
+ y: number,
64
+ width: number,
65
+ height: number,
66
+ color: string,
67
+ ) {
68
+ this.drawBox(ctx, x + 1, y + 1, width - 2, height - 2, color)
69
+ }
70
+
71
+ protected drawBoxText(
72
+ ctx: CanvasRenderingContext2D,
73
+ x: number,
74
+ y: number,
75
+ width: number,
76
+ color: string,
77
+ text: string,
78
+ ) {
79
+ ctx.fillStyle = color
80
+ const textStart = Math.max(x + 1, 0)
81
+ const textWidth = x - 1 + width - textStart
82
+ ctx.fillText(text, textStart, y + 11, textWidth)
83
+ }
84
+
85
+ draw(
86
+ stateModel: LinearApolloDisplay,
87
+ ctx: CanvasRenderingContext2D,
88
+ feature: AnnotationFeatureI,
89
+ xOffset: number,
90
+ row: number,
91
+ reversed: boolean,
92
+ ) {
93
+ const { apolloRowHeight: heightPx, lgv, session, theme } = stateModel
94
+ const { bpPerPx } = lgv
95
+ const { apolloSelectedFeature } = session
96
+ const offsetPx = (feature.start - feature.min) / bpPerPx
97
+ const widthPx = feature.length / bpPerPx
98
+ const startPx = reversed ? xOffset - offsetPx - widthPx : xOffset + offsetPx
99
+ const top = row * heightPx
100
+ const isSelected = this.getIsSelectedFeature(feature, apolloSelectedFeature)
101
+ const backgroundColor = this.getBackgroundColor(theme, isSelected)
102
+ const textColor = this.getTextColor(theme, isSelected)
103
+ const groupingColor = isSelected
104
+ ? 'rgba(130,0,0,0.45)'
105
+ : 'rgba(255,0,0,0.25)'
106
+ const featureBox: [number, number, number, number] = [
107
+ startPx,
108
+ top,
109
+ widthPx,
110
+ heightPx,
111
+ ]
112
+ this.drawBoxOutline(ctx, ...featureBox, textColor)
113
+ if (widthPx <= 2) {
114
+ // Don't need to add details if the feature is too small to see them
115
+ return
116
+ }
117
+
118
+ let featureLocations: { start: number; end: number; type: string }[] = [
119
+ feature,
120
+ ]
121
+ if (
122
+ feature.discontinuousLocations &&
123
+ feature.discontinuousLocations.length > 0
124
+ ) {
125
+ featureLocations = feature.discontinuousLocations.map((f) => ({
126
+ start: f.start,
127
+ end: f.end,
128
+ type: feature.type,
129
+ }))
130
+ }
131
+ if (featureLocations.length > 1) {
132
+ this.drawBoxFill(ctx, ...featureBox, groupingColor)
133
+ for (const location of featureLocations) {
134
+ const offsetPx = (location.start - feature.min) / bpPerPx
135
+ const widthPx = (location.end - location.start) / bpPerPx
136
+ const startPx = reversed
137
+ ? xOffset - offsetPx - widthPx
138
+ : xOffset + offsetPx
139
+ this.drawBoxOutline(ctx, startPx, top, widthPx, heightPx, textColor)
140
+ }
141
+ }
142
+
143
+ for (const location of featureLocations) {
144
+ const offsetPx = (location.start - feature.min) / bpPerPx
145
+ const widthPx = (location.end - location.start) / bpPerPx
146
+ const startPx = reversed
147
+ ? xOffset - offsetPx - widthPx
148
+ : xOffset + offsetPx
149
+ this.drawBoxFill(ctx, startPx, top, widthPx, heightPx, backgroundColor)
150
+ this.drawBoxText(ctx, startPx, top, widthPx, textColor, location.type)
151
+ }
152
+ }
153
+
154
+ getFeatureFromLayout(
155
+ feature: AnnotationFeatureI,
156
+ _bp: number,
157
+ _row: number,
158
+ ): AnnotationFeatureI | undefined {
159
+ return feature
160
+ }
161
+
162
+ getRowForFeature(
163
+ _feature: AnnotationFeatureI,
164
+ _childFeature: AnnotationFeatureI,
165
+ ): number | undefined {
166
+ return 0
167
+ }
168
+
169
+ /** @returns undefined if mouse not on the edge of this feature, otherwise 'start' or 'end' depending on which edge */
170
+ isMouseOnFeatureEdge(
171
+ mousePosition: MousePosition,
172
+ feature: AnnotationFeatureI,
173
+ stateModel: LinearApolloDisplay,
174
+ ) {
175
+ if (!mousePosition) {
176
+ return
177
+ }
178
+ const { refName, regionNumber, x } = mousePosition
179
+ const { lgv } = stateModel
180
+ const { bpToPx, offsetPx } = lgv
181
+ const startPxInfo = bpToPx({ refName, coord: feature.start, regionNumber })
182
+ const endPxInfo = bpToPx({ refName, coord: feature.end, regionNumber })
183
+ if (startPxInfo !== undefined && endPxInfo !== undefined) {
184
+ const startPx = startPxInfo.offsetPx - offsetPx
185
+ const endPx = endPxInfo.offsetPx - offsetPx
186
+ if (Math.abs(endPx - startPx) < 8) {
187
+ return
188
+ }
189
+ if (Math.abs(startPx - x) < 4) {
190
+ return 'start'
191
+ }
192
+ if (Math.abs(endPx - x) < 4) {
193
+ return 'end'
194
+ }
195
+ }
196
+ return
197
+ }
198
+
199
+ drawHover(stateModel: LinearApolloDisplay, ctx: CanvasRenderingContext2D) {
200
+ const { apolloHover, apolloRowHeight, displayedRegions, lgv, theme } =
201
+ stateModel
202
+ if (!apolloHover) {
203
+ return
204
+ }
205
+ const { feature, mousePosition } = apolloHover
206
+ if (!feature || !mousePosition) {
207
+ return
208
+ }
209
+ const { bpPerPx, bpToPx, offsetPx } = lgv
210
+ const displayedRegion = displayedRegions[mousePosition.regionNumber]
211
+ const { refName, reversed } = displayedRegion
212
+ const { end, length, start } = feature
213
+ const { regionNumber, y } = mousePosition
214
+ const startPx =
215
+ (bpToPx({ refName, coord: reversed ? end : start, regionNumber })
216
+ ?.offsetPx ?? 0) - offsetPx
217
+ const row = Math.floor(y / apolloRowHeight)
218
+ const top = row * apolloRowHeight
219
+ const widthPx = length / bpPerPx
220
+ ctx.fillStyle = theme?.palette.action.focus ?? 'rgba(0,0,0,0.04)'
221
+ ctx.fillRect(startPx, top, widthPx, apolloRowHeight)
222
+ }
223
+
224
+ drawDragPreview(
225
+ stateModel: LinearApolloDisplay,
226
+ overlayCtx: CanvasRenderingContext2D,
227
+ ) {
228
+ const { apolloDragging, apolloRowHeight, displayedRegions, lgv, theme } =
229
+ stateModel
230
+ const { bpPerPx, offsetPx } = lgv
231
+ if (!apolloDragging) {
232
+ return
233
+ }
234
+ const {
235
+ feature,
236
+ glyph,
237
+ mousePosition: startingMousePosition,
238
+ } = apolloDragging.start
239
+ if (!feature) {
240
+ throw new Error('no feature for drag preview??')
241
+ }
242
+ if (glyph !== this) {
243
+ throw new Error('drawDragPreview() called on wrong glyph?')
244
+ }
245
+ const { mousePosition: currentMousePosition } = apolloDragging.current
246
+ const edge = this.isMouseOnFeatureEdge(
247
+ startingMousePosition,
248
+ feature,
249
+ stateModel,
250
+ )
251
+ if (!edge) {
252
+ return
253
+ }
254
+
255
+ const row = Math.floor(startingMousePosition.y / apolloRowHeight)
256
+ const region = displayedRegions[startingMousePosition.regionNumber]
257
+ const rowCount = this.getRowCount(feature)
258
+
259
+ const featureEdgeBp = region.reversed
260
+ ? region.end - feature[edge]
261
+ : feature[edge] - region.start
262
+ const featureEdgePx = featureEdgeBp / bpPerPx - offsetPx
263
+
264
+ const rectX = Math.min(currentMousePosition.x, featureEdgePx)
265
+ const rectY = row * apolloRowHeight
266
+ const rectWidth = Math.abs(currentMousePosition.x - featureEdgePx)
267
+ const rectHeight = apolloRowHeight * rowCount
268
+
269
+ overlayCtx.strokeStyle = theme?.palette.info.main ?? 'rgb(255,0,0)'
270
+ overlayCtx.setLineDash([6])
271
+ overlayCtx.strokeRect(rectX, rectY, rectWidth, rectHeight)
272
+ overlayCtx.fillStyle = alpha(
273
+ theme?.palette.info.main ?? 'rgb(255,0,0)',
274
+ 0.2,
275
+ )
276
+ overlayCtx.fillRect(rectX, rectY, rectWidth, rectHeight)
277
+ }
278
+
279
+ onMouseMove(stateModel: LinearApolloDisplay, event: CanvasMouseEvent) {
280
+ const { feature, mousePosition } =
281
+ stateModel.getFeatureAndGlyphUnderMouse(event)
282
+ if (stateModel.apolloDragging) {
283
+ stateModel.setCursor('col-resize')
284
+ return
285
+ }
286
+ if (feature && mousePosition) {
287
+ const edge = this.isMouseOnFeatureEdge(mousePosition, feature, stateModel)
288
+ if (edge) {
289
+ stateModel.setCursor('col-resize')
290
+ } else {
291
+ stateModel.setCursor()
292
+ }
293
+ }
294
+ }
295
+
296
+ onMouseDown(stateModel: LinearApolloDisplay, event: CanvasMouseEvent) {
297
+ // swallow the mouseDown if we are on the edge of the feature
298
+ const { feature, mousePosition } =
299
+ stateModel.getFeatureAndGlyphUnderMouse(event)
300
+ if (feature && mousePosition) {
301
+ const edge = this.isMouseOnFeatureEdge(mousePosition, feature, stateModel)
302
+ if (edge) {
303
+ event.stopPropagation()
304
+ }
305
+ }
306
+ }
307
+
308
+ onMouseUp(stateModel: LinearApolloDisplay, event: CanvasMouseEvent) {
309
+ if (stateModel.apolloDragging ?? event.button !== 0) {
310
+ return
311
+ }
312
+ const { feature } = stateModel.getFeatureAndGlyphUnderMouse(event)
313
+ if (feature) {
314
+ stateModel.setSelectedFeature(feature)
315
+ }
316
+ }
317
+
318
+ startDrag(stateModel: LinearApolloDisplay): boolean {
319
+ // only accept the drag if we are on the edge of the feature
320
+ const { feature, mousePosition } = stateModel.apolloDragging?.start ?? {}
321
+ if (feature && mousePosition) {
322
+ const edge = this.isMouseOnFeatureEdge(mousePosition, feature, stateModel)
323
+ if (edge) {
324
+ return true
325
+ }
326
+ }
327
+ return false
328
+ }
329
+
330
+ continueDrag(
331
+ stateModel: LinearApolloDisplay,
332
+ currentMousePosition: MousePosition,
333
+ ) {
334
+ const { feature, glyph, mousePosition, topLevelFeature } =
335
+ stateModel.apolloDragging?.start ?? {}
336
+ if (!(currentMousePosition && mousePosition)) {
337
+ return
338
+ }
339
+ stateModel.setDragging({
340
+ start: {
341
+ feature,
342
+ topLevelFeature,
343
+ glyph,
344
+ mousePosition,
345
+ },
346
+ current: {
347
+ feature,
348
+ topLevelFeature,
349
+ glyph,
350
+ mousePosition: currentMousePosition,
351
+ },
352
+ })
353
+ }
354
+
355
+ executeDrag(stateModel: LinearApolloDisplay) {
356
+ const {
357
+ apolloDragging,
358
+ changeManager,
359
+ displayedRegions,
360
+ getAssemblyId,
361
+ setCursor,
362
+ } = stateModel
363
+ if (!apolloDragging) {
364
+ return
365
+ }
366
+ const {
367
+ feature,
368
+ glyph,
369
+ mousePosition: startingMousePosition,
370
+ } = apolloDragging.start
371
+ if (!feature) {
372
+ throw new Error('no feature for drag preview??')
373
+ }
374
+ if (glyph !== this) {
375
+ throw new Error('drawDragPreview() called on wrong glyph?')
376
+ }
377
+ const edge = this.isMouseOnFeatureEdge(
378
+ startingMousePosition,
379
+ feature,
380
+ stateModel,
381
+ )
382
+ if (!edge) {
383
+ return
384
+ }
385
+
386
+ const { mousePosition: currentMousePosition } = apolloDragging.current
387
+ const region = displayedRegions[startingMousePosition.regionNumber]
388
+ const newBp = currentMousePosition.bp
389
+ const assembly = getAssemblyId(region.assemblyName)
390
+ let change: LocationEndChange | LocationStartChange
391
+ if (edge === 'end') {
392
+ const featureId = feature._id
393
+ const oldEnd = feature.end
394
+ const newEnd = newBp
395
+ change = new LocationEndChange({
396
+ typeName: 'LocationEndChange',
397
+ changedIds: [featureId],
398
+ featureId,
399
+ oldEnd,
400
+ newEnd,
401
+ assembly,
402
+ })
403
+ } else {
404
+ const featureId = feature._id
405
+ const oldStart = feature.start
406
+ const newStart = newBp
407
+ change = new LocationStartChange({
408
+ typeName: 'LocationStartChange',
409
+ changedIds: [featureId],
410
+ featureId,
411
+ oldStart,
412
+ newStart,
413
+ assembly,
414
+ })
415
+ }
416
+ if (!changeManager) {
417
+ throw new Error('no change manager')
418
+ }
419
+ void changeManager.submit(change)
420
+ setCursor()
421
+ }
422
+ }