@jbrowse/product-core 4.2.1 → 5.0.0-beta.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 (133) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +629 -304
  2. package/esm/RootModel/BaseRootModel.js +138 -14
  3. package/esm/RootModel/HierarchicalConfig.d.ts +57 -21
  4. package/esm/RootModel/HierarchicalConfig.js +36 -0
  5. package/esm/RootModel/InternetAccounts.d.ts +18 -1
  6. package/esm/RootModel/InternetAccounts.js +120 -22
  7. package/esm/RootModel/PreferencesConfig.d.ts +53 -0
  8. package/esm/RootModel/PreferencesConfig.js +61 -0
  9. package/esm/RootModel/createConfigModel.d.ts +232 -0
  10. package/esm/RootModel/createConfigModel.js +56 -0
  11. package/esm/RootModel/index.d.ts +10 -5
  12. package/esm/RootModel/index.js +10 -5
  13. package/esm/RootModel/menuItems.d.ts +24 -0
  14. package/esm/RootModel/menuItems.js +143 -0
  15. package/esm/Session/BaseSession.d.ts +310 -11
  16. package/esm/Session/BaseSession.js +448 -14
  17. package/esm/Session/Connections.d.ts +114 -8
  18. package/esm/Session/Connections.js +195 -74
  19. package/esm/Session/DrawerWidgets.d.ts +115 -12
  20. package/esm/Session/DrawerWidgets.js +161 -42
  21. package/esm/Session/MultipleViews.d.ts +1266 -1128
  22. package/esm/Session/MultipleViews.js +357 -66
  23. package/esm/Session/Preferences.d.ts +1665 -0
  24. package/esm/Session/Preferences.js +54 -0
  25. package/esm/Session/ReferenceManagement.d.ts +28 -3
  26. package/esm/Session/ReferenceManagement.js +56 -41
  27. package/esm/Session/SessionTracks.d.ts +1275 -1138
  28. package/esm/Session/SessionTracks.js +532 -35
  29. package/esm/Session/Themes.d.ts +87 -2
  30. package/esm/Session/Themes.js +156 -23
  31. package/esm/Session/TrackMenu.d.ts +112 -0
  32. package/esm/Session/TrackMenu.js +200 -0
  33. package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
  34. package/esm/Session/TrackMenuSessionMixin.js +41 -0
  35. package/esm/Session/Tracks.d.ts +1169 -1117
  36. package/esm/Session/Tracks.js +160 -20
  37. package/esm/Session/assertSessionModel.d.ts +21 -0
  38. package/esm/Session/assertSessionModel.js +1 -0
  39. package/esm/Session/finalizeSession.d.ts +16 -0
  40. package/esm/Session/finalizeSession.js +29 -0
  41. package/esm/Session/index.d.ts +22 -10
  42. package/esm/Session/index.js +13 -10
  43. package/esm/Session/shareableSnapshot.d.ts +77 -0
  44. package/esm/Session/shareableSnapshot.js +196 -0
  45. package/esm/Session/temporaryAssemblyTracks.d.ts +23 -0
  46. package/esm/Session/temporaryAssemblyTracks.js +38 -0
  47. package/esm/assertExtends.d.ts +55 -0
  48. package/esm/assertExtends.js +1 -0
  49. package/esm/controllerTracks.d.ts +56 -0
  50. package/esm/controllerTracks.js +77 -0
  51. package/esm/destroyViewState.d.ts +21 -0
  52. package/esm/destroyViewState.js +29 -0
  53. package/esm/index.d.ts +31 -4
  54. package/esm/index.js +31 -4
  55. package/esm/loadPlugins.d.ts +33 -0
  56. package/esm/loadPlugins.js +38 -0
  57. package/esm/localFiles.d.ts +74 -0
  58. package/esm/localFiles.js +156 -0
  59. package/esm/observeSession.d.ts +56 -0
  60. package/esm/observeSession.js +91 -0
  61. package/esm/pluginInput.d.ts +32 -0
  62. package/esm/pluginInput.js +19 -0
  63. package/esm/pruneUnbuildableNodes.d.ts +109 -0
  64. package/esm/pruneUnbuildableNodes.js +568 -0
  65. package/esm/resolveAssemblies.d.ts +44 -0
  66. package/esm/resolveAssemblies.js +55 -0
  67. package/esm/rpcWorker.d.ts +26 -0
  68. package/esm/rpcWorker.js +96 -15
  69. package/esm/scheduleDetachedDestroy.d.ts +18 -0
  70. package/esm/scheduleDetachedDestroy.js +24 -0
  71. package/esm/sessionMigrations/index.d.ts +53 -0
  72. package/esm/sessionMigrations/index.js +432 -0
  73. package/esm/sessionUrl.d.ts +41 -0
  74. package/esm/sessionUrl.js +53 -0
  75. package/esm/sessionUtils.d.ts +69 -0
  76. package/esm/sessionUtils.js +613 -0
  77. package/esm/siblingCast.d.ts +14 -0
  78. package/esm/siblingCast.js +16 -0
  79. package/esm/snapshotUtils.d.ts +8 -0
  80. package/esm/snapshotUtils.js +12 -0
  81. package/esm/ui/AboutDialog.d.ts +3 -6
  82. package/esm/ui/AboutDialog.js +4 -4
  83. package/esm/ui/AboutDialogContents.d.ts +2 -6
  84. package/esm/ui/AboutDialogContents.js +34 -13
  85. package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
  86. package/esm/ui/AssemblyInfoPanel.js +69 -0
  87. package/esm/ui/DisplayDefaultsSection.d.ts +36 -0
  88. package/esm/ui/DisplayDefaultsSection.js +65 -0
  89. package/esm/ui/Drawer.d.ts +21 -0
  90. package/esm/ui/Drawer.js +60 -0
  91. package/esm/ui/DrawerControls.d.ts +5 -0
  92. package/esm/ui/DrawerControls.js +28 -0
  93. package/esm/ui/DrawerHeader.d.ts +6 -0
  94. package/esm/ui/DrawerHeader.js +36 -0
  95. package/esm/ui/DrawerWidget.d.ts +5 -0
  96. package/esm/ui/DrawerWidget.js +12 -0
  97. package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
  98. package/esm/ui/DrawerWidgetSelector.js +40 -0
  99. package/esm/ui/FileInfoPanel.d.ts +3 -6
  100. package/esm/ui/FileInfoPanel.js +35 -26
  101. package/esm/ui/HeaderButtons.d.ts +7 -1
  102. package/esm/ui/HeaderButtons.js +23 -18
  103. package/esm/ui/ModalWidget.d.ts +12 -0
  104. package/esm/ui/ModalWidget.js +33 -0
  105. package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
  106. package/esm/ui/ModalWidgetAppBar.js +9 -0
  107. package/esm/ui/PreferencesDialog.d.ts +56 -0
  108. package/esm/ui/PreferencesDialog.js +145 -0
  109. package/esm/ui/PreferencesResetDialog.d.ts +8 -0
  110. package/esm/ui/PreferencesResetDialog.js +19 -0
  111. package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
  112. package/esm/ui/RefNameAliasesDialog.js +65 -0
  113. package/esm/ui/RefNameInfoDialog.d.ts +3 -6
  114. package/esm/ui/RefNameInfoDialog.js +57 -63
  115. package/esm/ui/WidgetBody.d.ts +18 -0
  116. package/esm/ui/WidgetBody.js +22 -0
  117. package/esm/ui/WidgetHeading.d.ts +7 -0
  118. package/esm/ui/WidgetHeading.js +7 -0
  119. package/esm/ui/drawerLayout.d.ts +18 -0
  120. package/esm/ui/drawerLayout.js +22 -0
  121. package/esm/ui/index.d.ts +13 -1
  122. package/esm/ui/index.js +12 -1
  123. package/esm/ui/util.d.ts +37 -4
  124. package/esm/ui/util.js +35 -38
  125. package/esm/useEngineLifecycle.d.ts +13 -0
  126. package/esm/useEngineLifecycle.js +22 -0
  127. package/package.json +11 -15
  128. package/esm/RootModel/FormatAbout.d.ts +0 -12
  129. package/esm/RootModel/FormatAbout.js +0 -15
  130. package/esm/RootModel/FormatDetails.d.ts +0 -24
  131. package/esm/RootModel/FormatDetails.js +0 -27
  132. package/esm/Session/DialogQueue.d.ts +0 -17
  133. package/esm/Session/DialogQueue.js +0 -31
