@jbrowse/core 5.0.0-beta.1 → 5.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +112 -43
  2. package/esm/BaseFeatureWidget/BaseFeatureDetail/FeatureDetails.d.ts +2 -3
  3. package/esm/BaseFeatureWidget/BaseFeatureDetail/ParentFeatureLine.d.ts +4 -0
  4. package/esm/BaseFeatureWidget/BaseFeatureDetail/ParentFeatureLine.js +17 -0
  5. package/esm/BaseFeatureWidget/BaseFeatureDetail/index.js +4 -3
  6. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.d.ts +2 -2
  7. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +2 -2
  8. package/esm/BaseFeatureWidget/index.d.ts +1 -1
  9. package/esm/BaseFeatureWidget/stateModelFactory.d.ts +8 -1
  10. package/esm/BaseFeatureWidget/stateModelFactory.js +6 -0
  11. package/esm/BaseFeatureWidget/types.d.ts +10 -2
  12. package/esm/PluginLoader.d.ts +1 -0
  13. package/esm/PluginLoader.js +52 -8
  14. package/esm/PluginManager.d.ts +99 -17
  15. package/esm/PluginManager.js +194 -26
  16. package/esm/ReExports/BaseFeatureDetails.js +1 -1
  17. package/esm/ReExports/MuiDataGridReExports.d.ts +1 -0
  18. package/esm/ReExports/MuiDataGridReExports.js +2 -2
  19. package/esm/ReExports/barrelOnlyNames.d.ts +5 -0
  20. package/esm/ReExports/barrelOnlyNames.js +63 -0
  21. package/esm/ReExports/modules.js +31 -109
  22. package/esm/ReExports/publicTracks.d.ts +1 -0
  23. package/esm/ReExports/publicTracks.js +14 -0
  24. package/esm/ReExports/publicUi.d.ts +4 -0
  25. package/esm/ReExports/publicUi.js +33 -0
  26. package/esm/ReExports/publicUtil.d.ts +1 -1
  27. package/esm/ReExports/publicUtil.js +8 -4
  28. package/esm/ReExports/sharedModules.d.ts +96 -0
  29. package/esm/ReExports/sharedModules.js +66 -0
  30. package/esm/ReExports/uiStub.d.ts +2 -0
  31. package/esm/ReExports/uiStub.js +11 -0
  32. package/esm/ReExports/workerModules.d.ts +2 -0
  33. package/esm/ReExports/workerModules.js +15 -0
  34. package/esm/ReExports/workerNamespaceNames.d.ts +11 -0
  35. package/esm/ReExports/workerNamespaceNames.js +281 -0
  36. package/esm/configuration/configurationSchema.d.ts +5 -38
  37. package/esm/configuration/configurationSchema.js +64 -40
  38. package/esm/configuration/index.d.ts +2 -2
  39. package/esm/configuration/index.js +1 -1
  40. package/esm/configuration/promotableDefaults.d.ts +54 -19
  41. package/esm/configuration/promotableDefaults.js +117 -48
  42. package/esm/configuration/promotablePin.d.ts +50 -40
  43. package/esm/configuration/promotableResolve.d.ts +1 -1
  44. package/esm/configuration/promotableResolve.js +1 -1
  45. package/esm/configuration/tabixIndexFields.d.ts +2 -2
  46. package/esm/configuration/tabixIndexFields.js +2 -2
  47. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +12 -0
  48. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +35 -4
  49. package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +41 -0
  50. package/esm/data_adapters/BaseAdapter/featureDensity.js +32 -0
  51. package/esm/data_adapters/BaseAdapter/index.d.ts +2 -1
  52. package/esm/data_adapters/BaseAdapter/index.js +1 -0
  53. package/esm/data_adapters/BaseAdapter/stats.d.ts +33 -1
  54. package/esm/data_adapters/BaseAdapter/stats.js +52 -13
  55. package/esm/data_adapters/BaseAdapter/types.d.ts +3 -8
  56. package/esm/pluggableElementTypes/AdapterType.d.ts +2 -1
  57. package/esm/pluggableElementTypes/AdapterType.js +1 -1
  58. package/esm/pluggableElementTypes/DisplayType.d.ts +4 -12
  59. package/esm/pluggableElementTypes/DisplayType.js +3 -10
  60. package/esm/pluggableElementTypes/LazyStateModelElement.d.ts +27 -0
  61. package/esm/pluggableElementTypes/LazyStateModelElement.js +71 -0
  62. package/esm/pluggableElementTypes/ViewType.d.ts +20 -4
  63. package/esm/pluggableElementTypes/ViewType.js +32 -4
  64. package/esm/pluggableElementTypes/extendElementType.d.ts +1 -2
  65. package/esm/pluggableElementTypes/extendElementType.js +9 -5
  66. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +44 -3
  67. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +75 -2
  68. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +21 -0
  69. package/esm/pluggableElementTypes/models/BaseTrackModel.js +34 -2
  70. package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +49 -0
  71. package/esm/pluggableElementTypes/models/BaseViewModel.js +28 -0
  72. package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +14 -10
  73. package/esm/pluggableElementTypes/models/assertDisplayContract.js +25 -32
  74. package/esm/pluggableElementTypes/models/baseTrackConfig.js +7 -1
  75. package/esm/rpc/RpcRegistry.d.ts +10 -0
  76. package/esm/rpc/byteBudget.d.ts +9 -0
  77. package/esm/rpc/coreRpcMethods.d.ts +1 -0
  78. package/esm/rpc/coreRpcMethods.js +1 -0
  79. package/esm/rpc/methods/CoreGetFeatureDensity.d.ts +11 -0
  80. package/esm/rpc/methods/CoreGetFeatureDensity.js +22 -0
  81. package/esm/svg/svgReady.d.ts +1 -0
  82. package/esm/ui/BaseExportSvgDialog.d.ts +1 -1
  83. package/esm/ui/CascadingMenu.js +30 -8
  84. package/esm/ui/ErrorMessageStackTraceDialog.js +5 -6
  85. package/esm/ui/ExportSvgDialog.d.ts +1 -1
  86. package/esm/ui/LoadingOverlay.d.ts +14 -0
  87. package/esm/ui/LoadingOverlay.js +1 -1
  88. package/esm/ui/MenuTypes.d.ts +14 -5
  89. package/esm/ui/MenuTypes.js +3 -0
  90. package/esm/ui/PaletteContext.d.ts +11 -4
  91. package/esm/ui/PaletteContext.js +34 -5
  92. package/esm/ui/PinAdornment.js +31 -43
  93. package/esm/ui/ScrollChrome.d.ts +16 -0
  94. package/esm/ui/ScrollChrome.js +12 -0
  95. package/esm/ui/ScrollZoomToggle.d.ts +4 -3
  96. package/esm/ui/ScrollZoomToggle.js +4 -3
  97. package/esm/ui/SizeSliderRow.js +1 -1
  98. package/esm/ui/index.d.ts +3 -3
  99. package/esm/ui/index.js +4 -4
  100. package/esm/ui/launchViewMenu.js +7 -1
  101. package/esm/ui/legendMenuItem.d.ts +10 -16
  102. package/esm/ui/legendMenuItem.js +9 -20
  103. package/esm/ui/makeSizeMenu.js +1 -1
  104. package/esm/ui/menuItemAdornment.d.ts +2 -3
  105. package/esm/ui/menuItemAdornment.js +2 -3
  106. package/esm/ui/menuItems.d.ts +2 -3
  107. package/esm/ui/menuItems.js +1 -2
  108. package/esm/ui/palette.d.ts +11 -11
  109. package/esm/ui/palette.js +11 -11
  110. package/esm/ui/promotablePinCoverage.d.ts +16 -2
  111. package/esm/ui/promotablePinCoverage.js +40 -3
  112. package/esm/ui/theme.js +5 -0
  113. package/esm/ui/toggleMenuItems.d.ts +14 -9
  114. package/esm/ui/toggleMenuItems.js +32 -15
  115. package/esm/util/Base1DUtils.d.ts +4 -2
  116. package/esm/util/Base1DUtils.js +12 -4
  117. package/esm/util/SvgCanvas.js +4 -3
  118. package/esm/util/adapterConfigKey.d.ts +12 -0
  119. package/esm/util/adapterConfigKey.js +14 -0
  120. package/esm/util/adapterMetadata.js +2 -1
  121. package/esm/util/addAndShowTrack.d.ts +1 -1
  122. package/esm/util/addAndShowTrack.js +5 -1
  123. package/esm/util/addTrackFromWidget.js +2 -2
  124. package/esm/util/analytics.js +4 -5
  125. package/esm/util/cacheBudgets.d.ts +22 -19
  126. package/esm/util/cacheBudgets.js +22 -19
  127. package/esm/util/clampToContig.d.ts +17 -0
  128. package/esm/util/clampToContig.js +19 -0
  129. package/esm/util/colorBits.d.ts +2 -1
  130. package/esm/util/colorBits.js +2 -5
  131. package/esm/util/createStopTokenRotation.d.ts +9 -7
  132. package/esm/util/createStopTokenRotation.js +7 -27
  133. package/esm/util/featureTypes.d.ts +24 -0
  134. package/esm/util/featureTypes.js +35 -0
  135. package/esm/util/fetchContext.d.ts +2 -2
  136. package/esm/util/fetchContext.js +2 -2
  137. package/esm/util/fetchPhases.d.ts +10 -10
  138. package/esm/util/hooks.d.ts +8 -0
  139. package/esm/util/hooks.js +27 -0
  140. package/esm/util/index.d.ts +6 -5
  141. package/esm/util/index.js +5 -4
  142. package/esm/util/installFetch.d.ts +71 -34
  143. package/esm/util/installFetch.js +57 -29
  144. package/esm/util/installInitAutorun.d.ts +14 -13
  145. package/esm/util/installInitAutorun.js +17 -17
  146. package/esm/util/installPrerequisiteFetch.d.ts +86 -0
  147. package/esm/util/installPrerequisiteFetch.js +73 -0
  148. package/esm/util/lazyAfterAttach.d.ts +13 -0
  149. package/esm/util/lazyAfterAttach.js +31 -0
  150. package/esm/util/legendCandidates.d.ts +50 -0
  151. package/esm/util/legendCandidates.js +87 -0
  152. package/esm/util/mstUtils.d.ts +12 -1
  153. package/esm/util/mstUtils.js +20 -2
  154. package/esm/util/openFeatureWidget.d.ts +5 -0
  155. package/esm/util/openFeatureWidget.js +1 -0
  156. package/esm/util/openViews.d.ts +16 -0
  157. package/esm/util/openViews.js +22 -0
  158. package/esm/util/parentWalk.d.ts +1 -1
  159. package/esm/util/parentWalk.js +1 -1
  160. package/esm/util/pluginStore.d.ts +1 -0
  161. package/esm/util/pluginStore.js +8 -1
  162. package/esm/util/range.d.ts +13 -9
  163. package/esm/util/range.js +17 -8
  164. package/esm/util/renderToStaticMarkup.js +10 -0
  165. package/esm/util/rxjs.d.ts +16 -0
  166. package/esm/util/rxjs.js +49 -0
  167. package/esm/util/scheduleDetachedDestroy.d.ts +18 -0
  168. package/esm/util/scheduleDetachedDestroy.js +24 -0
  169. package/esm/util/scrollLatch.d.ts +1 -0
  170. package/esm/util/scrollLatch.js +8 -0
  171. package/esm/util/stopToken.d.ts +6 -5
  172. package/esm/util/tabix.d.ts +10 -4
  173. package/esm/util/tabix.js +11 -5
  174. package/esm/util/trackConfigDelta.d.ts +6 -6
  175. package/esm/util/trackConfigDelta.js +3 -3
  176. package/esm/util/tracks.d.ts +48 -0
  177. package/esm/util/tracks.js +192 -66
  178. package/esm/util/types/index.d.ts +58 -10
  179. package/esm/util/types/index.js +3 -0
  180. package/esm/util/unknownSnapshotKeys.d.ts +36 -0
  181. package/esm/util/unknownSnapshotKeys.js +69 -0
  182. package/esm/util/usePanelVirtualScroll.d.ts +6 -2
  183. package/esm/util/usePanelVirtualScroll.js +6 -2
  184. package/esm/util/useRowVirtualScroll.d.ts +1 -1
  185. package/esm/util/useRowVirtualScroll.js +1 -1
  186. package/esm/util/useVirtualScrollWheel.js +11 -1
  187. package/esm/util/viewStatus.d.ts +23 -0
  188. package/esm/util/viewStatus.js +17 -0
  189. package/esm/util/wheelZoom.d.ts +3 -1
  190. package/esm/util/wheelZoom.js +39 -14
  191. package/esm/util/whenViewSettled.d.ts +17 -4
  192. package/esm/util/whenViewSettled.js +19 -6
  193. package/esm/util/withLaunchInput.d.ts +168 -0
  194. package/esm/util/withLaunchInput.js +273 -0
  195. package/package.json +19 -11
  196. package/esm/ui/promotableMenuItems.d.ts +0 -19
  197. package/esm/ui/promotableMenuItems.js +0 -89
  198. package/esm/util/applyInitSettings.d.ts +0 -79
  199. package/esm/util/applyInitSettings.js +0 -91
