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