@@ -1,68 +1,502 @@
1
+ import { getConf } from '@jbrowse/core/configuration';
2
+ import SnackbarModel from '@jbrowse/core/ui/SnackbarModel';
3
+ import { setNumberGrouping } from '@jbrowse/core/util';
4
+ import { freezeDeep } from '@jbrowse/core/util/freezeDeep';
5
+ import { isFeature, unwrapFeature } from '@jbrowse/core/util/simpleFeature';
1
6
  import { ElementId } from '@jbrowse/core/util/types/mst';
2
7
  import { getParent, isStateTreeNode, types } from '@jbrowse/mobx-state-tree';
8
+ import { observable } from 'mobx';
9
+ function isAnimationMode(val) {
10
+ return val === 'system' || val === 'enabled' || val === 'disabled';
11
+ }
12
+ // Promoted per-display-type slot defaults live flat in `preferencesOverrides`
13
+ // under one composite key each (`displayTypeDefault\0<type>\0<slot>`), not under
14
+ // a single nested `displayTypeDefaults` object. Flat keys make each promoted
15
+ // default its own tracked observable-map entry — promoting one can't invalidate
16
+ // a reader of another (every promotable display's `rpcProps` reads one via
17
+ // `getDisplayTypeDefault`) — and collapse the get/set/diff logic to one lookup.
18
+ // The `\0` delimiter can't appear in a display type or slot name.
19
+ // The literal is a **persisted localStorage key prefix**, so treat it as data
20
+ // rather than as an identifier a rename should sweep — a blanket
21
+ // `displayTypeDefault` → `pin` pass did sweep it, and orphans every promoted
22
+ // default already in a browser's storage. Nothing released stores these yet, so
23
+ // the reason it keeps this spelling is naming, not compatibility: the key names
24
+ // *what is stored* (a display-type default, reached through
25
+ // `get/setDisplayTypeDefault`), while `Pin` names the menu control the write
26
+ // is offered from.
27
+ const DISPLAY_TYPE_DEFAULT_PREFIX = 'displayTypeDefault\0';
28
+ // Module-private on purpose: the composite-key spelling is a storage detail of
29
+ // this file's get/set/diff methods, and nothing outside it should be able to
30
+ // depend on the layout.
31
+ function displayTypeDefaultKey(displayType, slot) {
32
+ return `${DISPLAY_TYPE_DEFAULT_PREFIX}${displayType}\0${slot}`;
33
+ }
34
+ function parseDisplayTypeDefaultKey(key) {
35
+ const rest = key.startsWith(DISPLAY_TYPE_DEFAULT_PREFIX)
36
+ ? key.slice(DISPLAY_TYPE_DEFAULT_PREFIX.length)
37
+ : '';
38
+ const [displayType, slot] = rest.split('\0');
39
+ return displayType && slot ? { displayType, slot } : undefined;
40
+ }
41
+ // Head of the display path a promoted default takes in a `getPreferenceChanges`
42
+ // row: `[DISPLAY_TYPE_DEFAULTS_PATH_HEAD, displayType, slot]`. Purely a
43
+ // readable row label, deliberately distinct from the flat
44
+ // `DISPLAY_TYPE_DEFAULT_PREFIX` storage key above.
45
+ //
46
+ // Module-private, like the storage key: both the producer
47
+ // (`getPreferenceChanges`) and the consumer (`resetPreferenceChange`) are
48
+ // methods on this model, so the two path shapes this file emits are the only
49
+ // ones it has to undo. It used to be exported for the Preferences dialog to
50
+ // match on and re-route back through `setDisplayTypeDefault`, which meant a
51
+ // rename on one side alone would silently no-op that dialog's per-row reset.
52
+ const DISPLAY_TYPE_DEFAULTS_PATH_HEAD = 'displayTypeDefaults';
53
+ /**
54
+ * #stateModel BaseSessionModel
55
+ *
56
+ * base session shared by all JBrowse products. Be careful what you include
57
+ * here, everything will use it.
58
+ */
3
59
  export function BaseSessionModel(_pluginManager) {
4
- return types
60
+ const baseModel = types
5
61
  .model({
62
+ /**
63
+ * #property
64
+ */
6
65
  id: ElementId,
66
+ /**
67
+ * #property
68
+ */
7
69
  name: types.string,
8
- margin: 0,
70
+ /**
71
+ * #property
72
+ * used to keep track of which view is in focus
73
+ */
74
+ focusedViewId: types.maybe(types.string),
75
+ /**
76
+ * #property
77
+ * one session-wide toggle for all region highlight bands (URL/view
78
+ * highlights and bookmark overlays)
79
+ */
80
+ highlightsVisible: types.stripDefault(types.boolean, true),
81
+ /**
82
+ * #property
83
+ * views, tracks, displays and widgets `pruneUnbuildableNodes` took out of
84
+ * the tree because this build has no plugin for their type, each with the
85
+ * anchor that puts it back. Opaque here on purpose — nothing in the
86
+ * session reads it, and the shape belongs to that module.
87
+ *
88
+ * It is a **declared property** rather than data riding along in the
89
+ * snapshot because MST silently drops an undeclared key: without this
90
+ * line the held nodes survive the prune and then vanish the moment
91
+ * `setSession` builds the tree, and every unit test of the prune still
92
+ * passes. `heldNodesSurviveTheSession.test.ts` is the canary.
93
+ */
94
+ heldForMissingPlugins: types.stripDefault(types.frozen(), undefined),
9
95
  })
10
96
  .volatile(() => ({
97
+ /**
98
+ * #volatile
99
+ * this is the globally "selected" object. can be anything. code that
100
+ * wants to deal with this should examine it to see what kind of thing it
101
+ * is.
102
+ */
103
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
11
104
  selection: undefined,
105
+ /**
106
+ * #volatile
107
+ * this is the globally "hovered" object. can be anything. code that
108
+ * wants to deal with this should examine it to see what kind of thing it
109
+ * is.
110
+ */
111
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
12
112
  hovered: undefined,
113
+ /**
114
+ * #volatile
115
+ */
116
+ queueOfDialogs: [],
117
+ /**
118
+ * #volatile
119
+ * runtime user-preference overrides keyed by preference id, resolved by
120
+ * `getPreference` against the `configuration.preferences` admin defaults.
121
+ * Empty here (config-only); products that let users edit preferences load
122
+ * and persist these via localStorage. A runtime override map layered over
123
+ * config defaults, kept off the snapshot since prefs are local UI.
124
+ *
125
+ * An `observable.map` (not a plain object reassigned wholesale) so each
126
+ * preference is its own tracked key: writing one (`setScrollZoom`) can't
127
+ * invalidate a reader of another (`getDisplayTypeDefault` in a track's
128
+ * `rpcProps`). A single spread-replaced object made every setter wake
129
+ * every reader, so toggling scroll-to-zoom re-fetched every track. For the
130
+ * same reason each promoted per-display-type default is a flat composite
131
+ * key (see `displayTypeDefaultKey`), not a single nested `displayTypeDefaults`
132
+ * object — promoting one default can't wake readers of a different one.
133
+ *
134
+ * `deep: false` is load-bearing, not a micro-optimization. The default
135
+ * enhancer wraps an object/array value in a MobX Proxy on `set`, and a
136
+ * promoted default is handed straight back out by `getConf` — so an
137
+ * object-valued promotable slot (alignments `colorBy`) put a Proxy into
138
+ * `rpcProps()`, and V8's structured-clone serializer rejects a Proxy:
139
+ * `worker.postMessage` threw `DataCloneError` on the next fetch of any
140
+ * track following that default (electron IPC and `structuredClone` in the
141
+ * share bake likewise). The map still notifies per key on `set`, so shallow
142
+ * values lose no reactivity — and nothing can mutate a preference in place,
143
+ * because `setPreferenceOverride` freezes what it stores.
144
+ */
145
+ preferencesOverrides: observable.map(undefined, {
146
+ deep: false,
147
+ }),
13
148
  }))
14
149
  .views(self => ({
150
+ /**
151
+ * #getter
152
+ */
15
153
  get root() {
16
154
  return getParent(self);
17
155
  },
18
156
  }))
19
157
  .views(self => ({
158
+ /**
159
+ * #getter
160
+ */
20
161
  get jbrowse() {
21
162
  return self.root.jbrowse;
22
163
  },
164
+ /**
165
+ * #getter
166
+ */
23
167
  get rpcManager() {
24
168
  return self.root.rpcManager;
25
169
  },
170
+ /**
171
+ * #getter
172
+ */
26
173
  get configuration() {
27
174
  return this.jbrowse.configuration;
28
175
  },
176
+ /**
177
+ * #getter
178
+ */
29
179
  get adminMode() {
30
180
  return self.root.adminMode;
31
181
  },
182
+ /**
183
+ * #getter
184
+ */
32
185
  get textSearchManager() {
33
186
  return self.root.textSearchManager;
34
187
  },
35
188
  }))
36
189
  .views(self => ({
190
+ /**
191
+ * #getter
192
+ */
37
193
  get assemblies() {
38
194
  return self.jbrowse.assemblies;
39
195
  },
196
+ /**
197
+ * #getter
198
+ */
199
+ get DialogComponent() {
200
+ return self.queueOfDialogs[0]?.[0];
201
+ },
202
+ /**
203
+ * #getter
204
+ */
205
+ get DialogProps() {
206
+ return self.queueOfDialogs[0]?.[1];
207
+ },
208
+ }))
209
+ .views(self => ({
210
+ /**
211
+ * #method
212
+ * the admin/embedder `configuration.preferences` value for a key, ignoring
213
+ * any runtime override — i.e. what a reset falls back to. Exposed rather
214
+ * than inlined because "differs from the default" is a question the
215
+ * Preferences reset diff asks about settings this map doesn't hold (see
216
+ * `defaultUseWorkspaces`).
217
+ */
218
+ getPreferenceDefault(key) {
219
+ return getConf(self, ['preferences', key]);
220
+ },
221
+ /**
222
+ * #method
223
+ * resolved value of a user preference: a runtime override if the user set
224
+ * one, otherwise the admin/embedder `configuration.preferences` default.
225
+ * The override map is empty unless the product loads it (web/desktop).
226
+ */
227
+ getPreference(key) {
228
+ const override = self.preferencesOverrides.get(key);
229
+ return override === undefined
230
+ ? this.getPreferenceDefault(key)
231
+ : override;
232
+ },
233
+ /**
234
+ * #method
235
+ * resolved value of a per-display-type slot default the user promoted
236
+ * (see `setDisplayTypeDefault`); undefined when nothing was promoted.
237
+ */
238
+ getDisplayTypeDefault(displayType, slot) {
239
+ return self.preferencesOverrides.get(displayTypeDefaultKey(displayType, slot));
240
+ },
241
+ /**
242
+ * #method
243
+ * every per-display-type default the user has promoted, as
244
+ * `{ displayType, slot, value }` — the inventory the Preferences dialog
245
+ * lists and clears one at a time (`setDisplayTypeDefault(…, undefined)`).
246
+ *
247
+ * Here rather than filtered out of `getPreferenceChanges` by the dialog,
248
+ * because the composite-key layout is this file's: a consumer that
249
+ * recognized these rows by matching the path head is exactly the coupling
250
+ * `DISPLAY_TYPE_DEFAULTS_PATH_HEAD` stopped being exported over, where a
251
+ * rename on one side alone silently no-ops the other.
252
+ */
253
+ getDisplayTypeDefaults() {
254
+ const found = [];
255
+ for (const [key, value] of self.preferencesOverrides.entries()) {
256
+ const parsed = parseDisplayTypeDefaultKey(key);
257
+ if (parsed) {
258
+ found.push({ ...parsed, value });
259
+ }
260
+ }
261
+ return found;
262
+ },
263
+ /**
264
+ * #method
265
+ * every runtime preference-override that currently differs from its
266
+ * config/admin default, as `{ path, from, to }` rows — the exact set
267
+ * `clearPreferenceOverrides` reverts. Backs the confirmation diff shown
268
+ * before "Reset to defaults" (mirrors the per-track changes dialog). A
269
+ * scalar pref (animationMode, scrollZoom) whose override equals the
270
+ * default is omitted (reverting it is a no-op); each promoted
271
+ * per-display-type default is always a difference from the un-promoted
272
+ * state, so `from` reads "(default)".
273
+ */
274
+ getPreferenceChanges() {
275
+ const changes = [];
276
+ for (const [key, value] of self.preferencesOverrides.entries()) {
277
+ const dtd = parseDisplayTypeDefaultKey(key);
278
+ if (dtd) {
279
+ changes.push({
280
+ path: [
281
+ DISPLAY_TYPE_DEFAULTS_PATH_HEAD,
282
+ dtd.displayType,
283
+ dtd.slot,
284
+ ],
285
+ from: undefined,
286
+ to: value,
287
+ });
288
+ }
289
+ else {
290
+ const dflt = this.getPreferenceDefault(key);
291
+ if (value !== dflt) {
292
+ changes.push({
293
+ path: [key],
294
+ from: dflt,
295
+ to: value,
296
+ });
297
+ }
298
+ }
299
+ }
300
+ return changes;
301
+ },
302
+ }))
303
+ .views(self => ({
304
+ /**
305
+ * #getter
306
+ * resolved feature-layout animation mode (never undefined)
307
+ */
308
+ get animationMode() {
309
+ const mode = self.getPreference('animationMode');
310
+ return isAnimationMode(mode) ? mode : 'enabled';
311
+ },
312
+ /**
313
+ * #getter
314
+ * resolved scroll-to-zoom preference. Global and personal (never shared in
315
+ * a session snapshot); every wheel-zoom view reads this single value.
316
+ */
317
+ get scrollZoom() {
318
+ return self.getPreference('scrollZoom') === true;
319
+ },
320
+ /**
321
+ * #getter
322
+ * resolved thousand-separator preference. Read for display in the
323
+ * Preferences dialog; the formatter itself reads a plain module variable
324
+ * set at startup in each realm (see `setNumberGrouping`), because worker-
325
+ * built strings can't see a main-thread observable.
326
+ */
327
+ get numberGrouping() {
328
+ return self.getPreference('numberGrouping') !== false;
329
+ },
40
330
  }))
41
331
  .actions(self => ({
332
+ afterAttach() {
333
+ // push the admin/embedder default down to the formatter, which is a
334
+ // plain module variable rather than an observable (see
335
+ // `setNumberGrouping`). Embedded products stop here; web and desktop
336
+ // compose PreferencesSessionMixin, whose own afterAttach runs after
337
+ // this one and re-applies the value once the user's stored overrides
338
+ // are loaded, so a user override still wins over the config default.
339
+ setNumberGrouping(self.numberGrouping);
340
+ },
341
+ /**
342
+ * #action
343
+ * set the global selection, i.e. the globally-selected object. can be a
344
+ * feature, a view, just about anything
345
+ *
346
+ * A feature is unwrapped on the way in, so app state never holds a
347
+ * jexlFeatureProxy. `isFeature` accepts a proxy, but on one `id` is a
348
+ * data field rather than the method the Feature type promises — every
349
+ * consumer doing `isFeature(selection) ? selection.id() : …` would throw.
350
+ */
42
351
  setSelection(thing) {
43
- self.selection = thing;
352
+ self.selection = isFeature(thing) ? unwrapFeature(thing) : thing;
44
353
  },
354
+ /**
355
+ * #action
356
+ * clears the global selection
357
+ */
45
358
  clearSelection() {
46
359
  self.selection = undefined;
47
360
  },
361
+ /**
362
+ * #action
363
+ */
48
364
  setHovered(thing) {
49
365
  self.hovered = thing;
50
366
  },
51
- }))
52
- .postProcessSnapshot(snap => {
53
- if (!snap) {
54
- return snap;
55
- }
56
- const { margin, ...rest } = snap;
57
- return {
58
- ...rest,
59
- ...(margin ? { margin } : {}),
60
- };
61
- });
367
+ /**
368
+ * #action
369
+ * toggle all region highlight bands across every view
370
+ */
371
+ setHighlightsVisible(arg) {
372
+ self.highlightsVisible = arg;
373
+ },
374
+ /**
375
+ * #action
376
+ * turn highlight bands back on, so a newly made highlight or bookmark is
377
+ * never silently swallowed by an earlier "highlights off"
378
+ */
379
+ revealHighlights() {
380
+ self.highlightsVisible = true;
381
+ },
382
+ /**
383
+ * #action
384
+ * set a runtime user-preference override (see `getPreference`). Mutates
385
+ * volatile state; products persist these to localStorage. An `undefined`
386
+ * value deletes the key (rather than leaving a phantom entry that
387
+ * `getPreference` reads as absent) so the store never holds dead keys.
388
+ */
389
+ setPreferenceOverride(key, value) {
390
+ if (value === undefined) {
391
+ self.preferencesOverrides.delete(key);
392
+ }
393
+ else {
394
+ // frozen because `deep: false` hands an object-valued preference (a
395
+ // promoted `colorBy`) straight back out by reference to every display
396
+ // following it, so the "nothing mutates a preference in place" this
397
+ // store relies on is enforced rather than assumed
398
+ self.preferencesOverrides.set(key, freezeDeep(value));
399
+ }
400
+ },
401
+ /**
402
+ * #action
403
+ * clear every runtime preference override at once — scrollZoom,
404
+ * animationMode, and every promoted per-display-type default (see
405
+ * `setDisplayTypeDefault`) — so each falls back to its config/admin
406
+ * default. Backs the Preferences dialog "Reset to defaults" button.
407
+ */
408
+ clearPreferenceOverrides() {
409
+ self.preferencesOverrides.clear();
410
+ },
411
+ /**
412
+ * #action
413
+ * clear a single runtime preference override (see `getPreference`) so it
414
+ * falls back to its config/admin default. Backs the per-entry reset in the
415
+ * Preferences dialog "Reset to defaults" confirmation.
416
+ */
417
+ clearPreferenceOverride(key) {
418
+ self.preferencesOverrides.delete(key);
419
+ },
420
+ /**
421
+ * #action
422
+ * revert one row emitted by `getPreferenceChanges`, addressed by its
423
+ * display `path`. Backs the per-entry reset in the Preferences dialog's
424
+ * "Reset to defaults" confirmation.
425
+ *
426
+ * Lives here rather than in that dialog because this model owns both path
427
+ * shapes it has to undo: a promoted per-display-type default, whose row
428
+ * path is a readable label over a flat composite storage key, and every
429
+ * other override, whose path *is* its key. The dialog used to re-derive
430
+ * the first case from an exported path-head constant.
431
+ */
432
+ resetPreferenceChange(path) {
433
+ const [head, displayType, slot] = path;
434
+ if (head === DISPLAY_TYPE_DEFAULTS_PATH_HEAD && displayType && slot) {
435
+ this.setDisplayTypeDefault(displayType, slot, undefined);
436
+ }
437
+ else if (head) {
438
+ this.clearPreferenceOverride(head);
439
+ }
440
+ },
441
+ /**
442
+ * #action
443
+ * set the global scroll-to-zoom preference (see the `scrollZoom` getter)
444
+ */
445
+ setScrollZoom(flag) {
446
+ this.setPreferenceOverride('scrollZoom', flag);
447
+ },
448
+ /**
449
+ * #action
450
+ * promote (or, with `value` undefined, clear) a per-display-type slot
451
+ * default. Just a preference override under one flat composite key (see
452
+ * `displayTypeDefaultKey`), so it persists and independently tracks like
453
+ * any other pref, and clearing deletes only that key.
454
+ */
455
+ setDisplayTypeDefault(displayType, slot, value) {
456
+ this.setPreferenceOverride(displayTypeDefaultKey(displayType, slot), value);
457
+ },
458
+ /**
459
+ * #action
460
+ */
461
+ setName(str) {
462
+ self.name = str;
463
+ },
464
+ /**
465
+ * #action
466
+ * `undefined` is "no view is focused", which the property has always been
467
+ * able to hold (`types.maybe`) and this had no way to spell. Nothing
468
+ * cleared it on teardown as a result: a view that was focused when it left
469
+ * the session left its id behind, and since every consumer compares
470
+ * `focusedViewId === view.id`, the id matched nothing, the focus ring
471
+ * vanished with nothing to say why, and the dead id persisted into a saved
472
+ * or shared session. `takeOut` clears it now.
473
+ */
474
+ setFocusedViewId(viewId) {
475
+ self.focusedViewId = viewId;
476
+ },
477
+ /**
478
+ * #action
479
+ */
480
+ removeActiveDialog() {
481
+ self.queueOfDialogs = self.queueOfDialogs.slice(1);
482
+ },
483
+ /**
484
+ * #action
485
+ */
486
+ queueDialog(doneCallback) {
487
+ const [component, props] = doneCallback(() => {
488
+ this.removeActiveDialog();
489
+ });
490
+ self.queueOfDialogs = [...self.queueOfDialogs, [component, props]];
491
+ },
492
+ }));
493
+ return types.compose(baseModel, SnackbarModel());
62
494
  }
