@jbrowse/product-core 5.0.0-beta.1 → 5.0.0-beta.11

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 (105) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +24 -158
  2. package/esm/RootModel/BaseRootModel.js +7 -4
  3. package/esm/RootModel/HierarchicalConfig.d.ts +21 -47
  4. package/esm/RootModel/PreferencesConfig.d.ts +20 -36
  5. package/esm/RootModel/PreferencesConfig.js +17 -3
  6. package/esm/RootModel/createConfigModel.d.ts +148 -123
  7. package/esm/RootModel/createConfigModel.js +35 -14
  8. package/esm/RootModel/menuItems.js +7 -9
  9. package/esm/Session/BaseSession.d.ts +60 -85
  10. package/esm/Session/BaseSession.js +106 -172
  11. package/esm/Session/Connections.d.ts +4 -3
  12. package/esm/Session/Connections.js +4 -3
  13. package/esm/Session/DrawerWidgets.d.ts +32 -1
  14. package/esm/Session/DrawerWidgets.js +71 -11
  15. package/esm/Session/MultipleViews.d.ts +85 -333
  16. package/esm/Session/MultipleViews.js +50 -10
  17. package/esm/Session/Preferences.d.ts +53 -329
  18. package/esm/Session/Preferences.js +6 -2
  19. package/esm/Session/SessionTracks.d.ts +180 -396
  20. package/esm/Session/SessionTracks.js +291 -238
  21. package/esm/Session/Themes.d.ts +79 -30
  22. package/esm/Session/Themes.js +153 -40
  23. package/esm/Session/TrackMenu.d.ts +4 -28
  24. package/esm/Session/TrackMenu.js +28 -53
  25. package/esm/Session/TrackMenuSessionMixin.js +2 -1
  26. package/esm/Session/Tracks.d.ts +75 -341
  27. package/esm/Session/Tracks.js +192 -133
  28. package/esm/Session/copyTrackSnapshot.d.ts +21 -0
  29. package/esm/Session/copyTrackSnapshot.js +20 -0
  30. package/esm/Session/developerMode.d.ts +7 -0
  31. package/esm/Session/developerMode.js +43 -0
  32. package/esm/Session/finalizeSession.d.ts +1 -2
  33. package/esm/Session/finalizeSession.js +1 -2
  34. package/esm/Session/hydratedForms.d.ts +16 -0
  35. package/esm/Session/hydratedForms.js +13 -0
  36. package/esm/Session/index.d.ts +8 -3
  37. package/esm/Session/index.js +6 -3
  38. package/esm/Session/pluginExtraTrackItems.d.ts +11 -0
  39. package/esm/Session/pluginExtraTrackItems.js +13 -0
  40. package/esm/Session/readdedTrackConf.d.ts +8 -0
  41. package/esm/Session/readdedTrackConf.js +24 -0
  42. package/esm/Session/shareableSnapshot.d.ts +6 -60
  43. package/esm/Session/shareableSnapshot.js +8 -138
  44. package/esm/Session/temporaryAssemblyTracks.d.ts +4 -3
  45. package/esm/Session/temporaryAssemblyTracks.js +6 -7
  46. package/esm/controllerTracks.d.ts +31 -11
  47. package/esm/controllerTracks.js +63 -15
  48. package/esm/embeddedController.d.ts +57 -0
  49. package/esm/embeddedController.js +100 -0
  50. package/esm/index.d.ts +12 -11
  51. package/esm/index.js +9 -9
  52. package/esm/loadPlugins.d.ts +8 -3
  53. package/esm/loadPlugins.js +11 -4
  54. package/esm/observeSession.d.ts +4 -5
  55. package/esm/observeSession.js +3 -4
  56. package/esm/parallelChunkLoading.d.ts +30 -0
  57. package/esm/parallelChunkLoading.js +71 -0
  58. package/esm/pruneUnbuildableNodes.js +18 -24
  59. package/esm/resolveAssemblies.d.ts +36 -12
  60. package/esm/resolveAssemblies.js +38 -16
  61. package/esm/rpcWorker.d.ts +11 -5
  62. package/esm/rpcWorker.js +41 -16
  63. package/esm/sessionMigrations/index.d.ts +10 -48
  64. package/esm/sessionMigrations/index.js +116 -370
  65. package/esm/sessionUrl.d.ts +7 -8
  66. package/esm/sessionUrl.js +27 -14
  67. package/esm/sessionUtils.d.ts +6 -1
  68. package/esm/sessionUtils.js +78 -92
  69. package/esm/ui/AboutDialog.d.ts +10 -2
  70. package/esm/ui/AboutDialog.js +11 -2
  71. package/esm/ui/AboutDialogContents.js +5 -29
  72. package/esm/ui/AssemblyInfoPanel.js +4 -10
  73. package/esm/ui/DescriptionPanel.d.ts +3 -0
  74. package/esm/ui/DescriptionPanel.js +62 -0
  75. package/esm/ui/FileInfoPanel.js +12 -11
  76. package/esm/ui/GeneralPreferencesTab.d.ts +17 -0
  77. package/esm/ui/GeneralPreferencesTab.js +34 -0
  78. package/esm/ui/HeaderButtons.d.ts +1 -6
  79. package/esm/ui/HeaderButtons.js +5 -12
  80. package/esm/ui/ModalWidget.d.ts +2 -1
  81. package/esm/ui/ModalWidget.js +18 -3
  82. package/esm/ui/PreferenceCheckbox.d.ts +6 -0
  83. package/esm/ui/PreferenceCheckbox.js +16 -0
  84. package/esm/ui/PreferencesDialog.d.ts +5 -27
  85. package/esm/ui/PreferencesDialog.js +29 -119
  86. package/esm/ui/RefNameInfoDialog.js +13 -25
  87. package/esm/ui/ViewPreferencesTab.d.ts +12 -0
  88. package/esm/ui/ViewPreferencesTab.js +15 -0
  89. package/esm/ui/aboutTestUtils.d.ts +140 -0
  90. package/esm/ui/aboutTestUtils.js +25 -0
  91. package/esm/ui/descriptionHtml.d.ts +47 -0
  92. package/esm/ui/descriptionHtml.js +117 -0
  93. package/esm/ui/index.d.ts +2 -0
  94. package/esm/ui/index.js +1 -0
  95. package/esm/ui/preferencesReset.d.ts +18 -0
  96. package/esm/ui/preferencesReset.js +69 -0
  97. package/esm/ui/util.d.ts +9 -27
  98. package/esm/ui/util.js +12 -31
  99. package/esm/useEngineLifecycle.d.ts +7 -1
  100. package/esm/useEngineLifecycle.js +24 -2
  101. package/package.json +11 -10
  102. package/esm/scheduleDetachedDestroy.d.ts +0 -18
  103. package/esm/scheduleDetachedDestroy.js +0 -24
  104. package/esm/ui/DisplayDefaultsSection.d.ts +0 -36
  105. package/esm/ui/DisplayDefaultsSection.js +0 -65