@@ -58,7 +58,17 @@ onWheel) {
58
58
  // not-yet-committed model read.
59
59
  const runningRef = useRef(0);
60
60
  const applyScroll = useCallback((e, { scrollTop, viewportHeight, scrollableHeight }, commit) => {
61
- if (scrollableHeight <= 0) {
61
+ // Each handler takes the gesture whose dominant axis is its own: a
62
+ // sideways swipe belongs to the view's pan, and consuming its small
63
+ // vertical component would both drift the panel and — since consuming
64
+ // means preventDefault, which is how the view knows to stay out — kill
65
+ // the pan the user actually asked for. A tie is vertical, matching the
66
+ // view's own zoom-vs-pan tie-break. Raw deltas, because both axes of one
67
+ // event share a deltaMode. `holds` is what keeps this from cutting a
68
+ // gesture in half: once the panel has latched, the sideways momentum
69
+ // trailing a vertical scroll stays with the panel.
70
+ const sideways = Math.abs(e.deltaX) > Math.abs(e.deltaY);
71
+ if (scrollableHeight <= 0 || (sideways && !latch.holds(e))) {
62
72
  return;
63
73
  }
64
74
  // A fresh frame (nothing pending) re-syncs to the model's live scrollTop;
@@ -56,3 +56,26 @@ export interface ViewStatusInputs {
56
56
  * with nothing anywhere saying why. `noRegions` is that state, named.
57
57
  */
58
58
  export declare function computeViewStatus({ error, hasSomethingToShow, loading, }: ViewStatusInputs): ViewStatus;
59
+ /**
60
+ * The slice of `AssemblyManager` this needs, spelled out rather than imported:
61
+ * the real type is an MST model a `PluginManager` built, so naming it here
62
+ * would cost this leaf the whole application's type graph (`util/CLAUDE.md`).
63
+ */
64
+ interface AssemblyErrorSource {
65
+ get: (name: string) => {
66
+ error?: unknown;
67
+ } | undefined;
68
+ }
69
+ /**
70
+ * The assembly failures behind a view's `assemblyNames`, joined, or `undefined`
71
+ * when there are none.
72
+ *
73
+ * **Undefined and not `''`**, which is the whole reason this is shared. Three
74
+ * views spelled it as a bare `.join(', ')`, and a getter that answers the empty
75
+ * string for "nothing failed" reads as an error to every `!== undefined` test —
76
+ * `DotplotView.error` folded it in with `??`, so the view reported a terminal
77
+ * error permanently and jbrowse-img refused to render any dotplot. The in-app
78
+ * readers all happened to be truthiness tests, which is why it stayed invisible.
79
+ */
80
+ export declare function assemblyErrorMessage(assemblyManager: AssemblyErrorSource, assemblyNames: string[]): string | undefined;
81
+ export {};
@@ -18,3 +18,20 @@ export function computeViewStatus({ error, hasSomethingToShow, loading, }) {
18
18
  const pending = loading();
19
19
  return pending ? { type: 'loading', ...pending } : { type: 'ready' };
20
20
  }
21
+ /**
22
+ * The assembly failures behind a view's `assemblyNames`, joined, or `undefined`
23
+ * when there are none.
24
+ *
25
+ * **Undefined and not `''`**, which is the whole reason this is shared. Three
26
+ * views spelled it as a bare `.join(', ')`, and a getter that answers the empty
27
+ * string for "nothing failed" reads as an error to every `!== undefined` test —
28
+ * `DotplotView.error` folded it in with `??`, so the view reported a terminal
29
+ * error permanently and jbrowse-img refused to render any dotplot. The in-app
30
+ * readers all happened to be truthiness tests, which is why it stayed invisible.
31
+ */
32
+ export function assemblyErrorMessage(assemblyManager, assemblyNames) {
33
+ const errors = assemblyNames
34
+ .map(name => assemblyManager.get(name)?.error)
35
+ .filter(e => !!e);
36
+ return errors.length > 0 ? errors.join(', ') : undefined;
37
+ }
@@ -17,6 +17,7 @@ export interface WheelZoomTarget {
17
17
  views: WheelZoomView[];
18
18
  scrollZoom: boolean;
19
19
  originElement: () => HTMLElement | undefined;
20
+ hold?: <T>(fn: () => T) => T;
20
21
  }
21
22
  export interface WheelZoomControllerOptions {
22
23
  element: HTMLElement;
@@ -37,9 +38,10 @@ export interface WheelZoomControllerOptions {
37
38
  /**
38
39
  * The one wheel gesture handler for linear views. Its decision matrix:
39
40
  *
41
+ * - a wheel a nested scroller already consumed → declined, it owns the gesture
40
42
  * - ctrl/meta+wheel, or (scrollZoom && |dy| >= |dx|) → zoom about the cursor
41
43
  * - shift+wheel while scrollZoom is on → declined, we pass
42
- * - a horizontal delta, unless one arrives mid-zoom → pan by dx
44
+ * - a sideways delta, unless one arrives mid-zoom → pan by dx
43
45
  *
44
46
  * Declining shift is what leaves a nested panel its own scroll
45
47
  * (`usePanelVirtualScroll`) — it is not a way back to the page. Browsers turn
@@ -89,9 +89,10 @@ export function applyZoomAccum(bpPerPx, zoomAccum, elapsedMs) {
89
89
  /**
90
90
  * The one wheel gesture handler for linear views. Its decision matrix:
91
91
  *
92
+ * - a wheel a nested scroller already consumed → declined, it owns the gesture
92
93
  * - ctrl/meta+wheel, or (scrollZoom && |dy| >= |dx|) → zoom about the cursor
93
94
  * - shift+wheel while scrollZoom is on → declined, we pass
94
- * - a horizontal delta, unless one arrives mid-zoom → pan by dx
95
+ * - a sideways delta, unless one arrives mid-zoom → pan by dx
95
96
  *
96
97
  * Declining shift is what leaves a nested panel its own scroll
97
98
  * (`usePanelVirtualScroll`) — it is not a way back to the page. Browsers turn
@@ -141,26 +142,41 @@ export function createWheelZoomController({ element, resolveTarget, swallowUnhan
141
142
  s.originLeft = origin.getBoundingClientRect().left;
142
143
  }
143
144
  const originLeft = s.originLeft;
144
- transaction(() => {
145
- if (s.zoomAccum !== 0 && originLeft !== undefined) {
146
- const offset = s.lastClientX - originLeft;
147
- for (const view of target.views) {
148
- view.zoomTo(applyZoomAccum(view.bpPerPx, s.zoomAccum, elapsed), offset);
145
+ const apply = () => {
146
+ transaction(() => {
147
+ if (s.zoomAccum !== 0 && originLeft !== undefined) {
148
+ const offset = s.lastClientX - originLeft;
149
+ for (const view of target.views) {
150
+ view.zoomTo(applyZoomAccum(view.bpPerPx, s.zoomAccum, elapsed), offset);
151
+ }
149
152
  }
150
- }
151
- if (s.scrollDelta !== 0) {
152
- for (const view of target.views) {
153
- view.horizontalScroll(s.scrollDelta);
153
+ if (s.scrollDelta !== 0) {
154
+ for (const view of target.views) {
155
+ view.horizontalScroll(s.scrollDelta);
156
+ }
154
157
  }
155
- }
156
- });
158
+ });
159
+ };
160
+ if (target.hold) {
161
+ target.hold(apply);
162
+ }
163
+ else {
164
+ apply();
165
+ }
157
166
  }
158
167
  s.zoomAccum = 0;
159
168
  s.scrollDelta = 0;
160
169
  }
161
170
  function onWheel(event) {
162
171
  onEvent?.(event);
163
- const gated = presence !== undefined && !presence.isOver;
172
+ // A nested vertical scroller preventDefaults exactly what it consumed, and
173
+ // that flag is still readable on the way up, so the view declines what a
174
+ // panel is already scrolling: a trackpad emits a small deltaX alongside
175
+ // every vertical swipe, which used to pan the genome sideways under someone
176
+ // who was only scrolling reads. No timer of our own — the scroll latch
177
+ // holds the whole continuous gesture, boundary included, and releasing it
178
+ // is the pause that ends the gesture.
179
+ const gated = event.defaultPrevented || (presence !== undefined && !presence.isOver);
164
180
  const target = gated ? undefined : resolveTarget(event);
165
181
  if (target && !(event.shiftKey && target.scrollZoom)) {
166
182
  // a gap longer than the frame-elapsed clamp means this event starts a new
@@ -216,7 +232,16 @@ export function createWheelZoomController({ element, resolveTarget, swallowUnhan
216
232
  Math.abs(deltaX) > Math.abs(2 * deltaY)) {
217
233
  event.preventDefault();
218
234
  }
219
- s.scrollDelta = accumulateScroll(s.scrollDelta, deltaX);
235
+ // Pan on a gesture that is actually sideways. A trackpad puts a small
236
+ // deltaX on every vertical swipe, and panning on it drifted the genome
237
+ // under anyone scrolling a track — including where no panel handler
238
+ // reports the scroll for us to decline, as with the natively-scrolling
239
+ // pinned-tracks block. Ties go vertical, the same way the zoom test
240
+ // above resolves them. With scrollZoom on this changes nothing: a
241
+ // vertical-dominant wheel was already taken as a zoom.
242
+ if (Math.abs(deltaX) > Math.abs(deltaY)) {
243
+ s.scrollDelta = accumulateScroll(s.scrollDelta, deltaX);
244
+ }
220
245
  }
221
246
  frame.schedule(flush);
222
247
  }
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Wait until a view has either initialized or failed, and report which.
2
+ * Wait until a view has either initialized — launch blob consumed and all — or
3
+ * failed, and report which.
3
4
  *
4
5
  * `initialized` folds in a view's assemblies, so an assembly that fails to load
5
6
  * leaves it false **forever**. A bare `when(() => view.initialized)` therefore
@@ -9,6 +10,15 @@
9
10
  * export hung behind its dialog's spinner, and a launched breakpoint split view
10
11
  * that opened but was never navigated or zoomed.
11
12
  *
13
+ * `pendingLaunch` is the other half, and it matters because `initialized` can
14
+ * go true MID-launch: LGV's flips the moment displayedRegions land, while the
15
+ * same apply pass still has tracks to attach — so a caller acting there reads a
16
+ * positioned view with its tracks missing (an SVG export saves it that way).
17
+ * A launch failure cannot hang this: the state machine either clears the blob
18
+ * (materialized, reported as a snackbar) or sets the view's `error`
19
+ * (installInitAutorun's failure policy). A view with no launch machinery has no
20
+ * `pendingLaunch` and the term is vacuously satisfied.
21
+ *
12
22
  * Every view exposes a resolved `error` beside `initialized`, so waiting on the
13
23
  * pair turns the hang into something reportable. What to report is the caller's,
14
24
  * because the vocabularies genuinely differ — an export says "cannot export"
@@ -16,12 +26,15 @@
16
26
  * says the launch failed — which is why this returns the answer instead of
17
27
  * throwing one wording at both.
18
28
  *
19
- * No time bound, deliberately: `initialized || error` is a terminal pair, so a
20
- * slow-but-healthy remote assembly is waited out rather than guessed at.
29
+ * No time bound, deliberately: `(initialized && launch consumed) || error` is a
30
+ * terminal set, so a slow-but-healthy remote assembly is waited out rather than
31
+ * guessed at.
21
32
  *
22
- * @returns true if the view initialized, false if it settled on an error
33
+ * @returns true if the view initialized (and consumed any launch), false if it
34
+ * settled on an error
23
35
  */
24
36
  export declare function whenViewSettled(view: {
25
37
  initialized: boolean;
26
38
  error: unknown;
39
+ pendingLaunch?: unknown;
27
40
  }): Promise<boolean>;
@@ -1,6 +1,7 @@
1
1
  import { when } from 'mobx';
2
2
  /**
3
- * Wait until a view has either initialized or failed, and report which.
3
+ * Wait until a view has either initialized — launch blob consumed and all — or
4
+ * failed, and report which.
4
5
  *
5
6
  * `initialized` folds in a view's assemblies, so an assembly that fails to load
6
7
  * leaves it false **forever**. A bare `when(() => view.initialized)` therefore
@@ -10,6 +11,15 @@ import { when } from 'mobx';
10
11
  * export hung behind its dialog's spinner, and a launched breakpoint split view
11
12
  * that opened but was never navigated or zoomed.
12
13
  *
14
+ * `pendingLaunch` is the other half, and it matters because `initialized` can
15
+ * go true MID-launch: LGV's flips the moment displayedRegions land, while the
16
+ * same apply pass still has tracks to attach — so a caller acting there reads a
17
+ * positioned view with its tracks missing (an SVG export saves it that way).
18
+ * A launch failure cannot hang this: the state machine either clears the blob
19
+ * (materialized, reported as a snackbar) or sets the view's `error`
20
+ * (installInitAutorun's failure policy). A view with no launch machinery has no
21
+ * `pendingLaunch` and the term is vacuously satisfied.
22
+ *
13
23
  * Every view exposes a resolved `error` beside `initialized`, so waiting on the
14
24
  * pair turns the hang into something reportable. What to report is the caller's,
15
25
  * because the vocabularies genuinely differ — an export says "cannot export"
@@ -17,12 +27,15 @@ import { when } from 'mobx';
17
27
  * says the launch failed — which is why this returns the answer instead of
18
28
  * throwing one wording at both.
19
29
  *
20
- * No time bound, deliberately: `initialized || error` is a terminal pair, so a
21
- * slow-but-healthy remote assembly is waited out rather than guessed at.
30
+ * No time bound, deliberately: `(initialized && launch consumed) || error` is a
31
+ * terminal set, so a slow-but-healthy remote assembly is waited out rather than
32
+ * guessed at.
22
33
  *
23
- * @returns true if the view initialized, false if it settled on an error
34
+ * @returns true if the view initialized (and consumed any launch), false if it
35
+ * settled on an error
24
36
  */
25
37
  export async function whenViewSettled(view) {
26
- await when(() => view.initialized || !!view.error);
27
- return view.initialized;
38
+ const ready = () => view.initialized && view.pendingLaunch === undefined;
39
+ await when(() => ready() || !!view.error);
40
+ return ready();
28
41
  }
@@ -0,0 +1,168 @@
1
+ export { legacyInitMessage, unknownKeysMessage } from './unknownSnapshotKeys.ts';
2
+ import type { IAnyModelType, IModelType, SnapshotIn, SnapshotOut } from '@jbrowse/mobx-state-tree';
3
+ /**
4
+ * The slice of a PluginManager the partition reads: which view type a
5
+ * snapshot's `type` names, and that type's state model as REGISTERED — the
6
+ * one `extendViewType` may have composed more properties onto after the
7
+ * factory wrapped its own.
8
+ */
9
+ export interface ViewTypeLookup {
10
+ viewTypes: {
11
+ has: (name: string) => boolean;
12
+ };
13
+ getViewType: (name: string) => {
14
+ isStateModelLoaded: boolean;
15
+ stateModel: IAnyModelType;
16
+ };
17
+ }
18
+ export interface LaunchInputOptions<M extends IAnyModelType> {
19
+ /** the PluginManager; without it the partition sees only the wrapped model */
20
+ registry?: ViewTypeLookup;
21
+ /**
22
+ * Whether a snapshot shows the launch applied — rows built, regions set. The
23
+ * blob persists only until then: a snapshot taken mid-load (an autosave
24
+ * firing before the launch autorun navigates) has to rebuild the view rather
25
+ * than drop it to the import form, and a materialized view has nothing left
26
+ * to rebuild. `installInitAutorun` keys its failure policy off the same line,
27
+ * read from the live node. `Partial` because a `stripDefault` property is
28
+ * absent from the snapshot when empty, whatever its declared type says.
29
+ */
30
+ materialized: (snap: Partial<SnapshotOut<M>>) => boolean;
31
+ }
32
+ /**
33
+ * How a launch key's value is told apart from the state property of the same
34
+ * name. A key whose name collides with nothing is `launch`; the rest name the
35
+ * discriminator that splits one authored array into the recipe entries the
36
+ * launcher resolves and the built snapshots MST restores.
37
+ *
38
+ * `replay` is the one that is not remapped at all: the value lands on its
39
+ * property, and a copy rides in the blob so the launcher can run the ordered
40
+ * imperative step the property write alone does not produce.
41
+ */
42
+ export type LaunchKeyKind = 'launch' | 'trackEntries' | 'rows' | 'highlightEntries' | 'replay';
43
+ export interface LaunchKeySpec {
44
+ kind: LaunchKeyKind;
45
+ }
46
+ export interface LaunchKeyRegistration<Commands, PassThrough extends string = never> {
47
+ keys: Record<string, LaunchKeySpec>;
48
+ passThrough: readonly PassThrough[];
49
+ /** never assigned; it carries `Commands` to `withLaunchInput`'s return type */
50
+ commands?: Commands;
51
+ }
52
+ /**
53
+ * The launch keys a view captured, plus what it has to report.
54
+ *
55
+ * `Commands` is not made partial: hand-authored JSON is what fills this, so a
56
+ * key the interface marks required is not a guarantee here either way, and the
57
+ * launcher already guards the ones it cannot proceed without.
58
+ */
59
+ export type LaunchInput<Commands> = Commands & {
60
+ unknown?: Record<string, unknown>;
61
+ malformed?: Record<string, unknown>;
62
+ legacyInit?: boolean;
63
+ };
64
+ /**
65
+ * The blob when it still holds something to apply, else undefined. A view's
66
+ * gates ask this rather than the raw property: a snapshot whose only launch
67
+ * content was a typo has nothing to launch, and a view that thinks otherwise
68
+ * waits on an assembly nobody named and never leaves the spinner.
69
+ *
70
+ * Returns the blob itself, never a copy — the launch autorun clears by
71
+ * identity, and a fresh object per read strands the init it just applied.
72
+ */
73
+ export declare function pendingLaunch<C>(launch: LaunchInput<C> | undefined): LaunchInput<C> | undefined;
74
+ /**
75
+ * A view's snapshot type, widened to the authored shape: every launch key is
76
+ * accepted beside the declared properties, and a key that is both takes either
77
+ * meaning. Excess-property checking then makes a misspelled key an error at
78
+ * every literal site — a `defaultSession` view, an `addView` call, a
79
+ * `createViewState` spec.
80
+ */
81
+ export type LaunchSnapshotIn<M extends IAnyModelType, Commands, PassThrough extends string = never> = Omit<SnapshotIn<M>, keyof Commands> & {
82
+ [K in keyof Commands]?: K extends keyof SnapshotIn<M> ? Commands[K] | SnapshotIn<M>[K] : Commands[K];
83
+ } & {
84
+ [K in PassThrough]?: unknown;
85
+ } & {
86
+ /**
87
+ * @deprecated v4's nesting. Write every setting directly on the view object;
88
+ * this is unwrapped on the way in and warns.
89
+ */
90
+ init?: Commands;
91
+ };
92
+ type ReservedKey = 'id' | 'type' | 'launch';
93
+ /**
94
+ * What a view can be launched with, derived: the keys its launcher interprets
95
+ * (`Commands`) plus every declared property of its state model, optional, in
96
+ * the property's own snapshot type. Nothing is restated, so a property is
97
+ * authorable — and TYPED — from the line that declares it.
98
+ *
99
+ * ```ts
100
+ * export type LinearSyntenyViewInit = ViewInit<
101
+ * LinearSyntenyViewStateModel,
102
+ * LinearSyntenyViewCommands
103
+ * >
104
+ * ```
105
+ *
106
+ * The twin of `LaunchSnapshotIn` above, from the other end: that one widens a
107
+ * snapshot type for a literal MST site, this one is what a caller BUILDING a
108
+ * spec annotates, so its commands stay required and its properties optional.
109
+ */
110
+ export type ViewInit<M extends IAnyModelType, Commands> = Commands & Partial<Omit<SnapshotIn<M>, keyof Commands | ReservedKey>>;
111
+ export type LaunchInputModel<M extends IAnyModelType, Commands, PassThrough extends string = never> = M extends IModelType<infer P, infer O, any, infer S> ? IModelType<P, O, LaunchSnapshotIn<M, Commands, PassThrough>, S> : never;
112
+ /**
113
+ * Declare one view's launch keys. The argument is
114
+ * `Record<keyof Commands, LaunchKeySpec>`, so a command the view interprets and
115
+ * nobody registered is a compile error rather than a key that partitions as a
116
+ * typo and warns.
117
+ *
118
+ * `passThrough` names keys that are neither a launch key nor a declared
119
+ * property and still belong on the snapshot — a legacy spelling the view's own
120
+ * `preProcessSnapshot` converts.
121
+ */
122
+ export declare function defineLaunchKeys<Commands>(): <const P extends readonly string[] = readonly never[]>(keys: Record<keyof Commands, LaunchKeySpec>, { passThrough }?: {
123
+ passThrough?: P;
124
+ }) => LaunchKeyRegistration<Commands, P[number]>;
125
+ /**
126
+ * The part of a launch spec that belongs on the snapshot: everything but the
127
+ * launch keys, and the identity and `launch` keys no spec may set. For a caller
128
+ * that applies the launch itself and builds the view from a snapshot — a
129
+ * synteny row, whose parent navigates it and opens its tracks — so what it
130
+ * hands MST is settings only, and the view's own partition sorts those into
131
+ * properties and reported typos.
132
+ */
133
+ export declare function snapshotSettings(spec: Record<string, unknown>, registration: LaunchKeyRegistration<unknown, string> | undefined): {
134
+ [k: string]: unknown;
135
+ };
136
+ /**
137
+ * Accept a view's settings written directly on the view object, and move the
138
+ * ones a launcher has to resolve into the internal `launch` property.
139
+ *
140
+ * The partition is a `preProcessSnapshot` and is PURE. A session's view type is
141
+ * a `types.union`, so MST runs every member's preprocessor against every
142
+ * candidate snapshot while deciding which one matches, and runs it about twice
143
+ * more per instantiation; a warning from in here fires against snapshots that
144
+ * are about to be rejected. `afterAttach` — reached only by a snapshot that won
145
+ * — reports what the partition captured.
146
+ *
147
+ * `registry` is the PluginManager. With it the partition classifies against
148
+ * the view type as registered, `extendViewType`'s added properties included;
149
+ * without it, against the model as wrapped here. `materialized` draws the line
150
+ * past which the blob is no longer persisted; see `LaunchInputOptions`.
151
+ *
152
+ * ORDER: MST runs preprocessors in the reverse of the order they were added, so
153
+ * this belongs on the chain BEFORE a view's own legacy-key preprocessor, where
154
+ * it partitions the snapshot MST finally consumes rather than a key that remap
155
+ * is about to convert.
156
+ *
157
+ * The widening cast is the LAST link that may change the creation type: it
158
+ * replaces `CustomC`, so a `.props()` added after it is invisible to
159
+ * `SnapshotIn`. Further `preProcessSnapshot`/`postProcessSnapshot` links are
160
+ * fine — both carry `CustomC` through.
161
+ *
162
+ * Not `types.snapshotProcessor`, which carries the same widened input and stops
163
+ * being a `ModelType`. `PluginManager.pluggableMstType` filters union members on
164
+ * `isModelType`, so a wrapped view is dropped from the session's view union
165
+ * without a word, and `ViewType.stateModel` and every `.properties`
166
+ * introspection site break with it.
167
+ */
168
+ export declare function withLaunchInput<M extends IAnyModelType, Commands, PassThrough extends string>(model: M, registration: LaunchKeyRegistration<Commands, PassThrough>, { registry, materialized }: LaunchInputOptions<M>): LaunchInputModel<M, Commands, PassThrough>;