@jbrowse/core 5.0.0-beta.8 → 5.0.0-beta.9

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 (379) hide show
  1. package/README.md +427 -130
  2. package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.d.ts +3 -2
  3. package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.js +5 -3
  4. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -5
  5. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +8 -9
  6. package/esm/BaseFeatureWidget/BaseFeatureDetail/CoreDetails.js +8 -13
  7. package/esm/BaseFeatureWidget/BaseFeatureDetail/FeatureDetails.d.ts +2 -1
  8. package/esm/BaseFeatureWidget/BaseFeatureDetail/FeatureDetails.js +3 -3
  9. package/esm/BaseFeatureWidget/BaseFeatureDetail/FieldActionsButton.d.ts +8 -0
  10. package/esm/BaseFeatureWidget/BaseFeatureDetail/FieldActionsButton.js +24 -0
  11. package/esm/BaseFeatureWidget/BaseFeatureDetail/SimpleField.d.ts +3 -2
  12. package/esm/BaseFeatureWidget/BaseFeatureDetail/SimpleField.js +5 -3
  13. package/esm/BaseFeatureWidget/BaseFeatureDetail/index.d.ts +1 -0
  14. package/esm/BaseFeatureWidget/BaseFeatureDetail/index.js +1 -4
  15. package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.d.ts +16 -0
  16. package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.js +53 -0
  17. package/esm/BaseFeatureWidget/BaseFeatureDetail/util.d.ts +0 -8
  18. package/esm/BaseFeatureWidget/BaseFeatureDetail/util.js +0 -16
  19. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceContents.js +7 -34
  20. package/esm/BaseFeatureWidget/configSchema.d.ts +1 -1
  21. package/esm/BaseFeatureWidget/formatDetails.d.ts +13 -29
  22. package/esm/BaseFeatureWidget/formatDetails.js +46 -79
  23. package/esm/BaseFeatureWidget/index.d.ts +1 -0
  24. package/esm/BaseFeatureWidget/stateModelFactory.d.ts +35 -28
  25. package/esm/BaseFeatureWidget/stateModelFactory.js +94 -102
  26. package/esm/BaseFeatureWidget/types.d.ts +3 -0
  27. package/esm/BaseFeatureWidget/util.d.ts +0 -3
  28. package/esm/BaseFeatureWidget/util.js +0 -17
  29. package/esm/PluginLoader.d.ts +14 -9
  30. package/esm/PluginLoader.js +34 -26
  31. package/esm/PluginManager.d.ts +4 -4
  32. package/esm/PluginManager.js +1 -1
  33. package/esm/ReExports/coreModules.generated.d.ts +2 -0
  34. package/esm/ReExports/coreModules.generated.js +492 -0
  35. package/esm/ReExports/coreWorkerModules.generated.d.ts +2 -0
  36. package/esm/ReExports/coreWorkerModules.generated.js +453 -0
  37. package/esm/ReExports/frameworkModules.d.ts +2 -0
  38. package/esm/ReExports/frameworkModules.js +67 -0
  39. package/esm/ReExports/frameworkShared.d.ts +12 -0
  40. package/esm/ReExports/frameworkShared.js +16 -0
  41. package/esm/ReExports/frameworkWorkerModules.generated.d.ts +2 -0
  42. package/esm/ReExports/frameworkWorkerModules.generated.js +130 -0
  43. package/esm/ReExports/list.js +493 -79
  44. package/esm/ReExports/modules.d.ts +1 -1
  45. package/esm/ReExports/modules.js +10 -87
  46. package/esm/ReExports/registry.d.ts +1 -0
  47. package/esm/ReExports/registry.js +23 -5
  48. package/esm/ReExports/uiStub.d.ts +2 -1
  49. package/esm/ReExports/uiStub.js +24 -2
  50. package/esm/ReExports/workerModules.js +8 -12
  51. package/esm/TextSearch/TextSearchManager.d.ts +3 -12
  52. package/esm/TextSearch/TextSearchManager.js +22 -10
  53. package/esm/assemblyManager/assembly.d.ts +10 -14
  54. package/esm/assemblyManager/assembly.js +10 -14
  55. package/esm/assemblyManager/assemblyConfigSchema.d.ts +23 -81
  56. package/esm/assemblyManager/assemblyManager.d.ts +10 -16
  57. package/esm/assemblyManager/assemblyManager.js +7 -10
  58. package/esm/assemblyManager/loadRefNameMap.js +3 -3
  59. package/esm/configuration/configurationSchema.d.ts +75 -31
  60. package/esm/configuration/configurationSchema.js +74 -40
  61. package/esm/configuration/configurationSchemaUnion.d.ts +39 -0
  62. package/esm/configuration/configurationSchemaUnion.js +59 -0
  63. package/esm/configuration/configurationSlot.d.ts +37 -12
  64. package/esm/configuration/configurationSlot.js +83 -22
  65. package/esm/configuration/formatAboutConfigSchema.d.ts +7 -21
  66. package/esm/configuration/formatDetailsConfigSchema.d.ts +11 -37
  67. package/esm/configuration/formatDetailsConfigSchema.js +10 -13
  68. package/esm/configuration/getConf.d.ts +17 -9
  69. package/esm/configuration/getConf.js +52 -7
  70. package/esm/configuration/index.d.ts +11 -4
  71. package/esm/configuration/index.js +7 -4
  72. package/esm/configuration/mergeFormatCallbacks.d.ts +6 -7
  73. package/esm/configuration/mergeFormatCallbacks.js +13 -10
  74. package/esm/configuration/readConfObject.d.ts +2 -22
  75. package/esm/configuration/readConfObject.js +15 -49
  76. package/esm/configuration/requirements.d.ts +37 -0
  77. package/esm/configuration/requirements.js +88 -0
  78. package/esm/configuration/schemaRegistry.d.ts +21 -1
  79. package/esm/configuration/schemaRegistry.js +11 -0
  80. package/esm/configuration/schemaTypes.d.ts +3 -0
  81. package/esm/configuration/schemaTypes.js +10 -0
  82. package/esm/configuration/slotFacade.d.ts +35 -4
  83. package/esm/configuration/slotFacade.js +96 -9
  84. package/esm/configuration/snapshotPreprocess.d.ts +27 -0
  85. package/esm/configuration/snapshotPreprocess.js +102 -0
  86. package/esm/configuration/types.d.ts +161 -22
  87. package/esm/data_adapters/BaseAdapter/BaseAdapter.d.ts +2 -2
  88. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +11 -2
  89. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +12 -1
  90. package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +2 -3
  91. package/esm/data_adapters/BaseAdapter/featureDensity.js +2 -3
  92. package/esm/data_adapters/BaseAdapter/index.d.ts +22 -3
  93. package/esm/data_adapters/BaseAdapter/types.d.ts +3 -4
  94. package/esm/data_adapters/BaseAdapter/zoomRange.d.ts +10 -0
  95. package/esm/data_adapters/BaseAdapter/zoomRange.js +1 -0
  96. package/esm/data_adapters/CytobandAdapter/configSchema.d.ts +9 -11
  97. package/esm/data_adapters/getSequenceSubAdapter.d.ts +1 -3
  98. package/esm/data_adapters/getSequenceSubAdapter.js +1 -3
  99. package/esm/pluggableElementTypes/AdapterType.d.ts +7 -7
  100. package/esm/pluggableElementTypes/RpcMethodType.d.ts +1 -1
  101. package/esm/pluggableElementTypes/RpcMethodType.js +3 -8
  102. package/esm/pluggableElementTypes/RpcMethodTypeWithFiltersAndRenameRegions.d.ts +1 -2
  103. package/esm/pluggableElementTypes/TextSearchAdapterType.d.ts +7 -7
  104. package/esm/pluggableElementTypes/index.d.ts +1 -1
  105. package/esm/pluggableElementTypes/index.js +1 -1
  106. package/esm/pluggableElementTypes/models/BaseConnectionModelFactory.d.ts +17 -7
  107. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +45 -48
  108. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +40 -36
  109. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +10 -13
  110. package/esm/pluggableElementTypes/models/BaseTrackModel.js +6 -6
  111. package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +37 -17
  112. package/esm/pluggableElementTypes/models/BaseViewModel.js +25 -10
  113. package/esm/pluggableElementTypes/models/InternetAccountModel.d.ts +26 -16
  114. package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +1 -1
  115. package/esm/pluggableElementTypes/models/baseConnectionConfig.d.ts +22 -18
  116. package/esm/pluggableElementTypes/models/baseConnectionConfig.js +5 -5
  117. package/esm/pluggableElementTypes/models/baseInternetAccountConfig.d.ts +26 -31
  118. package/esm/pluggableElementTypes/models/baseTrackConfig.d.ts +57 -141
  119. package/esm/pluggableElementTypes/models/baseTrackConfig.js +22 -26
  120. package/esm/pluggableElementTypes/models/components/SaveTrackData.js +2 -2
  121. package/esm/pluggableElementTypes/models/components/fetchTrackData.d.ts +3 -4
  122. package/esm/pluggableElementTypes/models/components/fetchTrackData.js +3 -3
  123. package/esm/pluggableElementTypes/models/expandTrackConfigShorthand.d.ts +15 -7
  124. package/esm/pluggableElementTypes/models/expandTrackConfigShorthand.js +38 -29
  125. package/esm/pluggableElementTypes/models/index.d.ts +1 -2
  126. package/esm/pluggableElementTypes/models/index.js +1 -2
  127. package/esm/pluggableElementTypes/models/saveTrackFileTypes/genbank.d.ts +3 -3
  128. package/esm/pluggableElementTypes/models/saveTrackFileTypes/genbank.js +4 -4
  129. package/esm/pluggableElementTypes/models/saveTrackFileTypes/types.d.ts +3 -3
  130. package/esm/rpc/BaseRpcDriver.d.ts +1 -2
  131. package/esm/rpc/BaseRpcDriver.js +23 -30
  132. package/esm/rpc/MainThreadRpcDriver.d.ts +2 -2
  133. package/esm/rpc/MainThreadRpcDriver.js +13 -8
  134. package/esm/rpc/RpcClient.d.ts +17 -6
  135. package/esm/rpc/RpcClient.js +42 -12
  136. package/esm/rpc/RpcManager.d.ts +8 -10
  137. package/esm/rpc/RpcManager.js +1 -1
  138. package/esm/rpc/RpcRegistry.d.ts +8 -2
  139. package/esm/rpc/RpcServer.d.ts +3 -1
  140. package/esm/rpc/RpcServer.js +19 -12
  141. package/esm/rpc/WebWorkerRpcDriver.d.ts +3 -3
  142. package/esm/rpc/WebWorkerRpcDriver.js +9 -47
  143. package/esm/rpc/byteBudget.d.ts +3 -5
  144. package/esm/rpc/byteBudget.js +4 -4
  145. package/esm/rpc/configSchema.d.ts +7 -21
  146. package/esm/rpc/coreRpcMethods.d.ts +1 -0
  147. package/esm/rpc/coreRpcMethods.js +1 -0
  148. package/esm/rpc/methods/CoreEncodeFeatures.d.ts +6 -5
  149. package/esm/rpc/methods/CoreEncodeFeatures.js +26 -31
  150. package/esm/rpc/methods/CoreGetEncodedFeature.d.ts +13 -0
  151. package/esm/rpc/methods/CoreGetEncodedFeature.js +31 -0
  152. package/esm/rpc/methods/CoreGetExportData.js +2 -2
  153. package/esm/rpc/methods/CoreGetFeatureDensity.js +2 -2
  154. package/esm/rpc/methods/CoreGetFeatures.js +2 -2
  155. package/esm/rpc/methods/CoreGetSequence.js +2 -2
  156. package/esm/rpc/methods/layerFeatures.d.ts +26 -0
  157. package/esm/rpc/methods/layerFeatures.js +40 -0
  158. package/esm/svg/SvgExport.js +3 -4
  159. package/esm/svg/SvgThemeProviders.d.ts +12 -0
  160. package/esm/svg/SvgThemeProviders.js +15 -0
  161. package/esm/svg/exportViewSvg.d.ts +32 -0
  162. package/esm/svg/exportViewSvg.js +13 -0
  163. package/esm/svg/normalizeSvgMarkup.d.ts +7 -0
  164. package/esm/svg/normalizeSvgMarkup.js +99 -0
  165. package/esm/svg/serializeSvg.d.ts +7 -0
  166. package/esm/svg/serializeSvg.js +11 -0
  167. package/esm/svg/svgReady.d.ts +8 -7
  168. package/esm/svg/svgReady.js +8 -7
  169. package/esm/svg/trackNames.d.ts +5 -5
  170. package/esm/svg/trackNames.js +6 -6
  171. package/esm/svg/wrapSvgExport.js +6 -74
  172. package/esm/ui/CascadingMenu.js +83 -86
  173. package/esm/ui/ErrorMessageStackTraceDialog.js +0 -5
  174. package/esm/ui/FileSelector/useInternetAccounts.d.ts +156 -96
  175. package/esm/ui/HoverMenu.d.ts +1 -2
  176. package/esm/ui/HoverMenu.js +6 -3
  177. package/esm/ui/JexlFilterDialog.d.ts +13 -10
  178. package/esm/ui/JexlFilterDialog.js +122 -49
  179. package/esm/ui/JexlFilterRowEditors.d.ts +15 -0
  180. package/esm/ui/JexlFilterRowEditors.js +90 -0
  181. package/esm/ui/LazyBaseTooltip.d.ts +4 -0
  182. package/esm/ui/LazyBaseTooltip.js +9 -0
  183. package/esm/ui/LoadingOverlay.js +3 -1
  184. package/esm/ui/MonospaceTextField.d.ts +2 -1
  185. package/esm/ui/MonospaceTextField.js +21 -19
  186. package/esm/ui/PaletteContext.d.ts +7 -9
  187. package/esm/ui/PaletteContext.js +24 -10
  188. package/esm/ui/RefNameAutocomplete/index.d.ts +1 -2
  189. package/esm/ui/RefNameAutocomplete/index.js +1 -1
  190. package/esm/ui/ScrollChrome.d.ts +6 -9
  191. package/esm/ui/ScrollChrome.js +5 -6
  192. package/esm/ui/ScrollEdgeShadow.d.ts +6 -9
  193. package/esm/ui/ScrollEdgeShadow.js +9 -24
  194. package/esm/ui/SettingsChangesTable.js +3 -3
  195. package/esm/ui/VerticalScrollbar.d.ts +13 -34
  196. package/esm/ui/VerticalScrollbar.js +29 -83
  197. package/esm/ui/ViewLoadingScreen.d.ts +2 -2
  198. package/esm/ui/ViewLoadingScreen.js +3 -3
  199. package/esm/ui/colorScale.d.ts +26 -1
  200. package/esm/ui/colorScale.js +28 -5
  201. package/esm/ui/colors.d.ts +49 -1
  202. package/esm/ui/colors.js +139 -4
  203. package/esm/ui/hoverBoxStyle.d.ts +14 -2
  204. package/esm/ui/hoverBoxStyle.js +17 -2
  205. package/esm/ui/index.d.ts +5 -3
  206. package/esm/ui/index.js +9 -7
  207. package/esm/ui/launchViewMenu.js +3 -3
  208. package/esm/ui/legendSpec.d.ts +2 -0
  209. package/esm/ui/legendSpec.js +3 -0
  210. package/esm/ui/palette.d.ts +0 -1
  211. package/esm/ui/palette.js +3 -27
  212. package/esm/ui/replaceViewAction.d.ts +1 -1
  213. package/esm/ui/replaceViewAction.js +1 -1
  214. package/esm/ui/theme.d.ts +1 -1
  215. package/esm/ui/theme.js +6 -40
  216. package/esm/ui/viewHeaderHeight.d.ts +1 -0
  217. package/esm/ui/viewHeaderHeight.js +1 -0
  218. package/esm/util/Base1DUtils.d.ts +1 -1
  219. package/esm/util/Base1DUtils.js +2 -2
  220. package/esm/util/SvgCanvas.d.ts +15 -13
  221. package/esm/util/SvgCanvas.js +72 -53
  222. package/esm/util/aborting.d.ts +39 -0
  223. package/esm/util/aborting.js +84 -0
  224. package/esm/util/adapterMetadata.d.ts +1 -1
  225. package/esm/util/adapterMetadata.js +6 -6
  226. package/esm/util/addAndShowTrack.d.ts +1 -4
  227. package/esm/util/addRelativeUris.d.ts +10 -2
  228. package/esm/util/addRelativeUris.js +22 -7
  229. package/esm/util/addTrackFromWidget.d.ts +2 -5
  230. package/esm/util/animationAllowed.d.ts +9 -0
  231. package/esm/util/animationAllowed.js +13 -0
  232. package/esm/util/applyRowResizeWheel.d.ts +10 -5
  233. package/esm/util/applyRowResizeWheel.js +10 -21
  234. package/esm/util/bandLayout.d.ts +4 -4
  235. package/esm/util/bandLayout.js +4 -4
  236. package/esm/util/bezierConnector.js +5 -4
  237. package/esm/util/bgzfWorkerPool.d.ts +20 -74
  238. package/esm/util/bgzfWorkerPool.js +20 -74
  239. package/esm/util/cachedSetup.d.ts +1 -1
  240. package/esm/util/cachedSetup.js +2 -2
  241. package/esm/util/categoricalField.d.ts +37 -0
  242. package/esm/util/categoricalField.js +52 -0
  243. package/esm/util/color/cssColorsLevel4.d.ts +1 -0
  244. package/esm/util/color/cssColorsLevel4.js +1 -0
  245. package/esm/util/color/index.d.ts +5 -3
  246. package/esm/util/color/index.js +7 -4
  247. package/esm/util/colorBits.d.ts +1 -2
  248. package/esm/util/colorBits.js +17 -39
  249. package/esm/util/colorRamp.d.ts +27 -1
  250. package/esm/util/colorRamp.js +56 -2
  251. package/esm/util/colorSchemes.d.ts +9 -0
  252. package/esm/util/colorSchemes.js +7 -0
  253. package/esm/util/{createStopTokenRotation.d.ts → createAbortRotation.d.ts} +11 -14
  254. package/esm/util/{createStopTokenRotation.js → createAbortRotation.js} +14 -26
  255. package/esm/util/cssColorParse.d.ts +15 -0
  256. package/esm/util/cssColorParse.js +53 -0
  257. package/esm/util/diagonalizeRegions.d.ts +2 -3
  258. package/esm/util/diagonalizeRegions.js +7 -12
  259. package/esm/util/expandLooseSearchIndex.d.ts +7 -0
  260. package/esm/util/expandLooseSearchIndex.js +25 -0
  261. package/esm/util/featureTransforms.d.ts +30 -3
  262. package/esm/util/featureTransforms.js +233 -36
  263. package/esm/util/fetchAndMaybeUnzip.js +5 -6
  264. package/esm/util/fetchContext.d.ts +5 -6
  265. package/esm/util/fetchContext.js +1 -1
  266. package/esm/util/fetchHub.d.ts +3 -1
  267. package/esm/util/fetchHub.js +2 -2
  268. package/esm/util/fetchSeq.d.ts +2 -3
  269. package/esm/util/fetchSeq.js +2 -2
  270. package/esm/util/fieldReader.d.ts +17 -0
  271. package/esm/util/fieldReader.js +62 -0
  272. package/esm/util/groupKeys.d.ts +75 -0
  273. package/esm/util/groupKeys.js +152 -0
  274. package/esm/util/highlights.d.ts +6 -4
  275. package/esm/util/highlights.js +12 -25
  276. package/esm/util/hooks.d.ts +1 -2
  277. package/esm/util/hooks.js +1 -2
  278. package/esm/util/index.d.ts +3 -3
  279. package/esm/util/index.js +4 -3
  280. package/esm/util/indexCandidates.js +1 -1
  281. package/esm/util/installFetch.d.ts +13 -10
  282. package/esm/util/installFetch.js +11 -10
  283. package/esm/util/installPrerequisiteFetch.d.ts +5 -3
  284. package/esm/util/installPrerequisiteFetch.js +3 -4
  285. package/esm/util/io/index.js +12 -12
  286. package/esm/util/isDataCurrent.d.ts +7 -5
  287. package/esm/util/isDataCurrent.js +9 -6
  288. package/esm/util/jexl.js +24 -21
  289. package/esm/util/jexlFilterRows.d.ts +89 -0
  290. package/esm/util/jexlFilterRows.js +252 -0
  291. package/esm/util/jexlFilters.d.ts +7 -5
  292. package/esm/util/jexlStrings.d.ts +3 -4
  293. package/esm/util/jexlStrings.js +2 -3
  294. package/esm/util/legendCandidates.d.ts +31 -27
  295. package/esm/util/legendCandidates.js +79 -53
  296. package/esm/util/localStorage.js +4 -4
  297. package/esm/util/markEncoding.d.ts +32 -16
  298. package/esm/util/markEncoding.js +177 -150
  299. package/esm/util/markEncodingTypes.d.ts +137 -66
  300. package/esm/util/mstUtils.d.ts +19 -22
  301. package/esm/util/mstUtils.js +19 -22
  302. package/esm/util/numericUtils.d.ts +7 -0
  303. package/esm/util/numericUtils.js +16 -0
  304. package/esm/util/numericValue.d.ts +14 -0
  305. package/esm/util/numericValue.js +34 -0
  306. package/esm/util/openFeatureWidget.js +1 -2
  307. package/esm/util/openViews.d.ts +1 -0
  308. package/esm/util/openViews.js +3 -3
  309. package/esm/util/parseLineByLine.d.ts +3 -4
  310. package/esm/util/parseLineByLine.js +7 -13
  311. package/esm/util/progress.d.ts +21 -21
  312. package/esm/util/progress.js +27 -26
  313. package/esm/util/renameRegions.d.ts +1 -2
  314. package/esm/util/renderToStaticMarkup.js +16 -16
  315. package/esm/util/rxjs.d.ts +7 -8
  316. package/esm/util/rxjs.js +12 -15
  317. package/esm/util/simpleFeature.d.ts +0 -1
  318. package/esm/util/svgColorProps.d.ts +11 -0
  319. package/esm/util/svgColorProps.js +28 -1
  320. package/esm/util/tabix.d.ts +3 -4
  321. package/esm/util/tabix.js +6 -7
  322. package/esm/util/thresholdScale.d.ts +54 -0
  323. package/esm/util/thresholdScale.js +112 -0
  324. package/esm/util/timeGate.d.ts +4 -5
  325. package/esm/util/timeGate.js +4 -5
  326. package/esm/util/trackConfigDelta.d.ts +22 -18
  327. package/esm/util/trackConfigDelta.js +40 -38
  328. package/esm/util/tracks.d.ts +27 -4
  329. package/esm/util/tracks.js +116 -43
  330. package/esm/util/translateTranscript.d.ts +44 -0
  331. package/esm/util/translateTranscript.js +102 -0
  332. package/esm/util/types/index.d.ts +19 -7
  333. package/esm/util/types/services.d.ts +1 -1
  334. package/esm/util/unknownSnapshotKeys.d.ts +15 -0
  335. package/esm/util/unknownSnapshotKeys.js +13 -0
  336. package/esm/util/useFeatureSequence.js +4 -4
  337. package/esm/util/useFetch.d.ts +3 -4
  338. package/esm/util/useFetch.js +5 -6
  339. package/esm/util/usePanelVirtualScroll.d.ts +7 -39
  340. package/esm/util/usePanelVirtualScroll.js +9 -40
  341. package/esm/util/useRowVirtualScroll.d.ts +6 -29
  342. package/esm/util/useRowVirtualScroll.js +8 -49
  343. package/esm/util/useVirtualScrollWheel.d.ts +20 -4
  344. package/esm/util/useVirtualScrollWheel.js +40 -84
  345. package/esm/util/viewHighlights.d.ts +3 -0
  346. package/esm/util/viewHighlights.js +5 -0
  347. package/esm/util/viewStatus.d.ts +24 -15
  348. package/esm/util/viewStatus.js +17 -0
  349. package/esm/util/withLaunchInput.d.ts +1 -1
  350. package/esm/util/withLaunchInput.js +0 -4
  351. package/package.json +26 -12
  352. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/DLGAP3.d.ts +0 -80
  353. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/DLGAP3.js +0 -583
  354. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +0 -141
  355. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.js +0 -1116
  356. package/esm/ReExports/BaseFeatureDetails.d.ts +0 -1
  357. package/esm/ReExports/BaseFeatureDetails.js +0 -10
  358. package/esm/ReExports/barrelOnlyNames.d.ts +0 -5
  359. package/esm/ReExports/barrelOnlyNames.js +0 -63
  360. package/esm/ReExports/index.d.ts +0 -1
  361. package/esm/ReExports/index.js +0 -1
  362. package/esm/ReExports/publicTracks.d.ts +0 -1
  363. package/esm/ReExports/publicTracks.js +0 -14
  364. package/esm/ReExports/publicUi.d.ts +0 -4
  365. package/esm/ReExports/publicUi.js +0 -33
  366. package/esm/ReExports/publicUtil.d.ts +0 -1
  367. package/esm/ReExports/publicUtil.js +0 -32
  368. package/esm/ReExports/sharedModules.d.ts +0 -96
  369. package/esm/ReExports/sharedModules.js +0 -66
  370. package/esm/ReExports/workerNamespaceNames.d.ts +0 -11
  371. package/esm/ReExports/workerNamespaceNames.js +0 -275
  372. package/esm/pluggableElementTypes/models/HighlightsMixin.d.ts +0 -51
  373. package/esm/pluggableElementTypes/models/HighlightsMixin.js +0 -78
  374. package/esm/ui/highlightChipReveal.d.ts +0 -32
  375. package/esm/ui/highlightChipReveal.js +0 -39
  376. package/esm/util/adapterConfigKey.d.ts +0 -12
  377. package/esm/util/adapterConfigKey.js +0 -14
  378. package/esm/util/stopToken.d.ts +0 -212
  379. package/esm/util/stopToken.js +0 -438
