@genome-spy/core 0.73.0 → 0.75.0

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 (392) hide show
  1. package/dist/bundle/AbortablePromiseCache-3gHJdF3E.js +96 -0
  2. package/dist/bundle/browser-BTgw5ieH.js +126 -0
  3. package/dist/bundle/chunk-DmhlhrBa.js +11 -0
  4. package/dist/bundle/esm-BDFRLEuD.js +1248 -0
  5. package/dist/bundle/esm-BygJiwh0.js +573 -0
  6. package/dist/bundle/esm-CGX-qz1d.js +155 -0
  7. package/dist/bundle/esm-CgfVIRJ-.js +121 -0
  8. package/dist/bundle/esm-CuMSzCHy.js +298 -0
  9. package/dist/bundle/esm-DMXpJXM4.js +369 -0
  10. package/dist/bundle/esm-DQiq2Zhd.js +1426 -0
  11. package/dist/bundle/esm-DtE8VqAv.js +1015 -0
  12. package/dist/bundle/esm-sIoQYZ21.js +461 -0
  13. package/dist/bundle/index.es.js +21266 -25938
  14. package/dist/bundle/index.js +367 -383
  15. package/dist/bundle/parquetRead-DG_-F5j5.js +1609 -0
  16. package/dist/schema.json +13421 -7210
  17. package/dist/src/config/axisConfig.d.ts +16 -0
  18. package/dist/src/config/axisConfig.d.ts.map +1 -0
  19. package/dist/src/config/axisConfig.js +84 -0
  20. package/dist/src/config/defaultConfig.d.ts +3 -0
  21. package/dist/src/config/defaultConfig.d.ts.map +1 -0
  22. package/dist/src/config/defaultConfig.js +38 -0
  23. package/dist/src/config/defaults/axisDefaults.d.ts +5 -0
  24. package/dist/src/config/defaults/axisDefaults.d.ts.map +1 -0
  25. package/dist/src/config/defaults/axisDefaults.js +72 -0
  26. package/dist/src/config/defaults/markDefaults.d.ts +15 -0
  27. package/dist/src/config/defaults/markDefaults.d.ts.map +1 -0
  28. package/dist/src/config/defaults/markDefaults.js +121 -0
  29. package/dist/src/config/defaults/scaleDefaults.d.ts +5 -0
  30. package/dist/src/config/defaults/scaleDefaults.d.ts.map +1 -0
  31. package/dist/src/config/defaults/scaleDefaults.js +18 -0
  32. package/dist/src/config/defaults/titleDefaults.d.ts +5 -0
  33. package/dist/src/config/defaults/titleDefaults.d.ts.map +1 -0
  34. package/dist/src/config/defaults/titleDefaults.js +47 -0
  35. package/dist/src/config/defaults/viewDefaults.d.ts +3 -0
  36. package/dist/src/config/defaults/viewDefaults.d.ts.map +1 -0
  37. package/dist/src/config/defaults/viewDefaults.js +2 -0
  38. package/dist/src/config/markConfig.d.ts +8 -0
  39. package/dist/src/config/markConfig.d.ts.map +1 -0
  40. package/dist/src/config/markConfig.js +27 -0
  41. package/dist/src/config/mergeConfig.d.ts +8 -0
  42. package/dist/src/config/mergeConfig.d.ts.map +1 -0
  43. package/dist/src/config/mergeConfig.js +81 -0
  44. package/dist/src/config/resolveConfig.d.ts +22 -0
  45. package/dist/src/config/resolveConfig.d.ts.map +1 -0
  46. package/dist/src/config/resolveConfig.js +32 -0
  47. package/dist/src/config/scaleConfig.d.ts +40 -0
  48. package/dist/src/config/scaleConfig.d.ts.map +1 -0
  49. package/dist/src/config/scaleConfig.js +220 -0
  50. package/dist/src/config/styleUtils.d.ts +6 -0
  51. package/dist/src/config/styleUtils.d.ts.map +1 -0
  52. package/dist/src/config/styleUtils.js +10 -0
  53. package/dist/src/config/themes.d.ts +15 -0
  54. package/dist/src/config/themes.d.ts.map +1 -0
  55. package/dist/src/config/themes.js +293 -0
  56. package/dist/src/config/titleConfig.d.ts +12 -0
  57. package/dist/src/config/titleConfig.d.ts.map +1 -0
  58. package/dist/src/config/titleConfig.js +42 -0
  59. package/dist/src/config/viewConfig.d.ts +7 -0
  60. package/dist/src/config/viewConfig.d.ts.map +1 -0
  61. package/dist/src/config/viewConfig.js +29 -0
  62. package/dist/src/data/flowNode.d.ts +22 -1
  63. package/dist/src/data/flowNode.d.ts.map +1 -1
  64. package/dist/src/data/flowNode.js +37 -1
  65. package/dist/src/data/formats/bed.d.ts.map +1 -1
  66. package/dist/src/data/formats/bed.js +6 -1
  67. package/dist/src/data/formats/bedpe.d.ts.map +1 -1
  68. package/dist/src/data/formats/bedpe.js +4 -0
  69. package/dist/src/data/formats/fasta.d.ts.map +1 -1
  70. package/dist/src/data/formats/fasta.js +4 -0
  71. package/dist/src/data/formats/parquet.d.ts.map +1 -1
  72. package/dist/src/data/formats/parquet.js +4 -0
  73. package/dist/src/data/sources/dataSourceFactory.d.ts +2 -13
  74. package/dist/src/data/sources/dataSourceFactory.d.ts.map +1 -1
  75. package/dist/src/data/sources/dataSourceFactory.js +5 -141
  76. package/dist/src/data/sources/dataUtils.d.ts +25 -0
  77. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  78. package/dist/src/data/sources/dataUtils.js +23 -0
  79. package/dist/src/data/sources/inlineSource.js +2 -2
  80. package/dist/src/data/sources/lazy/axisGenomeSource.d.ts.map +1 -1
  81. package/dist/src/data/sources/lazy/axisGenomeSource.js +11 -0
  82. package/dist/src/data/sources/lazy/axisTickSource.d.ts +1 -1
  83. package/dist/src/data/sources/lazy/axisTickSource.d.ts.map +1 -1
  84. package/dist/src/data/sources/lazy/axisTickSource.js +19 -8
  85. package/dist/src/data/sources/lazy/bamSource.d.ts.map +1 -1
  86. package/dist/src/data/sources/lazy/bamSource.js +11 -0
  87. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  88. package/dist/src/data/sources/lazy/bigBedSource.js +12 -1
  89. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  90. package/dist/src/data/sources/lazy/bigWigSource.js +11 -0
  91. package/dist/src/data/sources/lazy/gff3Source.d.ts.map +1 -1
  92. package/dist/src/data/sources/lazy/gff3Source.js +12 -1
  93. package/dist/src/data/sources/lazy/indexedFastaSource.d.ts.map +1 -1
  94. package/dist/src/data/sources/lazy/indexedFastaSource.js +11 -0
  95. package/dist/src/data/sources/lazy/lazyDataSourceRegistry.d.ts +27 -0
  96. package/dist/src/data/sources/lazy/lazyDataSourceRegistry.d.ts.map +1 -0
  97. package/dist/src/data/sources/lazy/lazyDataSourceRegistry.js +65 -0
  98. package/dist/src/data/sources/lazy/registerBuiltInLazySources.d.ts +2 -0
  99. package/dist/src/data/sources/lazy/registerBuiltInLazySources.d.ts.map +1 -0
  100. package/dist/src/data/sources/lazy/registerBuiltInLazySources.js +8 -0
  101. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  102. package/dist/src/data/sources/lazy/singleAxisLazySource.js +11 -2
  103. package/dist/src/data/sources/lazy/vcfSource.d.ts.map +1 -1
  104. package/dist/src/data/sources/lazy/vcfSource.js +11 -0
  105. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  106. package/dist/src/data/sources/urlSource.js +13 -5
  107. package/dist/src/data/transforms/aggregate.d.ts +1 -0
  108. package/dist/src/data/transforms/aggregate.d.ts.map +1 -1
  109. package/dist/src/data/transforms/aggregate.js +30 -8
  110. package/dist/src/data/transforms/aggregateOps.d.ts.map +1 -1
  111. package/dist/src/data/transforms/aggregateOps.js +12 -1
  112. package/dist/src/data/transforms/coverage.js +2 -2
  113. package/dist/src/data/transforms/filter.js +1 -1
  114. package/dist/src/data/transforms/filterScoredLabels.d.ts +6 -0
  115. package/dist/src/data/transforms/filterScoredLabels.d.ts.map +1 -1
  116. package/dist/src/data/transforms/filterScoredLabels.js +9 -0
  117. package/dist/src/data/transforms/measureText.d.ts +1 -0
  118. package/dist/src/data/transforms/measureText.d.ts.map +1 -1
  119. package/dist/src/data/transforms/measureText.js +14 -5
  120. package/dist/src/data/transforms/pileup.d.ts.map +1 -1
  121. package/dist/src/data/transforms/pileup.js +1 -2
  122. package/dist/src/data/transforms/regexFold.d.ts.map +1 -1
  123. package/dist/src/data/transforms/regexFold.js +0 -1
  124. package/dist/src/embedFactory.d.ts +13 -0
  125. package/dist/src/embedFactory.d.ts.map +1 -0
  126. package/dist/src/embedFactory.js +127 -0
  127. package/dist/src/encoder/accessor.d.ts +3 -12
  128. package/dist/src/encoder/accessor.d.ts.map +1 -1
  129. package/dist/src/encoder/accessor.js +10 -65
  130. package/dist/src/encoder/encoder.d.ts +51 -8
  131. package/dist/src/encoder/encoder.d.ts.map +1 -1
  132. package/dist/src/encoder/encoder.js +179 -55
  133. package/dist/src/fonts/bmFontManager.js +1 -1
  134. package/dist/src/full.d.ts +2 -0
  135. package/dist/src/full.d.ts.map +1 -0
  136. package/dist/src/full.js +2 -0
  137. package/dist/src/genome/genome.d.ts +8 -0
  138. package/dist/src/genome/genome.d.ts.map +1 -1
  139. package/dist/src/genome/genome.js +16 -2
  140. package/dist/src/genome/genomeStore.js +1 -1
  141. package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -1
  142. package/dist/src/genome/rootGenomeConfig.js +6 -2
  143. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  144. package/dist/src/genome/scaleLocus.js +31 -7
  145. package/dist/src/genomeSpy/cursorManager.d.ts +69 -0
  146. package/dist/src/genomeSpy/cursorManager.d.ts.map +1 -0
  147. package/dist/src/genomeSpy/cursorManager.js +131 -0
  148. package/dist/src/genomeSpy/headlessBootstrap.d.ts +113 -0
  149. package/dist/src/genomeSpy/headlessBootstrap.d.ts.map +1 -0
  150. package/dist/src/genomeSpy/headlessBootstrap.js +246 -0
  151. package/dist/src/genomeSpy/interactionController.d.ts +5 -0
  152. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  153. package/dist/src/genomeSpy/interactionController.js +292 -59
  154. package/dist/src/genomeSpy/interactionDispatcher.d.ts +50 -0
  155. package/dist/src/genomeSpy/interactionDispatcher.d.ts.map +1 -0
  156. package/dist/src/genomeSpy/interactionDispatcher.js +203 -0
  157. package/dist/src/genomeSpy/viewContextFactory.d.ts +4 -2
  158. package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -1
  159. package/dist/src/genomeSpy/viewContextFactory.js +12 -4
  160. package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -1
  161. package/dist/src/genomeSpy/viewDataInit.js +7 -3
  162. package/dist/src/genomeSpy.d.ts +1 -124
  163. package/dist/src/genomeSpy.d.ts.map +1 -1
  164. package/dist/src/genomeSpy.js +7 -688
  165. package/dist/src/genomeSpyBase.d.ts +133 -0
  166. package/dist/src/genomeSpyBase.d.ts.map +1 -0
  167. package/dist/src/genomeSpyBase.js +719 -0
  168. package/dist/src/gl/arrayBuilder.d.ts.map +1 -1
  169. package/dist/src/gl/arrayBuilder.js +0 -3
  170. package/dist/src/gl/colorUtils.d.ts.map +1 -1
  171. package/dist/src/gl/colorUtils.js +3 -0
  172. package/dist/src/gl/dataToVertices.d.ts +12 -14
  173. package/dist/src/gl/dataToVertices.d.ts.map +1 -1
  174. package/dist/src/gl/dataToVertices.js +121 -95
  175. package/dist/src/gl/glslScaleGenerator.d.ts +5 -2
  176. package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
  177. package/dist/src/gl/glslScaleGenerator.js +15 -15
  178. package/dist/src/gl/vertexRangeIndex.d.ts +23 -0
  179. package/dist/src/gl/vertexRangeIndex.d.ts.map +1 -0
  180. package/dist/src/gl/vertexRangeIndex.js +150 -0
  181. package/dist/src/index.d.ts +3 -9
  182. package/dist/src/index.d.ts.map +1 -1
  183. package/dist/src/index.js +5 -114
  184. package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +4462 -0
  185. package/dist/src/marks/link.d.ts.map +1 -1
  186. package/dist/src/marks/link.js +0 -23
  187. package/dist/src/marks/mark.d.ts +8 -1
  188. package/dist/src/marks/mark.d.ts.map +1 -1
  189. package/dist/src/marks/mark.js +61 -20
  190. package/dist/src/marks/markUtils.d.ts +18 -1
  191. package/dist/src/marks/markUtils.d.ts.map +1 -1
  192. package/dist/src/marks/markUtils.js +52 -4
  193. package/dist/src/marks/point.d.ts.map +1 -1
  194. package/dist/src/marks/point.js +6 -26
  195. package/dist/src/marks/rect.d.ts.map +1 -1
  196. package/dist/src/marks/rect.js +13 -21
  197. package/dist/src/marks/rule.d.ts +7 -2
  198. package/dist/src/marks/rule.d.ts.map +1 -1
  199. package/dist/src/marks/rule.js +125 -16
  200. package/dist/src/marks/text.d.ts.map +1 -1
  201. package/dist/src/marks/text.js +5 -47
  202. package/dist/src/minimal.d.ts +8 -0
  203. package/dist/src/minimal.d.ts.map +1 -0
  204. package/dist/src/minimal.js +21 -0
  205. package/dist/src/paramRuntime/expressionCompiler.d.ts +2 -1
  206. package/dist/src/paramRuntime/expressionCompiler.d.ts.map +1 -1
  207. package/dist/src/paramRuntime/expressionCompiler.js +3 -2
  208. package/dist/src/paramRuntime/expressionRef.d.ts +4 -1
  209. package/dist/src/paramRuntime/expressionRef.d.ts.map +1 -1
  210. package/dist/src/paramRuntime/expressionRef.js +10 -3
  211. package/dist/src/paramRuntime/graphRuntime.d.ts.map +1 -1
  212. package/dist/src/paramRuntime/graphRuntime.js +15 -6
  213. package/dist/src/paramRuntime/paramRuntime.d.ts +8 -2
  214. package/dist/src/paramRuntime/paramRuntime.d.ts.map +1 -1
  215. package/dist/src/paramRuntime/paramRuntime.js +10 -5
  216. package/dist/src/paramRuntime/types.d.ts +1 -0
  217. package/dist/src/paramRuntime/types.d.ts.map +1 -1
  218. package/dist/src/paramRuntime/types.js +1 -0
  219. package/dist/src/paramRuntime/viewParamRuntime.d.ts +24 -4
  220. package/dist/src/paramRuntime/viewParamRuntime.d.ts.map +1 -1
  221. package/dist/src/paramRuntime/viewParamRuntime.js +52 -6
  222. package/dist/src/scale/scale.d.ts.map +1 -1
  223. package/dist/src/scale/scale.js +14 -7
  224. package/dist/src/scales/axisResolution.d.ts.map +1 -1
  225. package/dist/src/scales/axisResolution.js +9 -5
  226. package/dist/src/scales/domainPlanner.d.ts +82 -16
  227. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  228. package/dist/src/scales/domainPlanner.js +364 -131
  229. package/dist/src/scales/indexLikeDomainUtils.d.ts +29 -0
  230. package/dist/src/scales/indexLikeDomainUtils.d.ts.map +1 -0
  231. package/dist/src/scales/indexLikeDomainUtils.js +67 -0
  232. package/dist/src/scales/resolutionMemberOrder.d.ts +15 -0
  233. package/dist/src/scales/resolutionMemberOrder.d.ts.map +1 -0
  234. package/dist/src/scales/resolutionMemberOrder.js +22 -0
  235. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  236. package/dist/src/scales/scaleInstanceManager.js +7 -2
  237. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  238. package/dist/src/scales/scaleInteractionController.js +43 -4
  239. package/dist/src/scales/scalePropsResolver.d.ts +5 -3
  240. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
  241. package/dist/src/scales/scalePropsResolver.js +108 -8
  242. package/dist/src/scales/scaleResolution.d.ts +35 -1
  243. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  244. package/dist/src/scales/scaleResolution.js +381 -66
  245. package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -1
  246. package/dist/src/scales/scaleResolutionTestUtils.js +6 -2
  247. package/dist/src/scales/scaleRules.d.ts.map +1 -1
  248. package/dist/src/scales/scaleRules.js +16 -2
  249. package/dist/src/scales/selectionDomainUtils.d.ts +30 -0
  250. package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -1
  251. package/dist/src/scales/selectionDomainUtils.js +116 -1
  252. package/dist/src/screenshotExport.d.ts +23 -0
  253. package/dist/src/screenshotExport.d.ts.map +1 -0
  254. package/dist/src/screenshotExport.js +44 -0
  255. package/dist/src/screenshotHarness.d.ts.map +1 -1
  256. package/dist/src/screenshotHarness.js +26 -25
  257. package/dist/src/spec/axis.d.ts +43 -32
  258. package/dist/src/spec/channel.d.ts +19 -13
  259. package/dist/src/spec/config.d.ts +264 -0
  260. package/dist/src/spec/data.d.ts +19 -0
  261. package/dist/src/spec/decoration.d.ts +51 -0
  262. package/dist/src/spec/exampleFiles.d.ts +12 -0
  263. package/dist/src/spec/exampleFiles.d.ts.map +1 -0
  264. package/dist/src/spec/exampleFiles.js +52 -0
  265. package/dist/src/spec/font.d.ts +1 -1
  266. package/dist/src/spec/mark.d.ts +97 -13
  267. package/dist/src/spec/parameter.d.ts +30 -10
  268. package/dist/src/spec/root.d.ts +14 -0
  269. package/dist/src/spec/scale.d.ts +31 -14
  270. package/dist/src/spec/title.d.ts +13 -2
  271. package/dist/src/spec/tooltip.d.ts +1 -1
  272. package/dist/src/spec/transform.d.ts +39 -4
  273. package/dist/src/spec/view.d.ts +67 -19
  274. package/dist/src/styles/genome-spy.css +55 -55
  275. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  276. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  277. package/dist/src/styles/genome-spy.css.js +23 -22
  278. package/dist/src/testSetup.d.ts +2 -0
  279. package/dist/src/testSetup.d.ts.map +1 -0
  280. package/dist/src/testSetup.js +5 -0
  281. package/dist/src/tooltip/dataTooltipHandler.js +8 -2
  282. package/dist/src/tooltip/tooltipContext.d.ts.map +1 -1
  283. package/dist/src/tooltip/tooltipContext.js +3 -2
  284. package/dist/src/types/embedApi.d.ts +7 -0
  285. package/dist/src/types/encoder.d.ts +17 -15
  286. package/dist/src/types/scaleResolutionApi.d.ts +20 -0
  287. package/dist/src/types/viewContext.d.ts +23 -1
  288. package/dist/src/utils/expression.d.ts +18 -10
  289. package/dist/src/utils/expression.d.ts.map +1 -1
  290. package/dist/src/utils/expression.js +354 -19
  291. package/dist/src/utils/field.d.ts.map +1 -1
  292. package/dist/src/utils/field.js +0 -1
  293. package/dist/src/utils/inertia.d.ts.map +1 -1
  294. package/dist/src/utils/inertia.js +0 -1
  295. package/dist/src/utils/inputBinding.d.ts +1 -1
  296. package/dist/src/utils/interaction.d.ts +109 -0
  297. package/dist/src/utils/interaction.d.ts.map +1 -0
  298. package/dist/src/utils/interaction.js +200 -0
  299. package/dist/src/utils/interactionEvent.d.ts +21 -42
  300. package/dist/src/utils/interactionEvent.d.ts.map +1 -1
  301. package/dist/src/utils/interactionEvent.js +43 -66
  302. package/dist/src/utils/kWayMerge.js +1 -1
  303. package/dist/src/utils/mergeObjects.d.ts.map +1 -1
  304. package/dist/src/utils/mergeObjects.js +0 -2
  305. package/dist/src/utils/radixSort.d.ts.map +1 -1
  306. package/dist/src/utils/radixSort.js +0 -2
  307. package/dist/src/utils/throttle.d.ts.map +1 -1
  308. package/dist/src/utils/throttle.js +0 -2
  309. package/dist/src/utils/ui/tooltip.d.ts +1 -0
  310. package/dist/src/utils/ui/tooltip.d.ts.map +1 -1
  311. package/dist/src/utils/ui/tooltip.js +1 -0
  312. package/dist/src/utils/url.js +1 -1
  313. package/dist/src/view/axisGridView.d.ts +1 -1
  314. package/dist/src/view/axisGridView.d.ts.map +1 -1
  315. package/dist/src/view/axisGridView.js +2 -47
  316. package/dist/src/view/axisView.d.ts +2 -3
  317. package/dist/src/view/axisView.d.ts.map +1 -1
  318. package/dist/src/view/axisView.js +251 -106
  319. package/dist/src/view/concatView.d.ts +2 -1
  320. package/dist/src/view/concatView.d.ts.map +1 -1
  321. package/dist/src/view/concatView.js +4 -2
  322. package/dist/src/view/containerMutationHelper.d.ts +3 -0
  323. package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
  324. package/dist/src/view/containerMutationHelper.js +4 -1
  325. package/dist/src/view/facetView.d.ts +1 -1
  326. package/dist/src/view/facetView.js +3 -3
  327. package/dist/src/view/flowBuilder.d.ts +1 -1
  328. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  329. package/dist/src/view/flowBuilder.js +13 -9
  330. package/dist/src/view/gridView/gridChild.d.ts +6 -0
  331. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  332. package/dist/src/view/gridView/gridChild.js +72 -43
  333. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  334. package/dist/src/view/gridView/gridView.js +255 -101
  335. package/dist/src/view/gridView/keyboardZoomController.d.ts +2 -2
  336. package/dist/src/view/gridView/keyboardZoomController.d.ts.map +1 -1
  337. package/dist/src/view/gridView/keyboardZoomController.js +1 -1
  338. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  339. package/dist/src/view/gridView/scrollbar.js +4 -2
  340. package/dist/src/view/gridView/selectionRect.d.ts +4 -0
  341. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  342. package/dist/src/view/gridView/selectionRect.js +20 -1
  343. package/dist/src/view/gridView/separatorView.d.ts +1 -0
  344. package/dist/src/view/gridView/separatorView.d.ts.map +1 -1
  345. package/dist/src/view/gridView/separatorView.js +9 -0
  346. package/dist/src/view/interactionRouting.d.ts +20 -0
  347. package/dist/src/view/interactionRouting.d.ts.map +1 -0
  348. package/dist/src/view/interactionRouting.js +53 -0
  349. package/dist/src/view/layerView.d.ts.map +1 -1
  350. package/dist/src/view/layerView.js +12 -9
  351. package/dist/src/view/layout/grid.js +1 -1
  352. package/dist/src/view/renderingContext/bufferedViewRenderingContext.d.ts.map +1 -1
  353. package/dist/src/view/renderingContext/bufferedViewRenderingContext.js +0 -2
  354. package/dist/src/view/resolutionPlanner.d.ts +9 -0
  355. package/dist/src/view/resolutionPlanner.d.ts.map +1 -0
  356. package/dist/src/view/resolutionPlanner.js +302 -0
  357. package/dist/src/view/testUtils.d.ts +17 -3
  358. package/dist/src/view/testUtils.d.ts.map +1 -1
  359. package/dist/src/view/testUtils.js +62 -69
  360. package/dist/src/view/title.d.ts +8 -1
  361. package/dist/src/view/title.d.ts.map +1 -1
  362. package/dist/src/view/title.js +66 -76
  363. package/dist/src/view/unitView.d.ts +1 -1
  364. package/dist/src/view/unitView.d.ts.map +1 -1
  365. package/dist/src/view/unitView.js +72 -169
  366. package/dist/src/view/view.d.ts +76 -30
  367. package/dist/src/view/view.d.ts.map +1 -1
  368. package/dist/src/view/view.js +138 -48
  369. package/dist/src/view/viewFactory.d.ts +11 -3
  370. package/dist/src/view/viewFactory.d.ts.map +1 -1
  371. package/dist/src/view/viewFactory.js +37 -11
  372. package/dist/src/view/viewUtils.d.ts.map +1 -1
  373. package/dist/src/view/viewUtils.js +41 -5
  374. package/dist/src/view/zoom.d.ts +2 -2
  375. package/dist/src/view/zoom.d.ts.map +1 -1
  376. package/dist/src/view/zoom.js +21 -23
  377. package/package.json +18 -10
  378. package/dist/bundle/AbortablePromiseCache-Dj0vzLnp.js +0 -149
  379. package/dist/bundle/browser-0iNU5Wit.js +0 -138
  380. package/dist/bundle/index-BYsZN7b0.js +0 -1597
  381. package/dist/bundle/index-C3kClAEN.js +0 -1771
  382. package/dist/bundle/index-C7wOh6y1.js +0 -657
  383. package/dist/bundle/index-CRaQAuki.js +0 -326
  384. package/dist/bundle/index-D9v1PCj9.js +0 -507
  385. package/dist/bundle/index-GDOuv_D5.js +0 -266
  386. package/dist/bundle/index-Gt44EOIH.js +0 -628
  387. package/dist/bundle/inflate-GtwLkvSP.js +0 -1048
  388. package/dist/bundle/parquetRead-BnAGCa4_.js +0 -1663
  389. package/dist/bundle/unzip-Bac01w6X.js +0 -1492
  390. package/dist/src/config/scaleDefaults.d.ts +0 -8
  391. package/dist/src/config/scaleDefaults.d.ts.map +0 -1
  392. package/dist/src/config/scaleDefaults.js +0 -45