@@ -4,6 +4,7 @@ import type PluginManager from '@jbrowse/core/PluginManager';
4
4
  import type { BaseAssemblyConfigModel } from '@jbrowse/core/assemblyManager';
5
5
  import type { AnyConfigurationSchemaType } from '@jbrowse/core/configuration';
6
6
  import type { AnimationMode, DialogComponentType, TrackConfigChange } from '@jbrowse/core/util';
7
+ import type { HighlightType } from '@jbrowse/core/util/highlights';
7
8
  import type { IAnyStateTreeNode, Instance } from '@jbrowse/mobx-state-tree';
8
9
  type DoneCallback = (doneCallback: () => void) => [DialogComponentType, Record<string, unknown>];
9
10
  /**
@@ -28,22 +29,31 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
28
29
  focusedViewId: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
29
30
  /**
30
31
  * #property
31
- * one session-wide toggle for all region highlight bands (URL/view
32
- * highlights and bookmark overlays)
32
+ * translucent bands over genomic regions; every view draws the ones on
33
+ * its own assemblies
34
+ */
35
+ highlights: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IType<HighlightType, HighlightType, HighlightType>>, [undefined]>;
36
+ /**
37
+ * #property
33
38
  */
34
39
  highlightsVisible: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
40
+ /**
41
+ * #property
42
+ * whether a band's label is written on it
43
+ */
44
+ highlightLabelsVisible: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
35
45
  /**
36
46
  * #property
37
47
  * views, tracks, displays and widgets `pruneUnbuildableNodes` took out of
38
48
  * the tree because this build has no plugin for their type, each with the
39
- * anchor that puts it back. Opaque here on purpose — nothing in the
40
- * session reads it, and the shape belongs to that module.
49
+ * anchor that puts it back. Nothing in the session reads its contents,
50
+ * and `pruneUnbuildableNodes` defines their structure.
41
51
  *
42
- * It is a **declared property** rather than data riding along in the
43
- * snapshot because MST silently drops an undeclared key: without this
44
- * line the held nodes survive the prune and then vanish the moment
45
- * `setSession` builds the tree, and every unit test of the prune still
46
- * passes. `heldNodesSurviveTheSession.test.ts` is the canary.
52
+ * It is a **declared property** because MST drops undeclared snapshot
53
+ * keys without error. Without this declaration the held nodes survive
54
+ * the prune and are then discarded when `setSession` builds the tree,
55
+ * while every unit test of the prune still passes.
56
+ * `heldNodesSurviveTheSession.test.ts` tests that they survive.
47
57
  */