@@ -0,0 +1,112 @@
1
+ import { categoricalPalette } from '../ui/colors.js';
2
+ import { NO_VALUE_LABEL } from './categoricalField.js';
3
+ import { MISCONFIGURED_COLOR, NO_CATEGORY_COLOR } from './color/index.js';
4
+ import { groupKeyComparator, valueText } from './groupKeys.js';
5
+ import { numericValue } from './numericValue.js';
6
+ /**
7
+ * #api
8
+ * A domain written as strings — the shared `domain` slot is a `stringArray` —
9
+ * read back as the numbers it names. A member that is not a number reads NaN,
10
+ * which every comparison against it declines.
11
+ */
12
+ export function numericDomain(domain) {
13
+ return domain.map(Number);
14
+ }
15
+ /**
16
+ * #api
17
+ * A threshold scale's cut points as {@link thresholdIndex} walks them: the
18
+ * numbers the domain names, ascending. Cuts written high to low, as p-value
19
+ * thresholds often are, left the middle interval unreachable, the walk stopping
20
+ * at the first cut a value is under.
21
+ */
22
+ export function thresholdCuts(domain) {
23
+ return numericDomain(domain)
24
+ .filter(cut => Number.isFinite(cut))
25
+ .sort((a, b) => a - b);
26
+ }
27
+ /**
28
+ * #api
29
+ * The bin a value falls in: how many of the ascending cut points it is at or
30
+ * past, so a palette with one more entry than the domain paints it as
31
+ * `palette[thresholdIndex(value, domain)]`. A value that is not a finite
32
+ * number is in no bin and answers -1.
33
+ */
34
+ export function thresholdIndex(value, domain) {
35
+ const v = numericValue(value);
36
+ if (!Number.isFinite(v)) {
37
+ return -1;
38
+ }
39
+ let i = 0;
40
+ while (i < domain.length && v >= domain[i]) {
41
+ i++;
42
+ }
43
+ return i;
44
+ }
45
+ /**
46
+ * #api
47
+ * The colour of each interval, in order: the declared palette, and the
48
+ * default categorical palette where it runs out.
49
+ */
50
+ export function thresholdPalette(bins, palette = []) {
51
+ return Array.from({ length: bins }, (_, i) => palette[i] ?? categoricalPalette[i % categoricalPalette.length]);
52
+ }
53
+ /**
54
+ * #api
55
+ * What a threshold scale's bins are called in a key, one label per palette
56
+ * entry: `< a` below the first cut, `a – b` between two, `≥ b` past the last.
57
+ */
58
+ export function thresholdLabels(domain) {
59
+ if (domain.length === 0) {
60
+ return ['any value'];
61
+ }
62
+ return [
63
+ `< ${domain[0]}`,
64
+ ...domain.slice(1).map((cut, i) => `${domain[i]} – ${cut}`),
65
+ `≥ ${domain.at(-1)}`,
66
+ ];
67
+ }
68
+ /**
69
+ * #api
70
+ * The key a value holding text that is no number files under on a threshold
71
+ * scale, painted the misconfiguration grey rather than passing for a missing
72
+ * value.
73
+ */
74
+ export const NOT_A_NUMBER_LABEL = '(not a number)';
75
+ // A VCF's `AF=.` arrives as `[undefined]`, which is no value rather than text
76
+ // that fails to parse.
77
+ function isMissing(value) {
78
+ return Array.isArray(value)
79
+ ? value.every(v => v === undefined || v === null)
80
+ : valueText(value) === '';
81
+ }
82
+ /**
83
+ * #api
84
+ * A threshold scale read the way every categorical channel reads its field: a
85
+ * value files under the label of the bin it falls in, a feature with no value
86
+ * under `''`, and text that is no number under {@link NOT_A_NUMBER_LABEL}.
87
+ * The bins are the whole domain, so a key lists each one.
88
+ */
89
+ export function thresholdField(field, { domain = [], range = [], } = {}) {
90
+ const cuts = thresholdCuts(domain);
91
+ const labels = thresholdLabels(cuts);
92
+ const palette = thresholdPalette(labels.length, range);
93
+ const colorOf = new Map(labels.map((label, i) => [label, palette[i]]));
94
+ return {
95
+ field,
96
+ domain: labels,
97
+ closed: true,
98
+ key: value => {
99
+ if (isMissing(value)) {
100
+ return '';
101
+ }
102
+ const bin = thresholdIndex(value, cuts);
103
+ return bin < 0 ? NOT_A_NUMBER_LABEL : labels[bin];
104
+ },
105
+ compare: groupKeyComparator([...labels, NOT_A_NUMBER_LABEL]),
106
+ label: key => (key === '' ? NO_VALUE_LABEL : key),
107
+ sectionLabel: key => `${field}: ${key === '' ? 'none' : key}`,
108
+ color: key => key === ''
109
+ ? NO_CATEGORY_COLOR
110
+ : (colorOf.get(key) ?? MISCONFIGURED_COLOR),
111
+ };
112
+ }
@@ -5,8 +5,8 @@
5
5
  *