495
+ /** Type guard for BaseSession */
63
496
  export function isBaseSession(thing) {
64
497
  return 'id' in thing && 'name' in thing && 'root' in thing;
65
498
  }
499
+ /** Type guard for whether a thing is JBrowse session */
66
500
  export function isSession(thing) {
67
501
  return isStateTreeNode(thing) && isBaseSession(thing);
68
502
  }
@@ -1,21 +1,127 @@
1
1
  import type PluginManager from '@jbrowse/core/PluginManager';
2
- import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
2
+ import type { AnyConfiguration, AnyConfigurationModel } from '@jbrowse/core/configuration';
3
3
  import type { BaseConnectionConfigModel } from '@jbrowse/core/pluggableElementTypes/models/baseConnectionConfig';
4
4
  import type { IAnyStateTreeNode, Instance } from '@jbrowse/mobx-state-tree';
5
+ /**
6
+ * Persisted config of a connection track the user has opened. Keyed by trackId
7
+ * in `connectionTrackConfigs`. `connectionId` records provenance so the track
8
+ * can be grouped under its connection and re-hydrated on demand.
9
+ */
10
+ export interface ConnectionTrackConfigEntry {
11
+ connectionId: string;
12
+ config: Record<string, unknown>;
13
+ }
14
+ /**
15
+ * #stateModel ConnectionManagementSessionMixin
16
+ */
5
17
  export declare function ConnectionManagementSessionMixin(pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{
6
- connectionInstances: import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>;
18
+ /**
19
+ * #property
20
+ */
21
+ connectionInstances: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>, [undefined]>;
22
+ /**
23
+ * #property
24
+ * Persisted configs of connection tracks the user has opened, keyed by
25
+ * trackId. Unlike `connectionInstances` (stripped from snapshots, holds
26
+ * the whole fetched hub), this holds only the tracks in use, so an open
27
+ * connection track resolves synchronously on session load without
28
+ * re-establishing the connection.
29
+ */
30
+ connectionTrackConfigs: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IType<Record<string, ConnectionTrackConfigEntry>, Record<string, ConnectionTrackConfigEntry>, Record<string, ConnectionTrackConfigEntry>>, [undefined]>;
7
31
  }, {
32
+ /**
33
+ * #getter
34
+ */
8
35
  readonly connections: BaseConnectionConfigModel[];
9
36
  } & {
10
- makeConnection(configuration: AnyConfigurationModel, initialSnapshot?: {}): any;
11
- prepareToBreakConnection(configuration: AnyConfigurationModel): [() => void, Record<string, number>] | undefined;
37
+ /**
38
+ * #action
39
+ */
40
+ makeConnection(configuration: AnyConfigurationModel, initialSnapshot?: object): any;
41
+ /**
42
+ * #action
43
+ * Remove a live connection instance. Tolerant of an already-dormant
44
+ * connection (its instance is stripped from the session on reload).
45
+ * Leaves persisted open-track configs alone — the connect() error path
46
+ * calls this and the user's already-open tracks must survive a transient
47
+ * failure. Full removal goes through `deleteConnection`.
48
+ */
12
49
  breakConnection(configuration: AnyConfigurationModel): void;
50
+ /**
51
+ * #action
52
+ * Close every track a connection contributed — the live instance's
53
+ * tracks plus any persisted open-track configs (a dormant connection,
54
+ * never expanded this session, still renders its opened tracks from
55
+ * `connectionTrackConfigs`) — from all views/widgets, drop the live
56
+ * instance, and drop the persisted configs. The session is left as if the
57
+ * connection had never loaded.
58
+ */
59
+ teardownConnection(configuration: AnyConfigurationModel): void;
60
+ /**
61
+ * #action
62
+ * Fully remove a connection: tear down its tracks and live instance, then
63
+ * delete its config.
64
+ */
13
65
  deleteConnection(configuration: AnyConfigurationModel): any;
14
- addConnectionConf(connectionConf: AnyConfigurationModel): any;
66
+ /**
67
+ * #action
68
+ * Adds to the **config**, not the session: `jbrowse.connections`, which
69
+ * every visitor to this instance loads. jbrowse-web overrides this so a
70
+ * non-admin's connection lands in `sessionConnections` instead — see
71
+ * WebSessionConnectionsMixin, whose `addSessionConnectionConf` is what
72
+ * to call when you mean the session specifically rather than "wherever
73
+ * this user's edits go".
74
+ *
75
+ * Applications without that override (Desktop, the embedded products)
76
+ * have only this one destination, and it is the right one there: their
77
+ * config and session are saved as a single document.
78
+ */
79
+ addConnectionConf(connectionConf: AnyConfiguration): any;
80
+ /**
81
+ * #action
82
+ */
15
83
  clearConnections(): void;
16
- }, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
17
- connectionInstances: import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>;
18
- }>>;
84
+ /**
85
+ * #action
86
+ * Snapshot a just-opened connection track's config into
87
+ * `connectionTrackConfigs` so it survives session reload. No-op if the
88
+ * track isn't connection-provided or is already captured (edits go
89
+ * through `updateConnectionTrackConfig`).
90
+ */
91
+ captureConnectionTrack(trackId: string): void;
92
+ /**
93
+ * #action
94
+ * Persist an edit to an opened connection track. The full config is
95
+ * stored (not a delta): the connection's fetched "base" isn't present at
96
+ * load, so only a complete config resolves synchronously.
97
+ */
98
+ updateConnectionTrackConfig(trackConf: Record<string, unknown> & {
99
+ trackId: string;
100
+ }): void;
101
+ /**
102
+ * #action
103
+ * Upsert one opened connection track's persisted config.
104
+ */
105
+ setConnectionTrackConfig(trackId: string, connectionId: string, config: Record<string, unknown>): void;
106
+ /**
107
+ * #action
108
+ * Drop a connection track's persisted config once no open view still
109
+ * references it, so the session doesn't accumulate closed tracks.
110
+ */
111
+ pruneConnectionTrackConfig(trackId: string): void;
112
+ /**
113
+ * #action
114
+ * Lazily establish a single connection by id if it isn't already live —
115
+ * used when its category is expanded in the track selector. Fetches
116
+ * silently (no view launch / success snackbar); already-open tracks keep
117
+ * rendering from `connectionTrackConfigs` meanwhile. Idempotent.
118
+ */
119
+ hydrateConnection(connectionId: string): void;
120
+ }, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
121
+ /** Session mixin MST type for a session that has connections */
19
122
  export type SessionWithConnectionsType = ReturnType<typeof ConnectionManagementSessionMixin>;
123
+ /** Instance of a session that has connections: `connectionInstances`,
124
+ * `makeConnection()`, etc. */
20
125
  export type SessionWithConnections = Instance<SessionWithConnectionsType>;
126
+ /** Type guard for SessionWithConnections */
21
127
  export declare function isSessionWithConnections(session: IAnyStateTreeNode): session is SessionWithConnections;