@jbrowse/product-core 4.3.0 → 5.0.0-beta.2

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