6
6
  * `Date.now()` costs ~40ns, invisible until the loop is a 666k-read pileup —
7
7
  * there it measured ~28ms per gated callsite, and the alignments worker has two
8
- * of them (the progress emit in `createProgressReporter` and the stop-token
9
- * check in `checkStopTokenThrottled`). Both previously read the clock per call, on the
8
+ * of them (the progress emit in `createProgressReporter` and the yield in
9
+ * `createAbortBreakpoint`). Both previously read the clock per call, on the
10
10
  * documented reasoning that it was negligible next to the per-item work; at
11
11
  * this item count it measurably isn't.
12
12
  *
@@ -18,9 +18,8 @@
18
18
  * measure a low rate and hold a stride of 1, gating on time alone as before,
19
19
  * while a millions-of-calls loop thins itself out.
20
20
  *
21
- * `intervalMs` is per call rather than fixed at construction: it was added for
22
- * `checkStopTokenThrottled`, whose (since-deleted) sync-XHR probe backed off
23
- * linearly over a long loop. Both current callers pass a constant.
21
+ * `intervalMs` is per call rather than fixed at construction; both current
22
+ * callers pass a constant.
24
23
  */
25
24
  export declare function createTimeGate(): (intervalMs: number) => boolean;
26
25
  export type TimeGate = ReturnType<typeof createTimeGate>;