48
58
  heldForMissingPlugins: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IType<HeldNode[] | undefined, HeldNode[] | undefined, HeldNode[] | undefined>, [undefined]>;
49
59
  }, never>, {
@@ -75,23 +85,15 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
75
85
  *
76
86
  * An `observable.map` (not a plain object reassigned wholesale) so each
77
87
  * preference is its own tracked key: writing one (`setScrollZoom`) can't
78
- * invalidate a reader of another (`getDisplayTypeDefault` in a track's
79
- * `rpcProps`). A single spread-replaced object made every setter wake
80
- * every reader, so toggling scroll-to-zoom re-fetched every track. For the
81
- * same reason each promoted per-display-type default is a flat composite
82
- * key (see `displayTypeDefaultKey`), not a single nested `displayTypeDefaults`
83
- * object — promoting one default can't wake readers of a different one.
88
+ * invalidate a reader of another. A single spread-replaced object made
89
+ * every setter wake every reader, so toggling scroll-to-zoom re-fetched
90
+ * every track.
84
91
  *
85
- * `deep: false` is load-bearing, not a micro-optimization. The default
86
- * enhancer wraps an object/array value in a MobX Proxy on `set`, and a
87
- * promoted default is handed straight back out by `getConf` — so an
88
- * object-valued promotable slot (alignments `colorBy`) put a Proxy into
89
- * `rpcProps()`, and V8's structured-clone serializer rejects a Proxy:
90
- * `worker.postMessage` threw `DataCloneError` on the next fetch of any
91
- * track following that default (electron IPC and `structuredClone` in the
92
- * share bake likewise). The map still notifies per key on `set`, so shallow
93
- * values lose no reactivity — and nothing can mutate a preference in place,
94
- * because `setPreferenceOverride` freezes what it stores.
92
+ * `deep: false` keeps an object-valued preference a plain object rather
93
+ * than a MobX Proxy, which V8's structured-clone serializer rejects. The
94
+ * map still notifies per key on `set`, so shallow values lose no
95
+ * reactivity — and nothing can mutate a preference in place, because
96
+ * `setPreferenceOverride` freezes what it stores.
95
97
  */
96
98
  preferencesOverrides: import("mobx").ObservableMap<string, unknown>;
97
99
  } & {
@@ -152,37 +154,10 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
152
154
  getPreference(key: string): unknown;
153
155
  /**
154
156
  * #method
155
- * resolved value of a per-display-type slot default the user promoted
156
- * (see `setDisplayTypeDefault`); undefined when nothing was promoted.
157
- */
158
- getDisplayTypeDefault(displayType: string, slot: string): unknown;
159
- /**
160
- * #method
161
- * every per-display-type default the user has promoted, as
162
- * `{ displayType, slot, value }` — the inventory the Preferences dialog
163
- * lists and clears one at a time (`setDisplayTypeDefault(…, undefined)`).
164
- *
165
- * Here rather than filtered out of `getPreferenceChanges` by the dialog,
166
- * because the composite-key layout is this file's: a consumer that
167
- * recognized these rows by matching the path head is exactly the coupling
168
- * `DISPLAY_TYPE_DEFAULTS_PATH_HEAD` stopped being exported over, where a
169
- * rename on one side alone silently no-ops the other.
170
- */
171
- getDisplayTypeDefaults(): {
172
- displayType: string;
173
- slot: string;
174
- value: unknown;
175
- }[];
176
- /**
177
- * #method
178
- * every runtime preference-override that currently differs from its
179
- * config/admin default, as `{ path, from, to }` rows — the exact set
180
- * `clearPreferenceOverrides` reverts. Backs the confirmation diff shown
181
- * before "Reset to defaults" (mirrors the per-track changes dialog). A
182
- * scalar pref (animationMode, scrollZoom) whose override equals the
183
- * default is omitted (reverting it is a no-op); each promoted
184
- * per-display-type default is always a difference from the un-promoted
185
- * state, so `from` reads "(default)".
157
+ * every scalar preference override that currently differs from its
158
+ * config/admin default, as `{ path, from, to }` rows whose path is the
159
+ * override's own key. A scalar pref (animationMode, scrollZoom) whose
160
+ * override equals the default is omitted, since reverting it is a no-op.
186
161
  */
187
162
  getPreferenceChanges(): TrackConfigChange[];
188
163
  } & {
@@ -229,15 +204,31 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
229
204
  setHovered(thing: unknown): void;
230
205
  /**
231
206
  * #action
232
- * toggle all region highlight bands across every view
233
207
  */
234
208
  setHighlightsVisible(arg: boolean): void;
235
209
  /**
236
210
  * #action
237
- * turn highlight bands back on, so a newly made highlight or bookmark is
238
- * never silently swallowed by an earlier "highlights off"
239
211
  */
240
- revealHighlights(): void;
212
+ setHighlightLabelsVisible(arg: boolean): void;
213
+ /**
214
+ * #action
215
+ * an identical entry is not added twice, so a spec launched again does not
216
+ * stack its bands. Adding shows the bands again, since a highlight made
217
+ * while they are off would otherwise read as nothing happening
218
+ */
219
+ addHighlight(highlight: HighlightType): void;
220
+ /**
221
+ * #action
222
+ */
223
+ removeHighlight(highlight: HighlightType): void;
224
+ /**
225
+ * #action
226
+ */
227
+ updateHighlight(old: HighlightType, updates: Partial<HighlightType>): void;
228
+ /**
229
+ * #action
230
+ */
231
+ setHighlights(highlights: HighlightType[]): void;
241
232
  /**
242
233
  * #action
243
234
  * set a runtime user-preference override (see `getPreference`). Mutates
@@ -248,10 +239,9 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
248
239
  setPreferenceOverride(key: string, value: unknown): void;
249
240
  /**
250
241
  * #action
251
- * clear every runtime preference override at once — scrollZoom,
252
- * animationMode, and every promoted per-display-type default (see
253
- * `setDisplayTypeDefault`) — so each falls back to its config/admin
254
- * default. Backs the Preferences dialog "Reset to defaults" button.
242
+ * clear every runtime preference override at once, so each falls back to
243
+ * its config/admin default. Backs the Preferences dialog "Reset to
244
+ * defaults" button.
255
245
  */
256
246
  clearPreferenceOverrides(): void;
257
247
  /**
@@ -261,32 +251,11 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
261
251
  * Preferences dialog "Reset to defaults" confirmation.
262
252
  */
263
253
  clearPreferenceOverride(key: string): void;
264
- /**
265
- * #action
266
- * revert one row emitted by `getPreferenceChanges`, addressed by its
267
- * display `path`. Backs the per-entry reset in the Preferences dialog's
268
- * "Reset to defaults" confirmation.
269
- *
270
- * Lives here rather than in that dialog because this model owns both path
271
- * shapes it has to undo: a promoted per-display-type default, whose row
272
- * path is a readable label over a flat composite storage key, and every
273
- * other override, whose path *is* its key. The dialog used to re-derive
274
- * the first case from an exported path-head constant.
275
- */
276
- resetPreferenceChange(path: string[]): void;
277
254
  /**
278
255
  * #action
279
256
  * set the global scroll-to-zoom preference (see the `scrollZoom` getter)
280
257
  */
281
258
  setScrollZoom(flag: boolean): void;
282
- /**
283
- * #action
284
- * promote (or, with `value` undefined, clear) a per-display-type slot
285
- * default. Just a preference override under one flat composite key (see
286
- * `displayTypeDefaultKey`), so it persists and independently tracks like
287
- * any other pref, and clearing deletes only that key.
288
- */
289
- setDisplayTypeDefault(displayType: string, slot: string, value: unknown): void;
290
259
  /**
291
260
  * #action
292
261
  */
@@ -310,6 +279,10 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
310
279
  * #action
311
280
  */
312
281
  queueDialog(doneCallback: DoneCallback): void;
282
+ /**
283
+ * #action
284
+ */
285
+ removeDialog(entry: [DialogComponentType, Record<string, unknown>]): void;
313
286
  } & {
314
287
  snackbarMessages: import("mobx").IObservableArray<import("@jbrowse/core/ui/SnackbarModel").SnackbarMessage>;
315
288
  errorDialog: import("@jbrowse/core/ui/SnackbarModel").ErrorDialogState | undefined;
@@ -322,6 +295,8 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
322
295
  pushSnackbarMessage(message: string, level?: import("@jbrowse/core/util").NotificationLevel, actions?: import("@jbrowse/core/util").SnackAction[]): void;
323
296
  popSnackbarMessage(): import("@jbrowse/core/ui/SnackbarModel").SnackbarMessage | undefined;
324
297
  removeSnackbarMessage(message: string): void;
298
+ } & {
299
+ afterAttach(): void;
325
300
  }, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