@@ -28,15 +28,19 @@ import {
28
28
  isPrimaryPositionalChannel,
29
29
  isSecondaryChannel,
30
30
  } from "../encoder/encoder.js";
31
+ import { isExprRef } from "../paramRuntime/paramUtils.js";
31
32
  import { NominalDomain } from "../utils/domainArray.js";
32
33
  import { shallowArrayEquals } from "../utils/arrayUtils.js";
33
34
  import createIndexer from "../utils/indexer.js";
35
+ import { getCachedOrCall, invalidate } from "../utils/propertyCacher.js";
34
36
  import { resolveUrl } from "../utils/url.js";
37
+ import { orderResolutionMembers } from "./resolutionMemberOrder.js";
35
38
  import {
39
+ findIntervalSelectionBindingOwners,
36
40
  normalizeIntervalForSelection,
37
41
  requireIntervalSelection,
38
- requireParamRuntime,
39
42
  } from "./selectionDomainUtils.js";
43
+ import { toExternalIndexLikeInterval } from "./indexLikeDomainUtils.js";
40
44
 
41
45
  // Register scaleLocus to Vega-Scale.
42
46
  // Loci are discrete but the scale's domain can be adjusted in a continuous manner.
@@ -98,6 +102,9 @@ export default class ScaleResolution {
98
102
  /** @type {Set<ScaleResolutionMember>} */
99
103
  #dataDomainMembers = new Set();
100
104
 
105
+ /** @type {ScaleResolutionMember[] | undefined} */
106
+ #orderedMembers;
107
+
101
108
  /**
102
109
  * @type {Record<ScaleResolutionEventType, Set<ScaleResolutionListener>>}
103
110
  */
@@ -125,10 +132,28 @@ export default class ScaleResolution {
125
132
 
126
133
  #selectionReverseSyncSuppressionDepth = 0;
127
134
 
135
+ /** @type {(() => void)[]} */
136
+ #configuredDomainExprUnsubscribers = [];
137
+
138
+ #ignoreSelectionInitial = false;
139
+
140
+ /** @type {[number, number] | null | undefined} */
141
+ #lastLinkedSelectionInterval = undefined;
142
+
143
+ /** @type {import("../view/view.js").default | undefined} */
144
+ #hostView;
145
+
146
+ #resolvingScaleProps = 0;
147
+
148
+ #memberRegistrationBatchDepth = 0;
149
+
150
+ #membersDirty = false;
151
+
128
152
  /**
129
153
  * @param {Channel} channel
154
+ * @param {import("../view/view.js").default} [hostView]
130
155
  */
131
- constructor(channel) {
156
+ constructor(channel, hostView) {
132
157
  this.channel = channel;
133
158
  /** @type {import("../spec/channel.js").Type} Data type (quantitative, nominal, etc...) */
134
159
  this.type = null;
@@ -136,8 +161,11 @@ export default class ScaleResolution {
136
161
  /** @type {string} An optional unique identifier for the scale */
137
162
  this.name = undefined;
138
163
 
164
+ this.#hostView = hostView;
165
+
139
166
  this.#domainAggregator = new DomainPlanner({
140
- getMembers: () => this.#getActiveMembers(),
167
+ getActiveMembers: () => this.#getActiveMembers(),
168
+ getAllMembers: () => this.#members,
141
169
  getDataMembers: () =>
142
170
  this.#getActiveMembers(this.#dataDomainMembers),
143
171
  getType: () => this.type,
@@ -146,7 +174,7 @@ export default class ScaleResolution {
146
174
  });
147
175
 
148
176
  this.#scaleManager = new ScaleInstanceManager({
149
- getParamRuntime: () => this.#firstMemberView.paramRuntime,
177
+ getParamRuntime: () => this.#resolutionView.paramRuntime,
150
178
  onRangeChange: () => this.#notifyListeners("range"),
151
179
  onDomainChange: () => this.#notifyListeners("domain"),
152
180
  getGenomeStore: () => this.#viewContext.genomeStore,
@@ -157,8 +185,7 @@ export default class ScaleResolution {
157
185
  getAnimator: () => this.#viewContext.animator,
158
186
  getInitialDomainSnapshot: () =>
159
187
  this.#domainAggregator.initialDomainSnapshot,
160
- getResetDomain: () =>
161
- this.#domainAggregator.getConfiguredOrDefaultDomain(),
188
+ getResetDomain: () => this.#getConfiguredOrDefaultDomain(),
162
189
  fromComplexInterval: this.fromComplexInterval.bind(this),
163
190
  getGenomeExtent: () => this.#getLocusExtent(),
164
191
  });
@@ -175,6 +202,10 @@ export default class ScaleResolution {
175
202
  return first.view;
176
203
  }
177
204
 
205
+ get #resolutionView() {
206
+ return this.#hostView ?? this.#firstMemberView;
207
+ }
208
+
178
209
  /**
179
210
  * @param {Set<ScaleResolutionMember>} [members]
180
211
  */
@@ -199,7 +230,7 @@ export default class ScaleResolution {
199
230
  }
200
231
 
201
232
  get #viewContext() {
202
- return this.#firstMemberView.context;
233
+ return this.#resolutionView.context;
203
234
  }
204
235
 
205
236
  get zoomExtent() {
@@ -263,7 +294,7 @@ export default class ScaleResolution {
263
294
  type === "domain" &&
264
295
  this.#selectionReverseSyncSuppressionDepth === 0
265
296
  ) {
266
- this.#syncLinkedSelectionFromDomain();
297
+ this.syncLinkedSelectionFromDomain();
267
298
  }
268
299
 
269
300
  for (const listener of this.#listeners[type].values()) {
@@ -286,27 +317,15 @@ export default class ScaleResolution {
286
317
  }
287
318
  }
288
319
 
289
- #syncLinkedSelectionFromDomain() {
320
+ syncLinkedSelectionFromDomain() {
290
321
  const linkInfo =
291
- this.#domainAggregator.getSelectionConfiguredDomainInfo();
292
- if (!linkInfo) {
293
- return;
294
- }
295
-
296
- const shouldReverseSync =
297
- linkInfo.sync === "twoWay" ||
298
- (linkInfo.sync === "auto" && this.isZoomable());
299
- if (!shouldReverseSync) {
322
+ this.#domainAggregator.getSelectionConfiguredDomainBindingInfo();
323
+ if (!linkInfo || !this.isZoomable()) {
300
324
  return;
301
325
  }
302
326
 
303
- const runtime = requireParamRuntime(
304
- this.#firstMemberView.paramRuntime,
305
- linkInfo.param
306
- );
307
-
308
327
  const selection = requireIntervalSelection(
309
- runtime.getValue(linkInfo.param),
328
+ linkInfo.runtime.getValue(linkInfo.param),
310
329
  linkInfo.param
311
330
  );
312
331
 
@@ -332,7 +351,7 @@ export default class ScaleResolution {
332
351
  return;
333
352
  }
334
353
 
335
- runtime.setValue(linkInfo.param, {
354
+ linkInfo.runtime.setValue(linkInfo.param, {
336
355
  ...selection,
337
356
  type: "interval",
338
357
  intervals: {
@@ -350,6 +369,71 @@ export default class ScaleResolution {
350
369
  return normalizeIntervalForSelection(domain, this.zoomExtent);
351
370
  }
352
371
 
372
+ #getLinkedSelectionInfo() {
373
+ return this.#domainAggregator.getSelectionConfiguredDomainBindingInfo();
374
+ }
375
+
376
+ #shouldIncludeSelectionInitial() {
377
+ return !this.#ignoreSelectionInitial;
378
+ }
379
+
380
+ /**
381
+ * @param {boolean} [extractDataDomain]
382
+ * @param {import("../spec/scale.js").Scale["assembly"]} [locusAssembly]
383
+ * @returns {any[]}
384
+ */
385
+ #getConfiguredOrDefaultDomain(extractDataDomain = false, locusAssembly) {
386
+ return this.#domainAggregator.getConfiguredOrDefaultDomain(
387
+ extractDataDomain,
388
+ locusAssembly,
389
+ {
390
+ includeSelectionInitial: this.#shouldIncludeSelectionInitial(),
391
+ }
392
+ );
393
+ }
394
+
395
+ /**
396
+ * @returns {boolean}
397
+ */
398
+ #hasConfiguredDomain() {
399
+ for (const member of this.#members) {
400
+ if (
401
+ member.contributesToDomain &&
402
+ member.channelDef.scale?.domain !== undefined
403
+ ) {
404
+ return true;
405
+ }
406
+ }
407
+ return false;
408
+ }
409
+
410
+ /**
411
+ * @param {import("./domainPlanner.js").SelectionDomainLinkInfo} linkInfo
412
+ * @returns {[number, number] | null}
413
+ */
414
+ #getCurrentLinkedSelectionInterval(linkInfo) {
415
+ const selection = requireIntervalSelection(
416
+ linkInfo.runtime.getValue(linkInfo.param),
417
+ linkInfo.param
418
+ );
419
+ const interval = selection.intervals[linkInfo.encoding];
420
+ return interval && interval.length === 2
421
+ ? /** @type {[number, number]} */ (interval)
422
+ : null;
423
+ }
424
+
425
+ /**
426
+ * @param {[number, number] | null} previousInterval
427
+ * @param {[number, number] | null} nextInterval
428
+ */
429
+ #updateSelectionInitialBypass(previousInterval, nextInterval) {
430
+ if (nextInterval) {
431
+ this.#ignoreSelectionInitial = false;
432
+ } else if (previousInterval) {
433
+ this.#ignoreSelectionInitial = true;
434
+ }
435
+ }
436
+
353
437
  /**
354
438
  * Add a view to this resolution.
355
439
  * N.B. This is expected to be called in depth-first order
@@ -413,12 +497,16 @@ export default class ScaleResolution {
413
497
  if (!this.type) {
414
498
  this.type = type;
415
499
  } else if (type !== this.type && !isSecondaryChannel(channel)) {
500
+ // TODO: Revisit shared discrete positional scales when
501
+ // implementing Vega-Lite-like band-vs-point inference.
502
+ // Nominal/ordinal members should be able to share a scale even
503
+ // if one member wants "point" and another wants "band"; the
504
+ // merged result should resolve to "band". Explicit
505
+ // user-specified incompatible scale types should still error.
416
506
  // TODO: Include a reference to the layer
417
507
  throw new Error(
418
508
  `Can not use shared scale for different data types: ${this.type} vs. ${type}. Use "resolve: independent" for channel ${this.channel}`
419
509
  );
420
- // Actually, point scale could be changed into band scale
421
- // TODO: Use the same merging logic as in: https://github.com/vega/vega-lite/blob/master/src/scale.ts
422
510
  }
423
511
  }
424
512
 
@@ -426,23 +514,67 @@ export default class ScaleResolution {
426
514
  if (member.contributesToDomain) {
427
515
  this.#dataDomainMembers.add(member);
428
516
  }
429
- this.#domainAggregator.invalidateConfiguredDomain();
430
- this.#refreshSelectionDomainParamSubscriptions();
431
517
  return member;
432
518
  }
433
519
 
520
+ #syncMembers() {
521
+ this.#membersDirty = false;
522
+ this.#invalidateOrderedMembers();
523
+ this.#invalidateConfiguredDomain();
524
+ this.#refreshSelectionDomainParamSubscriptions();
525
+ this.#refreshConfiguredDomainExprSubscriptions();
526
+ }
527
+
528
+ #markMembersDirty() {
529
+ if (this.#memberRegistrationBatchDepth > 0) {
530
+ this.#membersDirty = true;
531
+ } else {
532
+ this.#syncMembers();
533
+ }
534
+ }
535
+
536
+ /**
537
+ * Executes a group of member registrations without refreshing derived
538
+ * membership state until the callback completes.
539
+ *
540
+ * @template T
541
+ * @param {Iterable<ScaleResolution>} resolutions
542
+ * @param {() => T} callback
543
+ * @returns {T}
544
+ */
545
+ static registerInBatch(resolutions, callback) {
546
+ const batchedResolutions = Array.from(resolutions);
547
+ for (const resolution of batchedResolutions) {
548
+ resolution.#memberRegistrationBatchDepth++;
549
+ }
550
+
551
+ try {
552
+ return callback();
553
+ } finally {
554
+ for (const resolution of batchedResolutions) {
555
+ resolution.#memberRegistrationBatchDepth--;
556
+ if (
557
+ resolution.#memberRegistrationBatchDepth === 0 &&
558
+ resolution.#membersDirty
559
+ ) {
560
+ resolution.#syncMembers();
561
+ }
562
+ }
563
+ }
564
+ }
565
+
434
566
  /**
435
567
  * @param {ScaleResolutionMember} member
436
568
  * @returns {() => boolean}
437
569
  */
438
570
  registerMember(member) {
439
571
  const registeredMember = this.#addMember(member);
572
+ this.#markMembersDirty();
440
573
  return () => {
441
574
  const removed = this.#members.delete(registeredMember);
442
575
  if (removed) {
443
576
  this.#dataDomainMembers.delete(registeredMember);
444
- this.#domainAggregator.invalidateConfiguredDomain();
445
- this.#refreshSelectionDomainParamSubscriptions();
577
+ this.#markMembersDirty();
446
578
  }
447
579
  return removed && this.#members.size === 0;
448
580
  };
@@ -450,6 +582,7 @@ export default class ScaleResolution {
450
582
 
451
583
  dispose() {
452
584
  this.#clearSelectionDomainParamSubscriptions();
585
+ this.#clearConfiguredDomainExprSubscriptions();
453
586
  this.#listeners.domain.clear();
454
587
  this.#listeners.range.clear();
455
588
  this.#scaleManager.dispose();
@@ -460,6 +593,14 @@ export default class ScaleResolution {
460
593
  unsubscribe();
461
594
  }
462
595
  this.#selectionDomainParamUnsubscribers = [];
596
+ this.#lastLinkedSelectionInterval = undefined;
597
+ }
598
+
599
+ #clearConfiguredDomainExprSubscriptions() {
600
+ for (const unsubscribe of this.#configuredDomainExprUnsubscribers) {
601
+ unsubscribe();
602
+ }
603
+ this.#configuredDomainExprUnsubscribers = [];
463
604
  }
464
605
 
465
606
  #refreshSelectionDomainParamSubscriptions() {
@@ -469,25 +610,57 @@ export default class ScaleResolution {
469
610
  return;
470
611
  }
471
612
 
472
- const linkInfo =
473
- this.#domainAggregator.getSelectionConfiguredDomainInfo();
613
+ const linkInfo = this.#getLinkedSelectionInfo();
474
614
  if (!linkInfo) {
475
615
  return;
476
616
  }
477
617
 
478
- const runtime = requireParamRuntime(
479
- this.#firstMemberView.paramRuntime,
480
- linkInfo.param
481
- );
618
+ this.#lastLinkedSelectionInterval =
619
+ this.#getCurrentLinkedSelectionInterval(linkInfo);
482
620
 
483
621
  this.#selectionDomainParamUnsubscribers.push(
484
- runtime.subscribe(linkInfo.param, () => {
485
- this.#domainAggregator.invalidateConfiguredDomain();
622
+ linkInfo.runtime.subscribe(linkInfo.param, () => {
623
+ const previousInterval = this.#lastLinkedSelectionInterval;
624
+ const currentInterval =
625
+ this.#getCurrentLinkedSelectionInterval(linkInfo);
626
+ this.#updateSelectionInitialBypass(
627
+ previousInterval,
628
+ currentInterval
629
+ );
630
+ this.#lastLinkedSelectionInterval = currentInterval;
631
+ this.#invalidateConfiguredDomain();
486
632
  this.reconfigureDomain();
487
633
  })
488
634
  );
489
635
  }
490
636
 
637
+ #refreshConfiguredDomainExprSubscriptions() {
638
+ this.#clearConfiguredDomainExprSubscriptions();
639
+
640
+ if (this.#members.size === 0) {
641
+ return;
642
+ }
643
+
644
+ const listener = () => {
645
+ this.#invalidateConfiguredDomain();
646
+ this.reconfigureDomain();
647
+ };
648
+
649
+ for (const member of this.#members) {
650
+ if (!member.contributesToDomain) {
651
+ continue;
652
+ }
653
+ const domain = member.channelDef.scale?.domain;
654
+ if (!isExprRef(domain)) {
655
+ continue;
656
+ }
657
+
658
+ const expr = member.view.paramRuntime.createExpression(domain.expr);
659
+ const unsubscribe = expr.subscribe(listener);
660
+ this.#configuredDomainExprUnsubscribers.push(unsubscribe);
661
+ }
662
+ }
663
+
491
664
  #hasRenderedMember() {
492
665
  for (const member of this.#members) {
493
666
  if (member.view.hasRendered()) {
@@ -539,11 +712,11 @@ export default class ScaleResolution {
539
712
  /**
540
713
  * Returns true if the domain has been defined explicitly, i.e. not extracted from the data.
541
714
  */
542
- #isExplicitDomain() {
543
- return this.#domainAggregator.hasConfiguredDomain();
715
+ isDomainDefinedExplicitly() {
716
+ return this.#hasConfiguredDomain();
544
717
  }
545
718
 
546
- #isDomainInitialized() {
719
+ isDomainInitialized() {
547
720
  const s = this.#scaleManager.scale;
548
721
  if (!s) {
549
722
  return false;
@@ -569,14 +742,68 @@ export default class ScaleResolution {
569
742
  * @returns {import("../spec/scale.js").Scale}
570
743
  */
571
744
  #getMergedScaleProps() {
572
- return resolveScalePropsBase({
573
- channel: this.channel,
574
- dataType: this.type,
575
- members: this.#members,
576
- isExplicitDomain: this.#isExplicitDomain(),
745
+ return getCachedOrCall(this, "mergedScaleProps", () => {
746
+ const props = resolveScalePropsBase({
747
+ channel: this.channel,
748
+ dataType: this.type,
749
+ orderedMembers: this.#getOrderedMembers(),
750
+ isExplicitDomain: this.isDomainDefinedExplicitly(),
751
+ configScopes: this.#resolutionView.getConfigScopes(),
752
+ });
753
+ this.#validateLinkedSelectionConfiguration(props);
754
+ return props;
577
755
  });
578
756
  }
579
757
 
758
+ #invalidateMergedScaleProps() {
759
+ invalidate(this, "mergedScaleProps");
760
+ }
761
+
762
+ #invalidateOrderedMembers() {
763
+ this.#orderedMembers = undefined;
764
+ }
765
+
766
+ /**
767
+ * Returns the participating members in a stable order.
768
+ *
769
+ * The membership set changes rarely, so cache the sorted order separately
770
+ * from merged scale props. That keeps parameter-driven domain updates from
771
+ * re-running the same path-based sort work.
772
+ *
773
+ * @returns {ScaleResolutionMember[]}
774
+ */
775
+ #getOrderedMembers() {
776
+ if (!this.#orderedMembers) {
777
+ this.#orderedMembers = orderResolutionMembers(this.#members);
778
+ }
779
+ return this.#orderedMembers;
780
+ }
781
+
782
+ #invalidateConfiguredDomain() {
783
+ this.#domainAggregator.invalidateConfiguredDomain();
784
+ this.#invalidateMergedScaleProps();
785
+ }
786
+
787
+ /**
788
+ * @param {import("../spec/scale.js").Scale} props
789
+ */
790
+ #validateLinkedSelectionConfiguration(props) {
791
+ const linkInfo = this.#getLinkedSelectionInfo();
792
+ if (!linkInfo || props === null || props.type === "null") {
793
+ return;
794
+ }
795
+
796
+ const isZoomable =
797
+ isContinuous(props.type) && !isDiscrete(props.type) && !!props.zoom;
798
+
799
+ if (linkInfo.hasInitial && !isZoomable) {
800
+ throw new Error(
801
+ `Selection domain reference "${linkInfo.param}.${linkInfo.encoding}" cannot use "initial" with a non-zoomable ${this.channel} scale. ` +
802
+ `Enable zoom on the linked scale or remove "initial".`
803
+ );
804
+ }
805
+ }
806
+
580
807
  /**
581
808
  * Returns locus assembly requirements without initializing the scale.
582
809
  *
@@ -604,6 +831,23 @@ export default class ScaleResolution {
604
831
  };
605
832
  }
606
833
 
834
+ /**
835
+ * Returns the resolved scale type without instantiating the scale.
836
+ *
837
+ * Useful during view construction, before assembly preflight has loaded
838
+ * URL-backed locus genomes.
839
+ *
840
+ * @returns {import("../spec/scale.js").Scale["type"] | undefined}
841
+ */
842
+ getResolvedScaleType() {
843
+ const props = this.#getMergedScaleProps();
844
+ if (props === null || props.type === "null") {
845
+ return undefined;
846
+ }
847
+
848
+ return props.type;
849
+ }
850
+
607
851
  /**
608
852
  * Returns the merged scale properties supplemented with inferred properties
609
853
  * and domain.
@@ -619,13 +863,23 @@ export default class ScaleResolution {
619
863
  return { type: "null" };
620
864
  }
621
865
 
622
- const domain = this.#domainAggregator.getConfiguredOrDefaultDomain(
623
- extractDataDomain,
624
- props.type === LOCUS ? props.assembly : undefined
625
- );
866
+ const resolvedProps = { ...props };
867
+
868
+ this.#resolvingScaleProps += 1;
869
+ let domain;
870
+ try {
871
+ domain = this.#getConfiguredOrDefaultDomain(
872
+ extractDataDomain,
873
+ resolvedProps.type === LOCUS
874
+ ? resolvedProps.assembly
875
+ : undefined
876
+ );
877
+ } finally {
878
+ this.#resolvingScaleProps -= 1;
879
+ }
626
880
 
627
- if (isDiscrete(props.type)) {
628
- const isExplicit = this.#isExplicitDomain();
881
+ if (isDiscrete(resolvedProps.type)) {
882
+ const isExplicit = this.isDomainDefinedExplicitly();
629
883
  const indexer = this.#getCategoricalIndexer(isExplicit);
630
884
  if (domain != null) {
631
885
  if (
@@ -641,7 +895,7 @@ export default class ScaleResolution {
641
895
  const indexedDomain = indexer
642
896
  .domain()
643
897
  .filter((value) => active.has(value));
644
- props.domain =
898
+ resolvedProps.domain =
645
899
  indexedDomain.length > 0
646
900
  ? /** @type {import("../spec/scale.js").ScalarDomain} */ (
647
901
  indexedDomain
@@ -649,7 +903,7 @@ export default class ScaleResolution {
649
903
  : new NominalDomain();
650
904
  } else {
651
905
  const indexedDomain = indexer.domain();
652
- props.domain =
906
+ resolvedProps.domain =
653
907
  indexedDomain.length > 0
654
908
  ? /** @type {import("../spec/scale.js").ScalarDomain} */ (
655
909
  indexedDomain
@@ -657,18 +911,21 @@ export default class ScaleResolution {
657
911
  : new NominalDomain();
658
912
  }
659
913
  // Scale props are spec-shaped; keep the indexer off the public type.
660
- /** @type {any} */ (props).domainIndexer = indexer;
914
+ /** @type {any} */ (resolvedProps).domainIndexer = indexer;
661
915
  } else if (domain && domain.length > 0) {
662
- props.domain = domain;
916
+ resolvedProps.domain = domain;
663
917
  }
664
918
 
665
- if (!props.domain && props.domainMid !== undefined) {
919
+ if (!resolvedProps.domain && resolvedProps.domainMid !== undefined) {
666
920
  // Initialize with a bogus domain so that scale.js can inject the domainMid.
667
921
  // The number of domain elements must be know before the glsl scale is generated.
668
- props.domain = [props.domainMin ?? 0, props.domainMax ?? 1];
922
+ resolvedProps.domain = [
923
+ resolvedProps.domainMin ?? 0,
924
+ resolvedProps.domainMax ?? 1,
925
+ ];
669
926
  }
670
927
 
671
- return props;
928
+ return resolvedProps;
672
929
  }
673
930
 
674
931
  /**
@@ -693,7 +950,7 @@ export default class ScaleResolution {
693
950
  */
694
951
  reconfigure() {
695
952
  this.#withSelectionReverseSyncSuppressed(() => {
696
- this.#domainAggregator.invalidateConfiguredDomain();
953
+ this.#invalidateConfiguredDomain();
697
954
  const state = this.#computeScaleState(true);
698
955
  if (!state) {
699
956
  return;
@@ -735,6 +992,7 @@ export default class ScaleResolution {
735
992
  });
736
993
  }
737
994
  this.#finalizeReconfigure(state);
995
+ this.syncLinkedSelectionFromDomain();
738
996
  });
739
997
  }
740
998
 
@@ -762,7 +1020,7 @@ export default class ScaleResolution {
762
1020
  scale,
763
1021
  props: this.#getScaleProps(extractDataDomain),
764
1022
  previousDomain: scale.domain(),
765
- domainWasInitialized: this.#isDomainInitialized(),
1023
+ domainWasInitialized: this.isDomainInitialized(),
766
1024
  hasSelectionConfiguredDomain:
767
1025
  this.#domainAggregator.hasSelectionConfiguredDomain(),
768
1026
  };
@@ -808,10 +1066,15 @@ export default class ScaleResolution {
808
1066
  hasSelectionConfiguredDomain,
809
1067
  } = inputs;
810
1068
 
1069
+ const initialDomainSnapshot = hasSelectionConfiguredDomain
1070
+ ? this.#domainAggregator.getDefaultDomain(true)
1071
+ : undefined;
1072
+
811
1073
  if (
812
1074
  this.#domainAggregator.captureInitialDomain(
813
1075
  scale,
814
- domainWasInitialized
1076
+ domainWasInitialized,
1077
+ initialDomainSnapshot
815
1078
  )
816
1079
  ) {
817
1080
  // Domain changes were suppressed during reconfigure; notify explicitly.
@@ -819,6 +1082,11 @@ export default class ScaleResolution {
819
1082
  return;
820
1083
  }
821
1084
 
1085
+ if (scale.props.domainTransition === false) {
1086
+ this.#notifyListeners("domain");
1087
+ return;
1088
+ }
1089
+
822
1090
  const newDomain = scale.domain();
823
1091
  const action = this.#interactionController.getDomainChangeAction(
824
1092
  previousDomain,
@@ -878,6 +1146,11 @@ export default class ScaleResolution {
878
1146
  * @returns {ScaleWithProps}
879
1147
  */
880
1148
  getScale() {
1149
+ if (this.#resolvingScaleProps > 0) {
1150
+ throw new Error(
1151
+ `Scale resolution for channel "${this.channel}" cannot read its own scale while its domain is being resolved.`
1152
+ );
1153
+ }
881
1154
  return this.#scaleManager.scale ?? this.initializeScale();
882
1155
  }
883
1156
 
@@ -898,6 +1171,13 @@ export default class ScaleResolution {
898
1171
  }
899
1172
 
900
1173
  getDomain() {
1174
+ if (this.#resolvingScaleProps > 0) {
1175
+ throw new Error(
1176
+ `Scale resolution for channel "${this.channel}" cannot read its own domain while its domain is being resolved.`
1177
+ );
1178
+ }
1179
+ // The underlying scale getter returns a fresh array. Treat this as a
1180
+ // read-only snapshot rather than a mutable backing store.
901
1181
  return this.getScale().domain();
902
1182
  }
903
1183
 
@@ -915,10 +1195,39 @@ export default class ScaleResolution {
915
1195
  */
916
1196
  getComplexDomain() {
917
1197
  return /** @type {NumericDomain | ComplexDomain} */ (
918
- toComplexInterval(this.#getGenomeSource(), this.getDomain())
1198
+ toComplexInterval(
1199
+ this.#getGenomeSource(),
1200
+ toExternalIndexLikeInterval(this.type, this.getDomain())
1201
+ )
919
1202
  );
920
1203
  }
921
1204
 
1205
+ /**
1206
+ * Returns metadata about a selection-linked domain, if present.
1207
+ */
1208
+ getLinkedSelectionDomainInfo() {
1209
+ const linkInfo = this.#getLinkedSelectionInfo();
1210
+ if (!linkInfo) {
1211
+ return;
1212
+ }
1213
+
1214
+ const root = this.#resolutionView.getLayoutAncestors().at(-1);
1215
+ const persist = root
1216
+ ? findIntervalSelectionBindingOwners(
1217
+ root,
1218
+ linkInfo.runtime,
1219
+ linkInfo.param,
1220
+ linkInfo.encoding
1221
+ ).some((owner) => owner.param.persist !== false)
1222
+ : false;
1223
+
1224
+ return {
1225
+ param: linkInfo.param,
1226
+ encoding: linkInfo.encoding,
1227
+ persist,
1228
+ };
1229
+ }
1230
+
922
1231
  /**
923
1232
  * Return true if the scale is zoomable and the current domain differs from the initial domain.
924
1233
  *
@@ -932,8 +1241,14 @@ export default class ScaleResolution {
932
1241
  * Returns true if zooming is supported and allowed in view spec.
933
1242
  */
934
1243
  isZoomable() {
935
- // Check explicit configuration
936
- return this.#interactionController.isZoomable();
1244
+ const props = this.#getMergedScaleProps();
1245
+ if (props === null || props.type === "null") {
1246
+ return false;
1247
+ }
1248
+
1249
+ return (
1250
+ isContinuous(props.type) && !isDiscrete(props.type) && !!props.zoom
1251
+ );
937
1252
  }
938
1253
 
939
1254
  /**