@@ -24,8 +24,8 @@ const STRIDE_GROWTH_LIMIT = 8;
24
24
  *
25
25
  * `Date.now()` costs ~40ns, invisible until the loop is a 666k-read pileup —
26
26
  * there it measured ~28ms per gated callsite, and the alignments worker has two
27
- * of them (the progress emit in `createProgressReporter` and the stop-token
28
- * check in `checkStopTokenThrottled`). Both previously read the clock per call, on the
27
+ * of them (the progress emit in `createProgressReporter` and the yield in
28
+ * `createAbortBreakpoint`). Both previously read the clock per call, on the
29
29
  * documented reasoning that it was negligible next to the per-item work; at
30
30
  * this item count it measurably isn't.
31
31
  *
@@ -37,9 +37,8 @@ const STRIDE_GROWTH_LIMIT = 8;
37
37
  * measure a low rate and hold a stride of 1, gating on time alone as before,
38
38
  * while a millions-of-calls loop thins itself out.
39
39
  *
40
- * `intervalMs` is per call rather than fixed at construction: it was added for
41
- * `checkStopTokenThrottled`, whose (since-deleted) sync-XHR probe backed off
42
- * linearly over a long loop. Both current callers pass a constant.
40
+ * `intervalMs` is per call rather than fixed at construction; both current
41
+ * callers pass a constant.
43
42
  */