326
301
  /** Session mixin MST type for the most basic session */
327
302
  export type BaseSessionType = ReturnType<typeof BaseSessionModel>;
@@ -1,55 +1,16 @@
1
- import { getConf } from '@jbrowse/core/configuration';
1
+ import { readConfObject } from '@jbrowse/core/configuration';
2
2
  import SnackbarModel from '@jbrowse/core/ui/SnackbarModel';
3
3
  import { setNumberGrouping } from '@jbrowse/core/util';
4
4
  import { freezeDeep } from '@jbrowse/core/util/freezeDeep';
5
+ import { isSameHighlight } from '@jbrowse/core/util/highlights';
5
6
  import { isFeature, unwrapFeature } from '@jbrowse/core/util/simpleFeature';
6
7
  import { ElementId } from '@jbrowse/core/util/types/mst';
7
- import { getParent, isStateTreeNode, types } from '@jbrowse/mobx-state-tree';
8
+ import { cast, getParent, isStateTreeNode, types, } from '@jbrowse/mobx-state-tree';
8
9
  import { observable } from 'mobx';
10
+ import { applyDeveloperMode } from './developerMode.js';
9
11
  function isAnimationMode(val) {
10
12
  return val === 'system' || val === 'enabled' || val === 'disabled';
11
13
  }
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
14
  /**
54
15
  * #stateModel BaseSessionModel
55
16
  *
@@ -74,22 +35,31 @@ export function BaseSessionModel(_pluginManager) {
74
35
  focusedViewId: types.maybe(types.string),
75
36
  /**
76
37
  * #property
77
- * one session-wide toggle for all region highlight bands (URL/view
78
- * highlights and bookmark overlays)
38
+ * translucent bands over genomic regions; every view draws the ones on
39
+ * its own assemblies
40
+ */
41
+ highlights: types.stripDefault(types.array(types.frozen()), []),
42
+ /**
43
+ * #property
79
44
  */
