react-msaview 3.0.0 → 3.0.1
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.
- package/bundle/index.js +31 -31
- package/dist/components/Header.js +4 -9
- package/dist/components/Header.js.map +1 -1
- package/dist/components/HeaderInfoArea.d.ts +6 -0
- package/dist/components/HeaderInfoArea.js +12 -0
- package/dist/components/HeaderInfoArea.js.map +1 -0
- package/dist/components/MSAPanel/Loading.d.ts +2 -0
- package/dist/components/MSAPanel/Loading.js +12 -0
- package/dist/components/MSAPanel/Loading.js.map +1 -0
- package/dist/components/MSAPanel/MSACanvas.js +1 -10
- package/dist/components/MSAPanel/MSACanvas.js.map +1 -1
- package/dist/components/MSAPanel/MSAMouseoverCanvas.js +1 -22
- package/dist/components/MSAPanel/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/MSAPanel/renderMSABlock.js +0 -1
- package/dist/components/MSAPanel/renderMSABlock.js.map +1 -1
- package/dist/components/MSAPanel/renderMSAMouseover.d.ts +5 -0
- package/dist/components/MSAPanel/renderMSAMouseover.js +24 -0
- package/dist/components/MSAPanel/renderMSAMouseover.js.map +1 -0
- package/dist/components/Minimap.js +13 -13
- package/dist/components/Minimap.js.map +1 -1
- package/dist/components/TreePanel/TreeCanvasBlock.js +8 -4
- package/dist/components/TreePanel/TreeCanvasBlock.js.map +1 -1
- package/dist/components/TreePanel/{TreeMenu.d.ts → TreeNodeMenu.d.ts} +1 -0
- package/dist/components/TreePanel/{TreeMenu.js → TreeNodeMenu.js} +8 -4
- package/dist/components/TreePanel/TreeNodeMenu.js.map +1 -0
- package/dist/components/TreePanel/dialogs/{TreeNodeInfoDlg.js → TreeNodeInfoDialog.js} +2 -2
- package/dist/components/TreePanel/dialogs/TreeNodeInfoDialog.js.map +1 -0
- package/dist/components/TreePanel/renderTreeCanvas.d.ts +8 -3
- package/dist/components/TreePanel/renderTreeCanvas.js +8 -7
- package/dist/components/TreePanel/renderTreeCanvas.js.map +1 -1
- package/dist/components/dialogs/SettingsDialog.js +31 -22
- package/dist/components/dialogs/SettingsDialog.js.map +1 -1
- package/dist/model.d.ts +120 -62
- package/dist/model.js +176 -124
- package/dist/model.js.map +1 -1
- package/dist/parsers/ClustalMSA.d.ts +1 -1
- package/dist/parsers/ClustalMSA.js +1 -1
- package/dist/parsers/ClustalMSA.js.map +1 -1
- package/dist/parsers/FastaMSA.d.ts +1 -1
- package/dist/parsers/FastaMSA.js +2 -2
- package/dist/parsers/FastaMSA.js.map +1 -1
- package/dist/parsers/StockholmMSA.d.ts +1 -1
- package/dist/parsers/StockholmMSA.js +2 -2
- package/dist/parsers/StockholmMSA.js.map +1 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.js +15 -7
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/components/Header.tsx +5 -11
- package/src/components/HeaderInfoArea.tsx +21 -0
- package/src/components/MSAPanel/Loading.tsx +16 -0
- package/src/components/MSAPanel/MSACanvas.tsx +1 -16
- package/src/components/MSAPanel/MSAMouseoverCanvas.tsx +2 -50
- package/src/components/MSAPanel/renderMSABlock.ts +0 -2
- package/src/components/MSAPanel/renderMSAMouseover.ts +51 -0
- package/src/components/Minimap.tsx +15 -15
- package/src/components/TreePanel/TreeCanvasBlock.tsx +8 -3
- package/src/components/TreePanel/{TreeMenu.tsx → TreeNodeMenu.tsx} +12 -3
- package/src/components/TreePanel/dialogs/{TreeNodeInfoDlg.tsx → TreeNodeInfoDialog.tsx} +1 -1
- package/src/components/TreePanel/renderTreeCanvas.ts +13 -4
- package/src/components/dialogs/SettingsDialog.tsx +61 -44
- package/src/model.ts +279 -154
- package/src/parsers/ClustalMSA.ts +1 -1
- package/src/parsers/FastaMSA.ts +1 -1
- package/src/parsers/StockholmMSA.ts +1 -1
- package/src/util.ts +19 -6
- package/src/version.ts +1 -1
- package/dist/components/OverviewRubberband.d.ts +0 -8
- package/dist/components/OverviewRubberband.js +0 -185
- package/dist/components/OverviewRubberband.js.map +0 -1
- package/dist/components/Rubberband.d.ts +0 -8
- package/dist/components/Rubberband.js +0 -185
- package/dist/components/Rubberband.js.map +0 -1
- package/dist/components/TreePanel/TreeMenu.js.map +0 -1
- package/dist/components/TreePanel/dialogs/TreeNodeInfoDlg.js.map +0 -1
- package/dist/components/dialogs/AnnotationDialog.d.ts +0 -11
- package/dist/components/dialogs/AnnotationDialog.js +0 -65
- package/dist/components/dialogs/AnnotationDialog.js.map +0 -1
- package/src/components/OverviewRubberband.tsx +0 -283
- package/src/components/Rubberband.tsx +0 -283
- package/src/components/dialogs/AnnotationDialog.tsx +0 -144
- /package/dist/components/TreePanel/dialogs/{TreeNodeInfoDlg.d.ts → TreeNodeInfoDialog.d.ts} +0 -0
package/src/model.ts
CHANGED
|
@@ -3,27 +3,23 @@ import { autorun } from 'mobx'
|
|
|
3
3
|
import { Instance, cast, types, addDisposer, SnapshotIn } from 'mobx-state-tree'
|
|
4
4
|
import { hierarchy, cluster, HierarchyNode } from 'd3-hierarchy'
|
|
5
5
|
import { ascending } from 'd3-array'
|
|
6
|
+
import Stockholm from 'stockholm-js'
|
|
7
|
+
// jbrowse
|
|
6
8
|
import { FileLocation, ElementId } from '@jbrowse/core/util/types/mst'
|
|
7
9
|
import { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
|
|
8
10
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
9
|
-
import { measureText, sum } from '@jbrowse/core/util'
|
|
11
|
+
import { measureText, notEmpty, sum } from '@jbrowse/core/util'
|
|
10
12
|
import BaseViewModel from '@jbrowse/core/pluggableElementTypes/models/BaseViewModel'
|
|
11
|
-
import Stockholm from 'stockholm-js'
|
|
12
|
-
|
|
13
|
-
export interface RowDetails {
|
|
14
|
-
[key: string]: unknown
|
|
15
|
-
name: string
|
|
16
|
-
range?: { start: number; end: number }
|
|
17
|
-
}
|
|
18
13
|
|
|
19
14
|
// locals
|
|
20
15
|
import {
|
|
16
|
+
clamp,
|
|
21
17
|
collapse,
|
|
18
|
+
filterHiddenLeafNodes,
|
|
22
19
|
generateNodeIds,
|
|
23
20
|
maxLength,
|
|
24
21
|
setBrLength,
|
|
25
22
|
skipBlanks,
|
|
26
|
-
clamp,
|
|
27
23
|
NodeWithIds,
|
|
28
24
|
NodeWithIdsAndLength,
|
|
29
25
|
} from './util'
|
|
@@ -38,12 +34,23 @@ import { UniprotTrack } from './UniprotTrack'
|
|
|
38
34
|
import { StructureModel } from './StructureModel'
|
|
39
35
|
import { DialogQueueSessionMixin } from './DialogQueue'
|
|
40
36
|
|
|
37
|
+
export interface RowDetails {
|
|
38
|
+
[key: string]: unknown
|
|
39
|
+
name: string
|
|
40
|
+
range?: { start: number; end: number }
|
|
41
|
+
}
|
|
42
|
+
|
|
41
43
|
interface BasicTrackModel {
|
|
42
44
|
id: string
|
|
43
45
|
name: string
|
|
44
46
|
associatedRowName?: string
|
|
45
47
|
height: number
|
|
46
48
|
}
|
|
49
|
+
interface Structure {
|
|
50
|
+
pdb: string
|
|
51
|
+
startPos: number
|
|
52
|
+
endPos: number
|
|
53
|
+
}
|
|
47
54
|
|
|
48
55
|
export interface TextTrackModel extends BasicTrackModel {
|
|
49
56
|
customColorScheme?: Record<string, string>
|
|
@@ -73,6 +80,9 @@ type StructureSnap = SnapshotIn<typeof StructureModel>
|
|
|
73
80
|
|
|
74
81
|
/**
|
|
75
82
|
* #stateModel MsaView
|
|
83
|
+
* extends
|
|
84
|
+
* - BaseViewModel
|
|
85
|
+
* - DialogQueueSessionMixin
|
|
76
86
|
*/
|
|
77
87
|
function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
78
88
|
|
|
@@ -89,136 +99,174 @@ const model = types
|
|
|
89
99
|
types.model('MsaView', {
|
|
90
100
|
/**
|
|
91
101
|
* #property
|
|
102
|
+
* id of view, randomly generated if not provided
|
|
92
103
|
*/
|
|
93
104
|
id: ElementId,
|
|
105
|
+
|
|
94
106
|
/**
|
|
95
107
|
* #property
|
|
108
|
+
* hardcoded view type
|
|
96
109
|
*/
|
|
97
110
|
type: types.literal('MsaView'),
|
|
111
|
+
|
|
98
112
|
/**
|
|
99
113
|
* #property
|
|
114
|
+
* height of the div containing the view, px
|
|
100
115
|
*/
|
|
101
116
|
height: types.optional(types.number, 550),
|
|
117
|
+
|
|
102
118
|
/**
|
|
103
119
|
* #property
|
|
120
|
+
* width of the area the tree is drawn in, px
|
|
104
121
|
*/
|
|
105
122
|
treeAreaWidth: types.optional(types.number, 400),
|
|
123
|
+
|
|
106
124
|
/**
|
|
107
125
|
* #property
|
|
126
|
+
* width of the tree within the treeArea, px
|
|
108
127
|
*/
|
|
109
128
|
treeWidth: types.optional(types.number, 300),
|
|
129
|
+
|
|
110
130
|
/**
|
|
111
131
|
* #getter
|
|
132
|
+
* synchronization that matches treeWidth to treeAreaWidth
|
|
112
133
|
*/
|
|
113
134
|
treeWidthMatchesArea: true,
|
|
135
|
+
|
|
114
136
|
/**
|
|
115
137
|
* #property
|
|
138
|
+
* height of each row, px
|
|
116
139
|
*/
|
|
117
140
|
rowHeight: 20,
|
|
141
|
+
|
|
118
142
|
/**
|
|
119
143
|
* #property
|
|
144
|
+
* scroll position, Y-offset, px
|
|
120
145
|
*/
|
|
121
146
|
scrollY: 0,
|
|
147
|
+
|
|
122
148
|
/**
|
|
123
149
|
* #property
|
|
150
|
+
* scroll position, X-offset, px
|
|
124
151
|
*/
|
|
125
152
|
scrollX: 0,
|
|
153
|
+
|
|
126
154
|
/**
|
|
127
155
|
* #property
|
|
128
|
-
|
|
129
|
-
resizeHandleWidth: 5,
|
|
130
|
-
/**
|
|
131
|
-
* #property
|
|
132
|
-
*/
|
|
133
|
-
blockSize: 1000,
|
|
134
|
-
/**
|
|
135
|
-
* #property
|
|
136
|
-
*/
|
|
137
|
-
mouseRow: types.maybe(types.number),
|
|
138
|
-
/**
|
|
139
|
-
* #property
|
|
140
|
-
*/
|
|
141
|
-
mouseCol: types.maybe(types.number),
|
|
142
|
-
/**
|
|
143
|
-
* #property
|
|
156
|
+
* currently "selected" structures, generally PDB 3-D protein structures
|
|
144
157
|
*/
|
|
145
158
|
selectedStructures: types.array(StructureModel),
|
|
159
|
+
|
|
146
160
|
/**
|
|
147
161
|
* #property
|
|
162
|
+
* right-align the labels
|
|
148
163
|
*/
|
|
149
164
|
labelsAlignRight: false,
|
|
165
|
+
|
|
150
166
|
/**
|
|
151
167
|
* #property
|
|
168
|
+
* width of columns, px
|
|
152
169
|
*/
|
|
153
170
|
colWidth: 16,
|
|
171
|
+
|
|
154
172
|
/**
|
|
155
173
|
* #property
|
|
174
|
+
* use "branch length" e.g. evolutionary distance to draw tree branch
|
|
175
|
+
* lengths. if false, the layout is a "cladogram" that does not take into
|
|
176
|
+
* account evolutionary distances
|
|
156
177
|
*/
|
|
157
178
|
showBranchLen: true,
|
|
158
179
|
/**
|
|
159
180
|
* #property
|
|
181
|
+
* draw MSA tiles with a background color
|
|
160
182
|
*/
|
|
161
183
|
bgColor: true,
|
|
184
|
+
|
|
162
185
|
/**
|
|
163
186
|
* #property
|
|
187
|
+
* draw tree, boolean
|
|
164
188
|
*/
|
|
165
189
|
drawTree: true,
|
|
190
|
+
|
|
166
191
|
/**
|
|
167
192
|
* #property
|
|
193
|
+
* draw clickable node bubbles on the tree
|
|
168
194
|
*/
|
|
169
195
|
drawNodeBubbles: true,
|
|
196
|
+
|
|
170
197
|
/**
|
|
171
198
|
* #property
|
|
199
|
+
* high resolution scale factor, helps make canvas look better on hi-dpi
|
|
200
|
+
* screens
|
|
172
201
|
*/
|
|
173
202
|
highResScaleFactor: 2,
|
|
203
|
+
|
|
174
204
|
/**
|
|
175
205
|
* #property
|
|
206
|
+
* default color scheme name
|
|
176
207
|
*/
|
|
177
208
|
colorSchemeName: 'maeditor',
|
|
209
|
+
|
|
178
210
|
/**
|
|
179
211
|
* #property
|
|
212
|
+
* filehandle object for the tree
|
|
180
213
|
*/
|
|
181
214
|
treeFilehandle: types.maybe(FileLocation),
|
|
215
|
+
|
|
182
216
|
/**
|
|
183
217
|
* #property
|
|
218
|
+
* filehandle object for the MSA (which could contain a tree e.g. with
|
|
219
|
+
* stockholm files)
|
|
184
220
|
*/
|
|
185
221
|
msaFilehandle: types.maybe(FileLocation),
|
|
222
|
+
|
|
186
223
|
/**
|
|
187
224
|
* #property
|
|
225
|
+
* filehandle object for tree metadata
|
|
188
226
|
*/
|
|
189
227
|
treeMetadataFilehandle: types.maybe(FileLocation),
|
|
228
|
+
|
|
190
229
|
/**
|
|
191
230
|
* #property
|
|
231
|
+
*
|
|
192
232
|
*/
|
|
193
233
|
currentAlignment: 0,
|
|
234
|
+
|
|
194
235
|
/**
|
|
195
236
|
* #property
|
|
237
|
+
* array of tree nodes that are 'collapsed'
|
|
196
238
|
*/
|
|
197
239
|
collapsed: types.array(types.string),
|
|
240
|
+
|
|
198
241
|
/**
|
|
199
242
|
* #property
|
|
243
|
+
* array of leaf nodes that are 'hidden', similar to collapsed but for leaf nodes
|
|
200
244
|
*/
|
|
201
|
-
|
|
245
|
+
hidden: types.array(types.string),
|
|
246
|
+
|
|
202
247
|
/**
|
|
203
248
|
* #property
|
|
249
|
+
* focus on particular subtree
|
|
204
250
|
*/
|
|
205
|
-
|
|
251
|
+
showOnly: types.maybe(types.string),
|
|
252
|
+
|
|
206
253
|
/**
|
|
207
254
|
* #property
|
|
255
|
+
* a list of "tracks" to display, as box-like glyphs (e.g. protein
|
|
256
|
+
* domains)
|
|
208
257
|
*/
|
|
209
|
-
|
|
258
|
+
boxTracks: types.array(UniprotTrack),
|
|
259
|
+
|
|
210
260
|
/**
|
|
211
261
|
* #property
|
|
262
|
+
* turned off tracks
|
|
212
263
|
*/
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
start: types.number,
|
|
216
|
-
end: types.number,
|
|
217
|
-
attributes: types.frozen<Record<string, string[]>>(),
|
|
218
|
-
}),
|
|
219
|
-
),
|
|
264
|
+
turnedOffTracks: types.map(types.boolean),
|
|
265
|
+
|
|
220
266
|
/**
|
|
221
267
|
* #property
|
|
268
|
+
* data from the loaded tree/msa/treeMetadata, generally loaded by
|
|
269
|
+
* autorun
|
|
222
270
|
*/
|
|
223
271
|
data: types.optional(
|
|
224
272
|
types
|
|
@@ -243,20 +291,47 @@ const model = types
|
|
|
243
291
|
}),
|
|
244
292
|
)
|
|
245
293
|
.volatile(() => ({
|
|
294
|
+
/**
|
|
295
|
+
* #volatile
|
|
296
|
+
* resize handle width between tree and msa area, px
|
|
297
|
+
*/
|
|
298
|
+
resizeHandleWidth: 5,
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* #volatile
|
|
302
|
+
* size of blocks of content to be drawn, px
|
|
303
|
+
*/
|
|
304
|
+
blockSize: 1000,
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* #volatile
|
|
308
|
+
* the currently mouse-hovered row
|
|
309
|
+
*/
|
|
310
|
+
mouseRow: undefined as number | undefined,
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* #volatile
|
|
314
|
+
* the currently mouse-hovered column
|
|
315
|
+
*/
|
|
316
|
+
mouseCol: undefined as number | undefined,
|
|
317
|
+
|
|
246
318
|
/**
|
|
247
319
|
* #volatile
|
|
248
320
|
* a dummy variable that is incremented when ref changes so autorun for
|
|
249
321
|
* drawing canvas commands will run
|
|
250
322
|
*/
|
|
251
323
|
nref: 0,
|
|
324
|
+
|
|
252
325
|
/**
|
|
253
326
|
* #volatile
|
|
254
327
|
*/
|
|
255
328
|
minimapHeight: 56,
|
|
329
|
+
|
|
256
330
|
/**
|
|
257
331
|
* #volatile
|
|
258
332
|
*/
|
|
259
333
|
error: undefined as unknown,
|
|
334
|
+
|
|
260
335
|
/**
|
|
261
336
|
* #volatile
|
|
262
337
|
*/
|
|
@@ -264,6 +339,7 @@ const model = types
|
|
|
264
339
|
left: 20,
|
|
265
340
|
top: 20,
|
|
266
341
|
},
|
|
342
|
+
|
|
267
343
|
/**
|
|
268
344
|
* #volatile
|
|
269
345
|
*/
|
|
@@ -272,18 +348,23 @@ const model = types
|
|
|
272
348
|
.actions(self => ({
|
|
273
349
|
/**
|
|
274
350
|
* #action
|
|
351
|
+
* set the height of the view in px
|
|
275
352
|
*/
|
|
276
353
|
setHeight(height: number) {
|
|
277
354
|
self.height = height
|
|
278
355
|
},
|
|
356
|
+
|
|
279
357
|
/**
|
|
280
358
|
* #action
|
|
359
|
+
* add to the selected structures
|
|
281
360
|
*/
|
|
282
361
|
addStructureToSelection(elt: StructureSnap) {
|
|
283
362
|
self.selectedStructures.push(elt)
|
|
284
363
|
},
|
|
364
|
+
|
|
285
365
|
/**
|
|
286
366
|
* #action
|
|
367
|
+
* remove from the selected structures
|
|
287
368
|
*/
|
|
288
369
|
removeStructureFromSelection(elt: StructureSnap) {
|
|
289
370
|
const r = self.selectedStructures.find(node => node.id === elt.id)
|
|
@@ -291,8 +372,10 @@ const model = types
|
|
|
291
372
|
self.selectedStructures.remove(r)
|
|
292
373
|
}
|
|
293
374
|
},
|
|
375
|
+
|
|
294
376
|
/**
|
|
295
377
|
* #action
|
|
378
|
+
* toggle a structure from the selected structures list
|
|
296
379
|
*/
|
|
297
380
|
toggleStructureSelection(elt: {
|
|
298
381
|
id: string
|
|
@@ -305,51 +388,67 @@ const model = types
|
|
|
305
388
|
self.selectedStructures.push(elt)
|
|
306
389
|
}
|
|
307
390
|
},
|
|
391
|
+
|
|
308
392
|
/**
|
|
309
393
|
* #action
|
|
394
|
+
* clear all selected structures
|
|
310
395
|
*/
|
|
311
396
|
clearSelectedStructures() {
|
|
312
397
|
self.selectedStructures = cast([])
|
|
313
398
|
},
|
|
399
|
+
|
|
314
400
|
/**
|
|
315
401
|
* #action
|
|
402
|
+
* set error state
|
|
316
403
|
*/
|
|
317
404
|
setError(error?: unknown) {
|
|
318
405
|
self.error = error
|
|
319
406
|
},
|
|
407
|
+
|
|
320
408
|
/**
|
|
321
409
|
* #action
|
|
410
|
+
* set mouse position (row, column) in the MSA
|
|
322
411
|
*/
|
|
323
412
|
setMousePos(col?: number, row?: number) {
|
|
324
413
|
self.mouseCol = col
|
|
325
414
|
self.mouseRow = row
|
|
326
415
|
},
|
|
416
|
+
|
|
327
417
|
/**
|
|
328
418
|
* #action
|
|
419
|
+
* set row height (px)
|
|
329
420
|
*/
|
|
330
421
|
setRowHeight(n: number) {
|
|
331
422
|
self.rowHeight = n
|
|
332
423
|
},
|
|
424
|
+
|
|
333
425
|
/**
|
|
334
426
|
* #action
|
|
427
|
+
* set col width (px)
|
|
335
428
|
*/
|
|
336
429
|
setColWidth(n: number) {
|
|
337
430
|
self.colWidth = n
|
|
338
431
|
},
|
|
432
|
+
|
|
339
433
|
/**
|
|
340
434
|
* #action
|
|
435
|
+
* set color scheme name
|
|
341
436
|
*/
|
|
342
437
|
setColorSchemeName(name: string) {
|
|
343
438
|
self.colorSchemeName = name
|
|
344
439
|
},
|
|
440
|
+
|
|
345
441
|
/**
|
|
346
442
|
* #action
|
|
443
|
+
* synchronize the treewidth and treeareawidth
|
|
347
444
|
*/
|
|
348
445
|
setTreeWidthMatchesArea(arg: boolean) {
|
|
349
446
|
self.treeWidthMatchesArea = arg
|
|
350
447
|
},
|
|
448
|
+
|
|
351
449
|
/**
|
|
352
450
|
* #action
|
|
451
|
+
* set scroll Y-offset (px)
|
|
353
452
|
*/
|
|
354
453
|
setScrollY(n: number) {
|
|
355
454
|
self.scrollY = n
|
|
@@ -357,22 +456,27 @@ const model = types
|
|
|
357
456
|
|
|
358
457
|
/**
|
|
359
458
|
* #action
|
|
459
|
+
* set tree area width (px)
|
|
360
460
|
*/
|
|
361
461
|
setTreeAreaWidth(n: number) {
|
|
362
462
|
self.treeAreaWidth = n
|
|
363
463
|
},
|
|
364
464
|
/**
|
|
365
465
|
* #action
|
|
466
|
+
* set tree width (px)
|
|
366
467
|
*/
|
|
367
468
|
setTreeWidth(n: number) {
|
|
368
469
|
self.treeWidth = n
|
|
369
470
|
},
|
|
471
|
+
|
|
370
472
|
/**
|
|
371
473
|
* #action
|
|
474
|
+
*
|
|
372
475
|
*/
|
|
373
476
|
setCurrentAlignment(n: number) {
|
|
374
477
|
self.currentAlignment = n
|
|
375
478
|
},
|
|
479
|
+
|
|
376
480
|
/**
|
|
377
481
|
* #action
|
|
378
482
|
*/
|
|
@@ -385,6 +489,21 @@ const model = types
|
|
|
385
489
|
setDrawTree(arg: boolean) {
|
|
386
490
|
self.drawTree = arg
|
|
387
491
|
},
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* #action
|
|
495
|
+
*/
|
|
496
|
+
hideNode(arg: string) {
|
|
497
|
+
self.hidden.push(arg)
|
|
498
|
+
},
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* #action
|
|
502
|
+
*/
|
|
503
|
+
clearHidden() {
|
|
504
|
+
self.hidden.clear()
|
|
505
|
+
},
|
|
506
|
+
|
|
388
507
|
/**
|
|
389
508
|
* #action
|
|
390
509
|
*/
|
|
@@ -395,42 +514,49 @@ const model = types
|
|
|
395
514
|
self.collapsed.push(node)
|
|
396
515
|
}
|
|
397
516
|
},
|
|
517
|
+
|
|
398
518
|
/**
|
|
399
519
|
* #action
|
|
400
520
|
*/
|
|
401
521
|
setShowOnly(node?: string) {
|
|
402
522
|
self.showOnly = node
|
|
403
523
|
},
|
|
524
|
+
|
|
404
525
|
/**
|
|
405
526
|
* #action
|
|
406
527
|
*/
|
|
407
528
|
setShowBranchLen(arg: boolean) {
|
|
408
529
|
self.showBranchLen = arg
|
|
409
530
|
},
|
|
531
|
+
|
|
410
532
|
/**
|
|
411
533
|
* #action
|
|
412
534
|
*/
|
|
413
535
|
setBgColor(arg: boolean) {
|
|
414
536
|
self.bgColor = arg
|
|
415
537
|
},
|
|
538
|
+
|
|
416
539
|
/**
|
|
417
540
|
* #action
|
|
418
541
|
*/
|
|
419
542
|
setDrawNodeBubbles(arg: boolean) {
|
|
420
543
|
self.drawNodeBubbles = arg
|
|
421
544
|
},
|
|
545
|
+
|
|
422
546
|
/**
|
|
423
547
|
* #action
|
|
424
548
|
*/
|
|
425
549
|
setData(data: { msa?: string; tree?: string }) {
|
|
426
550
|
self.data = cast(data)
|
|
427
551
|
},
|
|
552
|
+
|
|
428
553
|
/**
|
|
429
554
|
* #action
|
|
430
555
|
*/
|
|
431
556
|
async setMSAFilehandle(msaFilehandle?: FileLocationType) {
|
|
432
557
|
self.msaFilehandle = msaFilehandle
|
|
433
558
|
},
|
|
559
|
+
|
|
434
560
|
/**
|
|
435
561
|
* #action
|
|
436
562
|
*/
|
|
@@ -442,18 +568,21 @@ const model = types
|
|
|
442
568
|
self.treeFilehandle = treeFilehandle
|
|
443
569
|
}
|
|
444
570
|
},
|
|
571
|
+
|
|
445
572
|
/**
|
|
446
573
|
* #action
|
|
447
574
|
*/
|
|
448
575
|
setMSA(result: string) {
|
|
449
576
|
self.data.setMSA(result)
|
|
450
577
|
},
|
|
578
|
+
|
|
451
579
|
/**
|
|
452
580
|
* #action
|
|
453
581
|
*/
|
|
454
582
|
setTree(result: string) {
|
|
455
583
|
self.data.setTree(result)
|
|
456
584
|
},
|
|
585
|
+
|
|
457
586
|
/**
|
|
458
587
|
* #action
|
|
459
588
|
*/
|
|
@@ -533,12 +662,14 @@ const model = types
|
|
|
533
662
|
get blocks2d() {
|
|
534
663
|
return self.blocksY.flatMap(by => self.blocksX.map(bx => [bx, by]))
|
|
535
664
|
},
|
|
665
|
+
|
|
536
666
|
/**
|
|
537
667
|
* #getter
|
|
538
668
|
*/
|
|
539
669
|
get done() {
|
|
540
670
|
return self.initialized && (self.data.msa || self.data.tree)
|
|
541
671
|
},
|
|
672
|
+
|
|
542
673
|
/**
|
|
543
674
|
* #getter
|
|
544
675
|
*/
|
|
@@ -552,6 +683,7 @@ const model = types
|
|
|
552
683
|
get header() {
|
|
553
684
|
return this.MSA?.getHeader() || {}
|
|
554
685
|
},
|
|
686
|
+
|
|
555
687
|
/**
|
|
556
688
|
* #method
|
|
557
689
|
*/
|
|
@@ -559,7 +691,12 @@ const model = types
|
|
|
559
691
|
const matches = name.match(/\S+\/(\d+)-(\d+)/)
|
|
560
692
|
return {
|
|
561
693
|
data: this.MSA?.getRowData(name) || ({} as Record<string, unknown>),
|
|
562
|
-
...(matches && {
|
|
694
|
+
...(matches && {
|
|
695
|
+
range: {
|
|
696
|
+
start: +matches[1],
|
|
697
|
+
end: +matches[2],
|
|
698
|
+
},
|
|
699
|
+
}),
|
|
563
700
|
}
|
|
564
701
|
},
|
|
565
702
|
/**
|
|
@@ -578,7 +715,7 @@ const model = types
|
|
|
578
715
|
* #getter
|
|
579
716
|
*/
|
|
580
717
|
get noTree() {
|
|
581
|
-
return !!this.
|
|
718
|
+
return !!this._tree.noTree
|
|
582
719
|
},
|
|
583
720
|
/**
|
|
584
721
|
* #getter
|
|
@@ -617,7 +754,7 @@ const model = types
|
|
|
617
754
|
/**
|
|
618
755
|
* #getter
|
|
619
756
|
*/
|
|
620
|
-
get
|
|
757
|
+
get _tree(): NodeWithIds {
|
|
621
758
|
return self.data.tree
|
|
622
759
|
? generateNodeIds(parseNewick(self.data.tree))
|
|
623
760
|
: this.MSA?.getTree() || {
|
|
@@ -635,7 +772,8 @@ const model = types
|
|
|
635
772
|
},
|
|
636
773
|
/**
|
|
637
774
|
* #getter
|
|
638
|
-
*/
|
|
775
|
+
*/
|
|
776
|
+
get mouseOverRowName() {
|
|
639
777
|
return self.mouseRow !== undefined
|
|
640
778
|
? this.rowNames[self.mouseRow]
|
|
641
779
|
: undefined
|
|
@@ -652,9 +790,10 @@ const model = types
|
|
|
652
790
|
* #getter
|
|
653
791
|
*/
|
|
654
792
|
get root() {
|
|
655
|
-
let hier = hierarchy(this.
|
|
793
|
+
let hier = hierarchy(this._tree, d => d.branchset)
|
|
656
794
|
.sum(d => (d.branchset ? 0 : 1))
|
|
657
795
|
.sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
|
|
796
|
+
|
|
658
797
|
if (self.showOnly) {
|
|
659
798
|
const res = hier.find(node => node.data.id === self.showOnly)
|
|
660
799
|
if (res) {
|
|
@@ -665,22 +804,21 @@ const model = types
|
|
|
665
804
|
if (self.collapsed.length) {
|
|
666
805
|
self.collapsed
|
|
667
806
|
.map(collapsedId => hier.find(node => node.data.id === collapsedId))
|
|
668
|
-
.filter(
|
|
807
|
+
.filter(notEmpty)
|
|
669
808
|
.map(node => collapse(node))
|
|
670
809
|
}
|
|
810
|
+
if (self.hidden.length) {
|
|
811
|
+
self.hidden
|
|
812
|
+
.map(hiddenId => hier.find(node => node.data.id === hiddenId))
|
|
813
|
+
.filter(notEmpty)
|
|
814
|
+
.map(node => filterHiddenLeafNodes(node.parent, node.id))
|
|
815
|
+
}
|
|
671
816
|
return hier
|
|
672
817
|
},
|
|
673
818
|
/**
|
|
674
819
|
* #getter
|
|
675
820
|
*/
|
|
676
|
-
get structures(): Record<
|
|
677
|
-
string,
|
|
678
|
-
{
|
|
679
|
-
pdb: string
|
|
680
|
-
startPos: number
|
|
681
|
-
endPos: number
|
|
682
|
-
}[]
|
|
683
|
-
> {
|
|
821
|
+
get structures(): Record<string, Structure[]> {
|
|
684
822
|
return this.MSA?.getStructures() || {}
|
|
685
823
|
},
|
|
686
824
|
/**
|
|
@@ -695,6 +833,7 @@ const model = types
|
|
|
695
833
|
},
|
|
696
834
|
/**
|
|
697
835
|
* #getter
|
|
836
|
+
* widget width minus the tree area gives the space for the MSA
|
|
698
837
|
*/
|
|
699
838
|
get msaAreaWidth() {
|
|
700
839
|
return self.width - self.treeAreaWidth
|
|
@@ -706,8 +845,8 @@ const model = types
|
|
|
706
845
|
const blanks = []
|
|
707
846
|
const strs = this.hierarchy
|
|
708
847
|
.leaves()
|
|
709
|
-
.map(
|
|
710
|
-
.filter((item): item is string
|
|
848
|
+
.map(leaf => this.MSA?.getRow(leaf.data.name))
|
|
849
|
+
.filter((item): item is string => !!item)
|
|
711
850
|
|
|
712
851
|
for (let i = 0; i < strs[0]?.length; i++) {
|
|
713
852
|
let counter = 0
|
|
@@ -726,10 +865,11 @@ const model = types
|
|
|
726
865
|
* #getter
|
|
727
866
|
*/
|
|
728
867
|
get rows() {
|
|
868
|
+
const MSA = this.MSA
|
|
729
869
|
return this.hierarchy
|
|
730
870
|
.leaves()
|
|
731
|
-
.map(
|
|
732
|
-
.filter((f): f is [string, string
|
|
871
|
+
.map(leaf => [leaf.data.name, MSA?.getRow(leaf.data.name)] as const)
|
|
872
|
+
.filter((f): f is [string, string] => !!f[1])
|
|
733
873
|
},
|
|
734
874
|
/**
|
|
735
875
|
* #getter
|
|
@@ -774,18 +914,15 @@ const model = types
|
|
|
774
914
|
* generates a new tree that is clustered with x,y positions
|
|
775
915
|
*/
|
|
776
916
|
get hierarchy(): HierarchyNode<NodeWithIdsAndLength> {
|
|
777
|
-
const
|
|
917
|
+
const r = this.root
|
|
778
918
|
const clust = cluster<NodeWithIds>()
|
|
779
919
|
.size([this.totalHeight, self.treeWidth])
|
|
780
920
|
.separation(() => 1)
|
|
781
|
-
clust(
|
|
782
|
-
setBrLength(
|
|
783
|
-
|
|
784
|
-
(root.data.length = 0),
|
|
785
|
-
self.treeWidth / maxLength(root),
|
|
786
|
-
)
|
|
787
|
-
return root as HierarchyNode<NodeWithIdsAndLength>
|
|
921
|
+
clust(r)
|
|
922
|
+
setBrLength(r, (r.data.length = 0), self.treeWidth / maxLength(r))
|
|
923
|
+
return r as HierarchyNode<NodeWithIdsAndLength>
|
|
788
924
|
},
|
|
925
|
+
|
|
789
926
|
/**
|
|
790
927
|
* #getter
|
|
791
928
|
*/
|
|
@@ -810,12 +947,14 @@ const model = types
|
|
|
810
947
|
})
|
|
811
948
|
}
|
|
812
949
|
},
|
|
950
|
+
|
|
813
951
|
/**
|
|
814
952
|
* #action
|
|
815
953
|
*/
|
|
816
954
|
doScrollY(deltaY: number) {
|
|
817
955
|
self.scrollY = clamp(-self.totalHeight + 10, self.scrollY + deltaY, 0)
|
|
818
956
|
},
|
|
957
|
+
|
|
819
958
|
/**
|
|
820
959
|
* #action
|
|
821
960
|
*/
|
|
@@ -826,6 +965,7 @@ const model = types
|
|
|
826
965
|
0,
|
|
827
966
|
)
|
|
828
967
|
},
|
|
968
|
+
|
|
829
969
|
/**
|
|
830
970
|
* #action
|
|
831
971
|
*/
|
|
@@ -836,6 +976,7 @@ const model = types
|
|
|
836
976
|
0,
|
|
837
977
|
)
|
|
838
978
|
},
|
|
979
|
+
|
|
839
980
|
/**
|
|
840
981
|
* #action
|
|
841
982
|
*/
|
|
@@ -876,27 +1017,32 @@ const model = types
|
|
|
876
1017
|
const { rowHeight, hierarchy, treeMetadata, fontSize } = self
|
|
877
1018
|
if (rowHeight > 5) {
|
|
878
1019
|
for (const node of hierarchy.leaves()) {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1020
|
+
x = Math.max(
|
|
1021
|
+
measureText(
|
|
1022
|
+
treeMetadata[node.data.name]?.genome || node.data.name,
|
|
1023
|
+
fontSize,
|
|
1024
|
+
),
|
|
1025
|
+
x,
|
|
1026
|
+
)
|
|
884
1027
|
}
|
|
885
1028
|
}
|
|
886
1029
|
return x
|
|
887
1030
|
},
|
|
1031
|
+
|
|
888
1032
|
/**
|
|
889
1033
|
* #getter
|
|
890
1034
|
*/
|
|
891
1035
|
get secondaryStructureConsensus() {
|
|
892
1036
|
return self.MSA?.secondaryStructureConsensus
|
|
893
1037
|
},
|
|
1038
|
+
|
|
894
1039
|
/**
|
|
895
1040
|
* #getter
|
|
896
1041
|
*/
|
|
897
1042
|
get seqConsensus() {
|
|
898
1043
|
return self.MSA?.seqConsensus
|
|
899
1044
|
},
|
|
1045
|
+
|
|
900
1046
|
/**
|
|
901
1047
|
* #getter
|
|
902
1048
|
*/
|
|
@@ -911,69 +1057,40 @@ const model = types
|
|
|
911
1057
|
}
|
|
912
1058
|
return ['a']
|
|
913
1059
|
},
|
|
1060
|
+
|
|
914
1061
|
/**
|
|
915
1062
|
* #getter
|
|
916
1063
|
*/
|
|
917
|
-
get
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
}
|
|
930
|
-
})
|
|
931
|
-
: ([] as BasicTrack[])
|
|
1064
|
+
get adapterTrackModels(): BasicTrack[] {
|
|
1065
|
+
return (
|
|
1066
|
+
self.MSA?.tracks.map(t => ({
|
|
1067
|
+
model: {
|
|
1068
|
+
...t,
|
|
1069
|
+
data: t.data ? skipBlanks(self.blanks, t.data) : undefined,
|
|
1070
|
+
height: self.rowHeight,
|
|
1071
|
+
} as TextTrackModel,
|
|
1072
|
+
ReactComponent: TextTrack,
|
|
1073
|
+
})) || []
|
|
1074
|
+
)
|
|
1075
|
+
},
|
|
932
1076
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1077
|
+
/**
|
|
1078
|
+
* #getter
|
|
1079
|
+
*/
|
|
1080
|
+
get boxTrackModels(): BasicTrack[] {
|
|
1081
|
+
return self.boxTracks
|
|
1082
|
+
.filter(track => self.rows.some(row => row[0] === track.name))
|
|
936
1083
|
.map(track => ({
|
|
937
1084
|
model: track as BoxTrackModel,
|
|
938
1085
|
ReactComponent: BoxTrack,
|
|
939
1086
|
}))
|
|
1087
|
+
},
|
|
940
1088
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
features: self.annotatedRegions,
|
|
947
|
-
height: 100,
|
|
948
|
-
id: 'annotations',
|
|
949
|
-
name: 'User-created annotations',
|
|
950
|
-
data: self.annotatedRegions
|
|
951
|
-
.map(region => {
|
|
952
|
-
const attrs = region.attributes
|
|
953
|
-
? Object.entries(region.attributes)
|
|
954
|
-
.map(([k, v]) => `${k}=${v.join(',')}`)
|
|
955
|
-
.join(';')
|
|
956
|
-
: '.'
|
|
957
|
-
return [
|
|
958
|
-
'MSA_refcoord',
|
|
959
|
-
'.',
|
|
960
|
-
'.',
|
|
961
|
-
region.start,
|
|
962
|
-
region.end,
|
|
963
|
-
'.',
|
|
964
|
-
'.',
|
|
965
|
-
'.',
|
|
966
|
-
attrs,
|
|
967
|
-
].join('\t')
|
|
968
|
-
})
|
|
969
|
-
.join('\n'),
|
|
970
|
-
} as BoxTrackModel,
|
|
971
|
-
ReactComponent: BoxTrack,
|
|
972
|
-
},
|
|
973
|
-
]
|
|
974
|
-
: ([] as BasicTrack[])
|
|
975
|
-
|
|
976
|
-
return [...adapterTracks, ...boxTracks, ...annotationTracks]
|
|
1089
|
+
/**
|
|
1090
|
+
* #getter
|
|
1091
|
+
*/
|
|
1092
|
+
get tracks(): BasicTrack[] {
|
|
1093
|
+
return [...this.adapterTrackModels, ...this.boxTrackModels]
|
|
977
1094
|
},
|
|
978
1095
|
/**
|
|
979
1096
|
* #getter
|
|
@@ -981,6 +1098,7 @@ const model = types
|
|
|
981
1098
|
get turnedOnTracks() {
|
|
982
1099
|
return this.tracks.filter(f => !self.turnedOffTracks.has(f.model.id))
|
|
983
1100
|
},
|
|
1101
|
+
|
|
984
1102
|
/**
|
|
985
1103
|
* #method
|
|
986
1104
|
* returns coordinate in the current relative coordinate scheme
|
|
@@ -988,6 +1106,7 @@ const model = types
|
|
|
988
1106
|
pxToBp(coord: number) {
|
|
989
1107
|
return Math.floor((coord - self.scrollX) / self.colWidth)
|
|
990
1108
|
},
|
|
1109
|
+
|
|
991
1110
|
/**
|
|
992
1111
|
* #method
|
|
993
1112
|
*/
|
|
@@ -1022,6 +1141,7 @@ const model = types
|
|
|
1022
1141
|
|
|
1023
1142
|
return i - count
|
|
1024
1143
|
},
|
|
1144
|
+
|
|
1025
1145
|
/**
|
|
1026
1146
|
* #method
|
|
1027
1147
|
*/
|
|
@@ -1037,6 +1157,7 @@ const model = types
|
|
|
1037
1157
|
|
|
1038
1158
|
return position - count
|
|
1039
1159
|
},
|
|
1160
|
+
|
|
1040
1161
|
/**
|
|
1041
1162
|
* #method
|
|
1042
1163
|
*/
|
|
@@ -1058,6 +1179,29 @@ const model = types
|
|
|
1058
1179
|
}
|
|
1059
1180
|
return 0
|
|
1060
1181
|
},
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* #method
|
|
1185
|
+
*/
|
|
1186
|
+
relativePxToBp2(rowName: string, position: number) {
|
|
1187
|
+
const { rowNames, rows } = self
|
|
1188
|
+
const index = rowNames.indexOf(rowName)
|
|
1189
|
+
if (index !== -1) {
|
|
1190
|
+
const row = rows[index][1]
|
|
1191
|
+
|
|
1192
|
+
let k = 0
|
|
1193
|
+
let i = 0
|
|
1194
|
+
for (; k < position; i++) {
|
|
1195
|
+
if (row[i] !== '-') {
|
|
1196
|
+
k++
|
|
1197
|
+
} else if (k >= position) {
|
|
1198
|
+
break
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
return i
|
|
1202
|
+
}
|
|
1203
|
+
return 0
|
|
1204
|
+
},
|
|
1061
1205
|
/**
|
|
1062
1206
|
* #method
|
|
1063
1207
|
*/
|
|
@@ -1072,50 +1216,26 @@ const model = types
|
|
|
1072
1216
|
return j
|
|
1073
1217
|
},
|
|
1074
1218
|
}))
|
|
1075
|
-
|
|
1076
|
-
/**
|
|
1077
|
-
* #action
|
|
1078
|
-
*/
|
|
1079
|
-
addAnnotation(
|
|
1080
|
-
start: number,
|
|
1081
|
-
end: number,
|
|
1082
|
-
attributes: Record<string, string[]>,
|
|
1083
|
-
) {
|
|
1084
|
-
self.annotatedRegions.push({
|
|
1085
|
-
start: self.getPos(start),
|
|
1086
|
-
end: self.getPos(end),
|
|
1087
|
-
attributes,
|
|
1088
|
-
})
|
|
1089
|
-
},
|
|
1090
|
-
/**
|
|
1091
|
-
* #action
|
|
1092
|
-
*/
|
|
1093
|
-
setAnnotationClickBoundaries(left: number, right: number) {
|
|
1094
|
-
self.annotPos = { left, right }
|
|
1095
|
-
},
|
|
1096
|
-
/**
|
|
1097
|
-
* #action
|
|
1098
|
-
*/
|
|
1099
|
-
clearAnnotationClickBoundaries() {
|
|
1100
|
-
self.annotPos = undefined
|
|
1101
|
-
},
|
|
1102
|
-
/**
|
|
1103
|
-
* #action
|
|
1104
|
-
*/
|
|
1105
|
-
incrementRef() {
|
|
1106
|
-
self.nref++
|
|
1107
|
-
},
|
|
1108
|
-
}))
|
|
1219
|
+
|
|
1109
1220
|
.views(self => ({
|
|
1110
1221
|
/**
|
|
1111
1222
|
* #getter
|
|
1223
|
+
* total height of track area (px)
|
|
1112
1224
|
*/
|
|
1113
1225
|
get totalTrackAreaHeight() {
|
|
1114
1226
|
return sum(self.turnedOnTracks.map(r => r.model.height))
|
|
1115
1227
|
},
|
|
1116
1228
|
}))
|
|
1117
1229
|
.actions(self => ({
|
|
1230
|
+
/**
|
|
1231
|
+
* #action
|
|
1232
|
+
* internal, used for drawing to canvas
|
|
1233
|
+
*/
|
|
1234
|
+
incrementRef() {
|
|
1235
|
+
self.nref++
|
|
1236
|
+
},
|
|
1118
1237
|
afterCreate() {
|
|
1238
|
+
// autorun opens treeFilehandle
|
|
1119
1239
|
addDisposer(
|
|
1120
1240
|
self,
|
|
1121
1241
|
autorun(async () => {
|
|
@@ -1130,6 +1250,7 @@ const model = types
|
|
|
1130
1250
|
}
|
|
1131
1251
|
}),
|
|
1132
1252
|
)
|
|
1253
|
+
// autorun opens treeMetadataFilehandle
|
|
1133
1254
|
addDisposer(
|
|
1134
1255
|
self,
|
|
1135
1256
|
autorun(async () => {
|
|
@@ -1146,6 +1267,8 @@ const model = types
|
|
|
1146
1267
|
}
|
|
1147
1268
|
}),
|
|
1148
1269
|
)
|
|
1270
|
+
|
|
1271
|
+
// autorun opens msaFilehandle
|
|
1149
1272
|
addDisposer(
|
|
1150
1273
|
self,
|
|
1151
1274
|
autorun(async () => {
|
|
@@ -1160,6 +1283,8 @@ const model = types
|
|
|
1160
1283
|
}
|
|
1161
1284
|
}),
|
|
1162
1285
|
)
|
|
1286
|
+
|
|
1287
|
+
// autorun synchronizes treeWidth with treeAreaWidth
|
|
1163
1288
|
addDisposer(
|
|
1164
1289
|
self,
|
|
1165
1290
|
autorun(async () => {
|