44
43
  export function createTimeGate() {
45
44
  let stride = 1;
@@ -12,18 +12,18 @@
12
12
  * - `diffTrackConfig(base, edited)` — the minimal delta
13
13
  * - `mergeTrackConfig(base, delta)` — reconstruct the effective config
14
14
  *
15
- * Deliberate limitation (no tombstones): a delta records adds/changes only, not
16
- * deletions. If a user *resets* a slot the admin had customized (base has it,
17
- * edited omits it), the delta can't express "drop below the admin value", so the
18
- * field keeps following the base across a reload. This keeps the merge trivial
19
- * and the shared JSON free of deletion sentinels, and following the admin value
20
- * is a defensible outcome.
21
- *
22
- * What it is NOT free to conflate is a removal with a reset. Both diff to an
23
- * empty delta, and `SessionTracks.updateTrackConfiguration` used to read that as
24
- * "the edit netted back to the base" and revert the track's working copy — which
25
- * undid a removal on screen 400ms after it landed. That branch now keeps the
26
- * working copy; see the comment there.
15
+ * A delta is a JSON Merge Patch (RFC 7396) over the base, so a `null` member is
16
+ * a reset: the merge removes the member and the schema's default applies. That
17
+ * is ADR-146's spelling of a reset, and the diff writes one wherever the base
18
+ * sets a member the edited config lacks — a slot put back to its default, or a
19
+ * list emptied, since `stripDefault` drops both from a snapshot. Where the edit
20
+ * left a whole namespace at its default, the nulls land on the members the base
21
+ * set inside it rather than on the namespace, so a member the admin adds there
22
+ * later still flows through. The inference
23
+ * is exact only because both sides are post-`stripDefault` snapshots: a member
24
+ * the admin wrote at its default is absent from both, where a `null` for it
25
+ * would block a later admin value. As in RFC 7396, a delta cannot set a member
26
+ * to a literal `null`; null and absent are one state.
27
27
  *
28
28
  * `displays` is merged by `displayId` so an edit to one display doesn't pin the
29
29
  * others. Nested config objects (e.g. `adapter`) recurse. Any other array (value
@@ -42,18 +42,22 @@ type Json = string | number | boolean | null | undefined | Json[] | {
42
42
  [key: string]: Json;
43
43
  };
44
44
  /**
45
- * Minimal delta of `edited` against `base`. Both are plain config snapshots
46
- * (post-stripDefault). Records adds/changes only; see the module note on the
47
- * no-tombstone limitation. Always retains `trackId` so the delta is
48
- * self-identifying.
45
+ * Minimal delta of `edited` against `base`, both post-stripDefault snapshots. A
46
+ * member `base` sets and `edited` lacks is a `null` (see the module note).
47
+ * Always retains `trackId` so the delta is self-identifying.
49
48
  */
50
49
  export declare function diffTrackConfig(base: Record<string, unknown>, edited: Record<string, unknown>): Record<string, unknown>;
51
50
  /**
52
51
  * Reconstruct the effective config by layering `delta` over the live `base`.
53
- * Inverse of `diffTrackConfig` on the add/change path (see module note).
52
+ * A `null` removes the member it names, and one naming something the base no
53
+ * longer has is dropped, so no `null` member reaches the config a track
54
+ * hydrates from.
54
55
  */
55
56
  export declare function mergeTrackConfig(base: Record<string, unknown>, delta: Record<string, unknown>): Record<string, unknown>;
56
- /** A single overridden slot: the dotted `path`, its base `from` and edited `to`. */
57
+ /**
58
+ * A single overridden slot: the dotted `path`, its base `from` and edited `to`,
59
+ * which is `undefined` for a reset.
60
+ */
57
61
  export interface TrackConfigChange {
58
62
  /**
59
63
  * Where the setting lives. A producer that wants a friendlier heading sets
@@ -12,18 +12,18 @@
12
12
  * - `diffTrackConfig(base, edited)` — the minimal delta
13
13
  * - `mergeTrackConfig(base, delta)` — reconstruct the effective config
14
14
  *
15
- * Deliberate limitation (no tombstones): a delta records adds/changes only, not
16
- * deletions. If a user *resets* a slot the admin had customized (base has it,
17
- * edited omits it), the delta can't express "drop below the admin value", so the
18
- * field keeps following the base across a reload. This keeps the merge trivial
19
- * and the shared JSON free of deletion sentinels, and following the admin value
20
- * is a defensible outcome.
21
- *
22
- * What it is NOT free to conflate is a removal with a reset. Both diff to an
23
- * empty delta, and `SessionTracks.updateTrackConfiguration` used to read that as
24
- * "the edit netted back to the base" and revert the track's working copy — which
25
- * undid a removal on screen 400ms after it landed. That branch now keeps the
26
- * working copy; see the comment there.
15
+ * A delta is a JSON Merge Patch (RFC 7396) over the base, so a `null` member is
16
+ * a reset: the merge removes the member and the schema's default applies. That
17
+ * is ADR-146's spelling of a reset, and the diff writes one wherever the base
18
+ * sets a member the edited config lacks — a slot put back to its default, or a
19
+ * list emptied, since `stripDefault` drops both from a snapshot. Where the edit
20
+ * left a whole namespace at its default, the nulls land on the members the base
21
+ * set inside it rather than on the namespace, so a member the admin adds there
22
+ * later still flows through. The inference
23
+ * is exact only because both sides are post-`stripDefault` snapshots: a member
24
+ * the admin wrote at its default is absent from both, where a `null` for it
25
+ * would block a later admin value. As in RFC 7396, a delta cannot set a member
26
+ * to a literal `null`; null and absent are one state.
27
27
  *
28
28
  * `displays` is merged by `displayId` so an edit to one display doesn't pin the
29
29
  * others. Nested config objects (e.g. `adapter`) recurse. Any other array (value
@@ -49,10 +49,14 @@ function isDisplayArray(v) {
49
49
  v.length > 0 &&
50
50
  v.every(e => isPlainObject(e) && typeof e.displayId === 'string'));
51
51
  }
52
- // Sentinel returned by diffValue when a value is unchanged, so callers can tell
53
- // "no delta" apart from a legitimate `undefined` slot value.
54
52
  const UNCHANGED = Symbol('unchanged');
55
53
  function diffValue(base, edited) {
54
+ if (edited == null) {
55
+ if (base == null) {
56
+ return UNCHANGED;
57
+ }
58
+ return isPlainObject(base) ? diffValue(base, {}) : null;
59
+ }
56
60
  if (deepEqual(base, edited)) {
57
61
  return UNCHANGED;
58
62
  }
@@ -65,13 +69,10 @@ function diffValue(base, edited) {
65
69
  if (baseDisplay) {
66
70
  const d = diffValue(baseDisplay, editedDisplay);
67
71
  if (d !== UNCHANGED) {
68
- // always re-key the delta entry by displayId so merge can realign it
69
72
  out.push({ ...d, displayId: id });
70
73
  }
71
74
  }
72
75
  else {
73
- // a display present only in the edited config (e.g. an added display):
74
- // carry it whole
75
76
  out.push(editedDisplay);
76
77
  }
77
78
  }
@@ -79,25 +80,20 @@ function diffValue(base, edited) {
79
80
  }
80
81
  if (isPlainObject(edited) && isPlainObject(base)) {
81
82
  const out = {};
82
- for (const [k, v] of Object.entries(edited)) {
83
- const d = diffValue(base[k], v);
83
+ for (const k of new Set([...Object.keys(base), ...Object.keys(edited)])) {
84
+ const d = diffValue(base[k], edited[k]);
84
85
  if (d !== UNCHANGED) {
85
86
  out[k] = d;
86
87
  }
87
88
  }
88
- // an object whose only difference from base is dropped keys (a reset the
89
- // delta can't express, see module note) yields no adds/changes: treat it as
90
- // unchanged rather than emitting a content-free entry
91
89
  return Object.keys(out).length > 0 ? out : UNCHANGED;
92
90
  }
93
- // scalar, or an object/array replacing a scalar (or a value array): replace
94
91
  return edited;
95
92
  }
96
93
  /**
97
- * Minimal delta of `edited` against `base`. Both are plain config snapshots
98
- * (post-stripDefault). Records adds/changes only; see the module note on the
99
- * no-tombstone limitation. Always retains `trackId` so the delta is
100
- * self-identifying.
94
+ * Minimal delta of `edited` against `base`, both post-stripDefault snapshots. A
95
+ * member `base` sets and `edited` lacks is a `null` (see the module note).
96
+ * Always retains `trackId` so the delta is self-identifying.
101
97
  */
102
98
  export function diffTrackConfig(base, edited) {
103
99
  const d = diffValue(base, edited);
@@ -106,34 +102,40 @@ export function diffTrackConfig(base, edited) {
106
102
  return { ...delta, trackId };
107
103
  }
108
104
  function mergeValue(base, delta) {
109
- if (isDisplayArray(delta) && isDisplayArray(base)) {
105
+ if (isDisplayArray(delta)) {
106
+ const baseDisplays = isDisplayArray(base) ? base : [];
110
107
  const deltaById = new Map(delta.map(d => [d.displayId, d]));
111
- const merged = base.map(baseDisplay => {
108
+ const merged = baseDisplays.map(baseDisplay => {
112
109
  const d = deltaById.get(baseDisplay.displayId);
113
110
  return d ? mergeValue(baseDisplay, d) : baseDisplay;
114
111
  });
115
- // displays present only in the delta (added by the user) append after base
116
- const baseIds = new Set(base.map(d => d.displayId));
112
+ const baseIds = new Set(baseDisplays.map(d => d.displayId));
117
113
  for (const d of delta) {
118
114
  if (!baseIds.has(d.displayId)) {
119
- merged.push(d);
115
+ merged.push(mergeValue(undefined, d));
120
116
  }
121
117
  }
122
118
  return merged;
123
119
  }
124
- if (isPlainObject(delta) && isPlainObject(base)) {
125
- const out = { ...base };
120
+ if (isPlainObject(delta)) {
121
+ const out = isPlainObject(base) ? { ...base } : {};
126
122
  for (const [k, v] of Object.entries(delta)) {
127
- out[k] = mergeValue(base[k], v);
123
+ if (v === null) {
124
+ delete out[k];
125
+ }
126
+ else {
127
+ out[k] = mergeValue(out[k], v);
128
+ }
128
129
  }
129
130
  return out;
130
131
  }
131
- // scalar / value array / type mismatch: delta wins
132
132
  return delta;
133
133
  }
134
134
  /**
135
135
  * Reconstruct the effective config by layering `delta` over the live `base`.
136
- * Inverse of `diffTrackConfig` on the add/change path (see module note).
136
+ * A `null` removes the member it names, and one naming something the base no
137
+ * longer has is dropped, so no `null` member reaches the config a track
138
+ * hydrates from.
137
139
  */
138
140
  export function mergeTrackConfig(base, delta) {
139
141
  return mergeValue(base, delta);
@@ -172,7 +174,7 @@ function walkDelta(base, delta, path, out, identityKeys = IDENTITY_KEYS) {
172
174
  }
173
175
  }
174
176
  else {
175
- out.push({ path, from: base, to: delta });
177
+ out.push({ path, from: base, to: delta ?? undefined });
176
178
  }
177
179
  }
178
180
  /**
@@ -223,6 +223,7 @@ export declare function guessTrackType(adapterType: string, model?: IAnyStateTre
223
223
  export interface LooseTrackInput {
224
224
  uri: string;
225
225
  index?: string;
226
+ baseUri?: string;
226
227
  [key: string]: unknown;
227
228
  }
228
229
  /**
@@ -383,6 +384,31 @@ export declare function viewDisplayNames(pluginManager: PluginManager, viewType:
383
384
  * so the pickers agree on it.
384
385
  */
385
386
  export declare function viewCanDisplayTrack(pluginManager: PluginManager, viewDisplays: Set<string>, trackType: string): boolean;
387
+ /**
388
+ * The tracks a view can be offered: those that support every assembly it
389
+ * displays, and that declare a display it can draw. The track selector's tree
390
+ * and every other picker that opens a track into a view read this one answer,
391
+ * or one of them offers a track the next hides.
392
+ */
393
+ export declare function filterTracks(tracks: AnyConfigurationModel[], self: {
394
+ view?: IStateTreeNode & {
395
+ type: string;
396
+ };
397
+ assemblyNames: string[];
398
+ }): AnyConfigurationModel[];
399
+ /**
400
+ * Every track config a view can be offered, each assembly's sequence track
401
+ * first and then {@link filterTracks} over the list handed in. The track
402
+ * selector's tree and the close-up dialog are both this, so a track one
403
+ * offers and the other hides would have to be a difference in the assemblies
404
+ * they were asked about.
405
+ */
406
+ export declare function offeredTracks(tracks: AnyConfigurationModel[], self: {
407
+ view?: IStateTreeNode & {
408
+ type: string;
409
+ };
410
+ assemblyNames: string[];
411
+ }): AnyConfigurationModel[];
386
412
  export declare function pickDisplayForView({ declaredDisplays, requestedType, trackDisplayTypes, viewDisplayTypes, preferredDisplayTypes, }: {
387
413
  declaredDisplays: DisplayConfSnapshot[];
388
414
  requestedType: string | undefined;
@@ -447,10 +473,7 @@ export declare function allSessionTracks(session: {
447
473
  connectionInstances?: {
448
474
  tracks: AnyConfigurationModel[];
449
475
  }[];
450
- }): (import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
451
- setSubschema(slotName: string, data: Record<string, unknown>): any;
452
- setSlot(slotName: string, value: unknown): void;
453
- } & IStateTreeNode<import("../configuration/types.ts").AnyConfigurationSchemaType>)[];
476
+ }): AnyConfigurationModel[];
454
477
  /**
455
478
  * The session's annotation for `assemblyName`, as trackIds: what a view opened
456
479
  * on another genome starts with. Only FeatureTracks, not everything configured
@@ -8,13 +8,14 @@ export { getFileName } from './getFileName.js';
8
8
  // it. `fetchContext.ts` takes it from mstUtils instead, which is what keeps the
9
9
  // configuration schemas this file pulls in out of the fetch harness's graph.
10
10
  export { getRpcSessionId } from './parentWalk.js';
11
- import { getParent, getSnapshot, hasParent, isAlive, isStateTreeNode, } from '@jbrowse/mobx-state-tree';
11
+ import { getParent, getSnapshot, getType, hasParent, isAlive, isModelType, isStateTreeNode, } from '@jbrowse/mobx-state-tree';
12
12
  import { observable, runInAction, untracked } from 'mobx';
13
13
  import { readConfObject } from '../configuration/index.js';
14
14
  import { getFileHandle, storeFileHandle, verifyPermission, } from './fileHandleStore.js';
15
15
  import { getFileName } from './getFileName.js';
16
16
  import { getContainingView, getEnv, getSession, objectHash, } from './mstUtils.js';
17
17
  import { isViewModel } from './types/index.js';
18
+ import { setterOnlyMessage, unknownKeysMessage } from './unknownSnapshotKeys.js';
18
19
  // Deliberately uncached. This used to memoize on a permanent WeakMap keyed by
19
20
  // track, but every caller reads it from inside a reactive getter or autorun: the
20
21
  // memo meant the reaction never subscribed to the config, so editing
@@ -405,9 +406,10 @@ export function guessTrackType(adapterType, model, file) {
405
406
  * the caller can ask for a full config instead.
406
407
  */
407
408
  export function guessTrackConf(input, pluginManager, assemblyName) {
408
- const { uri, index, ...extra } = typeof input === 'string' ? { uri: input } : input;
409
+ const { uri, index, baseUri, ...extra } = typeof input === 'string' ? { uri: input } : input;
410
+ const base = baseUri ? { baseUri } : {};
409
411
  return {
410
- ...guessTrackConfForLocation({ uri, locationType: 'UriLocation' }, index ? { uri: index, locationType: 'UriLocation' } : undefined, pluginManager, assemblyName),
412
+ ...guessTrackConfForLocation({ uri, locationType: 'UriLocation', ...base }, index ? { uri: index, locationType: 'UriLocation', ...base } : undefined, pluginManager, assemblyName),
411
413
  ...extra,
412
414
  };
413
415
  }
@@ -501,6 +503,43 @@ export function getTrackName(conf, session) {
501
503
  : (conf.trackId ?? '');
502
504
  return trackName || trackId;
503
505
  }
506
+ // The first-show path spreads the same settings into the display's `create()`
507
+ // snapshot, so a DECLARED PROP (`resolution` on the wiggle displays) has
508
+ // already landed by the time applyDisplaySettings reports it took no slot.
509
+ // Only a declared prop: MST restores nothing else from a snapshot, so a
510
+ // volatile or a getter of the same name (`scrollTop`) did not land and the
511
+ // caller has to hear about it.
512
+ function didNotLand(unapplied, display) {
513
+ const type = display ? getType(display) : undefined;
514
+ const props = type && isModelType(type) ? Object.keys(type.properties) : [];
515
+ return unapplied.filter(entry => !props.includes(entry.key));
516
+ }
517
+ function notifySettingsReport(session, trackId, displayType, unapplied, failed) {
518
+ const label = `${displayType} for track "${trackId}"`;
519
+ const keys = (of) => of.map(entry => entry.key);
520
+ const setterOnly = unapplied.filter(e => e.reason === 'setter-only');
521
+ const unknown = unapplied.filter(e => e.reason !== 'setter-only');
522
+ if (unknown.length) {
523
+ session.notifyError(unknownKeysMessage(label, keys(unknown)));
524
+ }
525
+ if (setterOnly.length) {
526
+ session.notifyError(setterOnlyMessage(label, keys(setterOnly)));
527
+ }
528
+ if (failed.length) {
529
+ session.notifyError(`Track "${trackId}" rejected ${failed.map(f => `${f.key}: ${f.error}`).join('; ')}`);
530
+ }
531
+ }
532
+ // `type` picks a display for a new track and means nothing to a shown one.
533
+ function restyleShown(self, trackId, found, { type: _type, ...settings }) {
534
+ const track = found;
535
+ if (Object.keys(settings).length && track.applyDisplaySettings) {
536
+ const report = track.applyDisplaySettings(settings);
537
+ // no `didNotLand` here: this path spreads no snapshot, so every entry
538
+ // applyDisplaySettings reports really did write nothing
539
+ notifySettingsReport(getSession(self), trackId, track.activeDisplay.type, report.unapplied, report.failed);
540
+ }
541
+ return found;
542
+ }
504
543
  /**
505
544
  * Which display a track opens with in a given view, and the display config that
506
545
  * display must use: `{ type, conf }`, or undefined when the view supports none
@@ -564,6 +603,74 @@ export function viewCanDisplayTrack(pluginManager, viewDisplays, trackType) {
564
603
  .getTrackType(trackType)
565
604
  .displayTypes.some(d => viewDisplays.has(d.name)));
566
605
  }
606
+ /**
607
+ * Whether every name in `subset` appears in `superset`. An absent superset
608
+ * contains nothing; an absent subset is contained by anything.
609
+ */
610
+ function containsAll(superset = [], subset = []) {
611
+ const s = new Set(superset);
612
+ return subset.every(x => s.has(x));
613
+ }
614
+ /**
615
+ * The tracks a view can be offered: those that support every assembly it
616
+ * displays, and that declare a display it can draw. The track selector's tree
617
+ * and every other picker that opens a track into a view read this one answer,
618
+ * or one of them offers a track the next hides.
619
+ */
620
+ export function filterTracks(tracks, self) {
621
+ const { view } = self;
622
+ if (!view) {
623
+ return [];
624
+ }
625
+ const { assemblyManager } = getSession(view);
626
+ const { pluginManager } = getEnv(view);
627
+ const canonical = (names) => canonicalAssemblyNames(names, assemblyManager);
628
+ const viewAssemblyNames = canonical(self.assemblyNames);
629
+ const viewDisplays = viewDisplayNames(pluginManager, view.type);
630
+ return tracks.filter(c => {
631
+ const trackAssemblyNames = readConfObject(c, 'assemblyNames');
632
+ return (
633
+ // a view that declares no assemblies (one still initializing) constrains
634
+ // nothing; otherwise the track must cover every one of them
635
+ (viewAssemblyNames.length === 0 ||
636
+ containsAll(trackAssemblyNames && canonical(trackAssemblyNames), viewAssemblyNames)) &&
637
+ viewCanDisplayTrack(pluginManager, viewDisplays, c.type));
638
+ });
639
+ }
640
+ /**
641
+ * The assembly's own sequence track, when the view can draw one. It is in no
642
+ * track list — the config lives on the assembly (see {@link getConfAssemblyNames}
643
+ * on why a sequence track's assembly is positional) — so a picker that walks
644
+ * `session.tracks` alone cannot offer the track most views open first.
645
+ */
646
+ function refSeqTrackConf(assemblyName, view) {
647
+ const { assemblyManager } = getSession(view);
648
+ const { pluginManager } = getEnv(view);
649
+ const conf = assemblyManager.get(assemblyName)?.configuration.sequence;
650
+ // the same display-compatibility question filterTracks asks of every other
651
+ // track, so a view that cannot draw a sequence is not offered one
652
+ return conf &&
653
+ viewCanDisplayTrack(pluginManager, viewDisplayNames(pluginManager, view.type), conf.type)
654
+ ? conf
655
+ : undefined;
656
+ }
657
+ /**
658
+ * Every track config a view can be offered, each assembly's sequence track
659
+ * first and then {@link filterTracks} over the list handed in. The track
660
+ * selector's tree and the close-up dialog are both this, so a track one
661
+ * offers and the other hides would have to be a difference in the assemblies
662
+ * they were asked about.
663
+ */
664
+ export function offeredTracks(tracks, self) {
665
+ const { view, assemblyNames } = self;
666
+ if (!view) {
667
+ return [];
668
+ }
669
+ return [
670
+ ...assemblyNames.map(name => refSeqTrackConf(name, view)),
671
+ ...filterTracks(tracks, self),
672
+ ].filter(conf => conf !== undefined);
673
+ }
567
674
  export function pickDisplayForView({ declaredDisplays, requestedType, trackDisplayTypes, viewDisplayTypes, preferredDisplayTypes = new Set(), }) {
568
675
  const supported = new Set(viewDisplayTypes);
569
676
  // A requested type is CHECKED, not taken on faith. It used to pass straight
@@ -634,6 +741,9 @@ function resolveTrackDisplayChoice(self, trackId, displayInitialSnapshot, inline
634
741
  throw new Error(`Could not resolve identifier "${trackId}"`);
635
742
  }
636
743
  const confSnapshot = structuredClone(isStateTreeNode(rawConf) ? getSnapshot(rawConf) : rawConf);
744
+ // `& { type: string }` because `rawConf` unions a live node with a loose
745
+ // `Record<string, unknown>`, and the node's `type: string` widens back to
746
+ // `unknown` across that union
637
747
  const conf = pluginManager.evaluateExtensionPoint('Core-preProcessTrackConfig', confSnapshot);
638
748
  const trackType = pluginManager.getTrackType(conf.type);
639
749
  // Validate only a conf that is not already in the tree. `configSchema.create`
@@ -706,7 +816,7 @@ inlineConf) {
706
816
  const session = getSession(self);
707
817
  const found = self.tracks.find(t => t.configuration.trackId === trackId);
708
818
  if (found) {
709
- return found;
819
+ return restyleShown(self, trackId, found, displayInitialSnapshot);
710
820
  }
711
821
  // Single choke point for all "open a track" paths — errors surface as
712
822
  // snackbars. Config is validated before the push so the open set never holds
@@ -756,47 +866,10 @@ inlineConf) {
756
866
  // reference can resolve. Slots only, deliberately: this is the
757
867
  // declarative surface, and `allowSetters` stays off so a spec key cannot
758
868
  // reach internal display actions.
759
- //
760
- // `type` is dropped first because this function already consumed it
761
- // (pickDisplayForView above), and only `failed` — a key whose write threw —
762
- // is notified: `unapplied` here also holds the MST display props the
763
- // snapshot spread above already applied, so notifying on it would report a
764
- // correct call as broken. A throwing slot value used to reach the outer
765
- // catch and raise a snackbar; per-key catching kept the track intact but
766
- // took the message away with it.
767
869
  const displaySettings = Object.fromEntries(Object.entries(displayInitialSnapshot).filter(([key]) => key !== 'type'));
768
870
  const report = track.applyDisplaySettings(displaySettings);
769
- // A key that reached NOTHING, told apart from the ones `unapplied` holds
770
- // legitimately. `unapplied` means "not a config slot", which covers both an
771
- // MST display prop the snapshot spread above already applied and a
772
- // misspelling that did nothing at all — so reporting the list wholesale
773
- // called a correct spec broken, and reporting none of it let
774
- // `{ trackId, colorSchem: 'x' }` load a plausible track with the setting
775
- // silently missing. The display's own model type says which is which.
776
- // Asked of the display NODE, not of its model type: `type.properties` sees
777
- // declared props and misses everything else a key can legitimately reach —
778
- // a volatile the snapshot spread above just set (`resolution` on the GC
779
- // content display is one) reads as unreached and gets reported over a
780
- // correct call.
781
- // `unapplied` holds three kinds of entry, and only one of them means the
782
- // key did nothing at all: applyDisplaySettings annotates `type` and the
783
- // "a setter exists, pass allowSetters" case in parentheses, and pushes a
784
- // BARE key when the display has neither a slot nor a setter for it. So the
785
- // bare ones are the candidates, and the node itself settles the rest — the
786
- // snapshot spread above may already have landed the key as a prop or a
787
- // volatile, which is what `resolution` on the GC content display is.
788
871
  const drawn = track.displays[0];
789
- const reachedNothing = drawn
790
- ? report.unapplied.filter(entry => !entry.includes(' (') && !(entry in drawn))
791
- : [];
792
- if (reachedNothing.length > 0) {
793
- session.notifyError(`Track "${trackId}" ignored ${reachedNothing.join(', ')}: neither a setting the ${displayType} accepts nor one of its properties`);
794
- }
795
- if (report.failed.length) {
796
- session.notifyError(`Track "${trackId}" rejected ${report.failed
797
- .map(f => `${f.key}: ${f.error}`)
798
- .join('; ')}`);
799
- }
872
+ notifySettingsReport(session, trackId, displayType, didNotLand(report.unapplied, drawn), report.failed);
800
873
  // if this track came from a connection, persist its config so it survives
801
874
  // reload without re-establishing the connection (no-op otherwise)
802
875
  session.captureConnectionTrack?.(trackId);
@@ -840,7 +913,7 @@ export async function launchTrackGeneric(self, trackId, initialSnapshot = {}, di
840
913
  const session = getSession(self);
841
914
  const found = self.tracks.find(t => t.configuration.trackId === trackId);
842
915
  if (found) {
843
- return found;
916
+ return restyleShown(self, trackId, found, displayInitialSnapshot);
844
917
  }
845
918
  try {
846
919
  const { picked } = resolveTrackDisplayChoice(self, trackId, displayInitialSnapshot, inlineConf);