80
45
  highlightsVisible: types.stripDefault(types.boolean, true),
46
+ /**
47
+ * #property
48
+ * whether a band's label is written on it
49
+ */
50
+ highlightLabelsVisible: types.stripDefault(types.boolean, true),
81
51
  /**
82
52
  * #property
83
53
  * views, tracks, displays and widgets `pruneUnbuildableNodes` took out of
84
54
  * 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.
55
+ * anchor that puts it back. Nothing in the session reads its contents,
56
+ * and `pruneUnbuildableNodes` defines their structure.
87
57
  *
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.
58
+ * It is a **declared property** because MST drops undeclared snapshot
59
+ * keys without error. Without this declaration the held nodes survive
60
+ * the prune and are then discarded when `setSession` builds the tree,
61
+ * while every unit test of the prune still passes.
62
+ * `heldNodesSurviveTheSession.test.ts` tests that they survive.
93
63
  */
94
64
  heldForMissingPlugins: types.stripDefault(types.frozen(), undefined),
95
65
  })
@@ -124,23 +94,15 @@ export function BaseSessionModel(_pluginManager) {
124
94
  *
125
95
  * An `observable.map` (not a plain object reassigned wholesale) so each
126
96
  * 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.
97
+ * invalidate a reader of another. A single spread-replaced object made
98
+ * every setter wake every reader, so toggling scroll-to-zoom re-fetched
99
+ * every track.
133
100
  *
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.
101
+ * `deep: false` keeps an object-valued preference a plain object rather
102
+ * than a MobX Proxy, which V8's structured-clone serializer rejects. The
103
+ * map still notifies per key on `set`, so shallow values lose no
104
+ * reactivity — and nothing can mutate a preference in place, because
105
+ * `setPreferenceOverride` freezes what it stores.
144
106
  */
145
107
  preferencesOverrides: observable.map(undefined, {
146
108
  deep: false,
@@ -216,7 +178,8 @@ export function BaseSessionModel(_pluginManager) {
216
178
  * `defaultUseWorkspaces`).
217
179
  */
218
180
  getPreferenceDefault(key) {
219
- return getConf(self, ['preferences', key]);
181
+ const { preferences } = self.configuration;
182
+ return preferences ? readConfObject(preferences, key) : undefined;
220
183
  },
221
184
  /**
222
185
  * #method
@@ -232,70 +195,22 @@ export function BaseSessionModel(_pluginManager) {
232
195
  },
233
196
  /**
234
197
  * #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)".
198
+ * every scalar preference override that currently differs from its
199
+ * config/admin default, as `{ path, from, to }` rows whose path is the
200
+ * override's own key. A scalar pref (animationMode, scrollZoom) whose
201
+ * override equals the default is omitted, since reverting it is a no-op.
273
202
  */
274
203
  getPreferenceChanges() {
275
204
  const changes = [];
276
205
  for (const [key, value] of self.preferencesOverrides.entries()) {
277
- const dtd = parseDisplayTypeDefaultKey(key);
278
- if (dtd) {
206
+ const dflt = this.getPreferenceDefault(key);
207
+ if (value !== dflt) {
279
208
  changes.push({
280
- path: [
281
- DISPLAY_TYPE_DEFAULTS_PATH_HEAD,
282
- dtd.displayType,
283
- dtd.slot,
284
- ],
285
- from: undefined,
209
+ path: [key],
210
+ from: dflt,
286
211
  to: value,
287
212
  });
288
213
  }
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
214
  }
300
215
  return changes;
301
216
  },
@@ -366,19 +281,53 @@ export function BaseSessionModel(_pluginManager) {
366
281
  },
367
282
  /**
368
283
  * #action
369
- * toggle all region highlight bands across every view
370
284
  */
371
285
  setHighlightsVisible(arg) {
372
286
  self.highlightsVisible = arg;
373
287
  },
374
288
  /**
375
289
  * #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
290
  */
379
- revealHighlights() {
291
+ setHighlightLabelsVisible(arg) {
292
+ self.highlightLabelsVisible = arg;
293
+ },
294
+ /**
295
+ * #action
296
+ * an identical entry is not added twice, so a spec launched again does not
297
+ * stack its bands. Adding shows the bands again, since a highlight made
298
+ * while they are off would otherwise read as nothing happening
299
+ */
300
+ addHighlight(highlight) {
301
+ if (!self.highlights.some(h => isSameHighlight(h, highlight))) {
302
+ self.highlights.push(highlight);
303
+ }
380
304
  self.highlightsVisible = true;
381
305
  },
306
+ /**
307
+ * #action
308
+ */
309
+ removeHighlight(highlight) {
310
+ self.highlights.remove(highlight);
311
+ },
312
+ /**
313
+ * #action
314
+ */
315
+ updateHighlight(old, updates) {
316
+ const idx = self.highlights.indexOf(old);
317
+ if (idx !== -1) {
318
+ self.highlights[idx] = { ...old, ...updates };
319
+ self.highlightsVisible = true;
320
+ }
321
+ },
322
+ /**
323
+ * #action
324
+ */
325
+ setHighlights(highlights) {
326
+ self.highlights = cast(highlights);
327
+ if (highlights.length) {
328
+ self.highlightsVisible = true;
329
+ }
330
+ },
382
331
  /**
383
332
  * #action
384
333
  * set a runtime user-preference override (see `getPreference`). Mutates
@@ -391,19 +340,18 @@ export function BaseSessionModel(_pluginManager) {
391
340
  self.preferencesOverrides.delete(key);
392
341
  }
393
342
  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
343
+ // frozen because `deep: false` hands an object-valued preference
344
+ // straight back out by reference, so the "nothing mutates a
345
+ // preference in place" this store relies on is enforced rather than
346
+ // assumed
398
347
  self.preferencesOverrides.set(key, freezeDeep(value));
399
348
  }
400
349
  },
401
350
  /**
402
351
  * #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.
352
+ * clear every runtime preference override at once, so each falls back to
353
+ * its config/admin default. Backs the Preferences dialog "Reset to
354
+ * defaults" button.
407
355
  */
408
356
  clearPreferenceOverrides() {
409
357
  self.preferencesOverrides.clear();
@@ -417,27 +365,6 @@ export function BaseSessionModel(_pluginManager) {
417
365
  clearPreferenceOverride(key) {
418
366
  self.preferencesOverrides.delete(key);
419
367
  },
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
368
  /**
442
369
  * #action
443
370
  * set the global scroll-to-zoom preference (see the `scrollZoom` getter)
@@ -445,16 +372,6 @@ export function BaseSessionModel(_pluginManager) {
445
372
  setScrollZoom(flag) {
446
373
  this.setPreferenceOverride('scrollZoom', flag);
447
374
  },
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
375
  /**
459
376
  * #action
460
377
  */
@@ -484,13 +401,30 @@ export function BaseSessionModel(_pluginManager) {
484
401
  * #action
485
402
  */
486
403
  queueDialog(doneCallback) {
487
- const [component, props] = doneCallback(() => {
488
- this.removeActiveDialog();
404
+ // its own entry, not the head: a late second call must not close the
405
+ // dialog queued behind it
406
+ const entry = doneCallback(() => {
407
+ this.removeDialog(entry);
489
408
  });
490
- self.queueOfDialogs = [...self.queueOfDialogs, [component, props]];
409
+ self.queueOfDialogs = [...self.queueOfDialogs, entry];
410
+ },
411
+ /**
412
+ * #action
413
+ */
414
+ removeDialog(entry) {
415
+ self.queueOfDialogs = self.queueOfDialogs.filter(d => d !== entry);
416
+ },
417
+ }));
418
+ return types.compose(baseModel, SnackbarModel()).actions(self => ({
419
+ // after the compose rather than in the afterAttach above, because the
420
+ // notices land in the snackbar this line is the first place to hold both
421
+ // halves of. Web and desktop reach it a second time once the user's stored
422
+ // overrides are loaded (`PreferencesSessionMixin`), and our MST fork
423
+ // auto-chains both hooks.
424
+ afterAttach() {
425
+ applyDeveloperMode(self);
491
426
  },
492
427
  }));
493
- return types.compose(baseModel, SnackbarModel());
494
428
  }
495
429
  /** Type guard for BaseSession */
496
430
  export function isBaseSession(thing) {
@@ -111,9 +111,10 @@ export declare function ConnectionManagementSessionMixin(pluginManager: PluginMa
111
111
  pruneConnectionTrackConfig(trackId: string): void;
112
112
  /**
113
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
114
+ * Lazily establish a single connection by id if it isn't already live;
115
+ * the track selector calls it when the connection's category is
116
+ * expanded. Fetches without launching a view or showing a success
117
+ * snackbar; already-open tracks keep
117
118
  * rendering from `connectionTrackConfigs` meanwhile. Idempotent.
118
119
  */
119
120
  hydrateConnection(connectionId: string): void;
@@ -189,9 +189,10 @@ export function ConnectionManagementSessionMixin(pluginManager) {
189
189
  },
190
190
  /**
191
191
  * #action
192
- * Lazily establish a single connection by id if it isn't already live —
193
- * used when its category is expanded in the track selector. Fetches
194
- * silently (no view launch / success snackbar); already-open tracks keep
192
+ * Lazily establish a single connection by id if it isn't already live;
193
+ * the track selector calls it when the connection's category is
194
+ * expanded. Fetches without launching a view or showing a success
195
+ * snackbar; already-open tracks keep
195
196
  * rendering from `connectionTrackConfigs` meanwhile. Idempotent.
196
197
  */
197
198
  hydrateConnection(connectionId) {