ggaction 0.0.13 → 0.0.16

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 (359) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +41 -12
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +39 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/basic.js +2 -2
  13. package/src/actions/boxPlots/components.js +20 -12
  14. package/src/actions/boxPlots/create.js +4 -5
  15. package/src/actions/boxPlots/edit.js +34 -9
  16. package/src/actions/boxPlots/materialize.js +12 -4
  17. package/src/actions/boxPlots/options.js +48 -16
  18. package/src/actions/canvas/actions.js +3 -3
  19. package/src/actions/canvas/fitting.js +1 -1
  20. package/src/actions/categoryOrder/index.js +21 -12
  21. package/src/actions/charts/area.js +6 -2
  22. package/src/actions/charts/bar.js +3 -2
  23. package/src/actions/charts/beeswarm.js +1 -1
  24. package/src/actions/charts/density.js +7 -3
  25. package/src/actions/charts/ecdf.js +6 -3
  26. package/src/actions/charts/endpoints.js +29 -7
  27. package/src/actions/charts/heatmap.js +3 -2
  28. package/src/actions/charts/histogram.js +5 -4
  29. package/src/actions/charts/horizon.js +4 -2
  30. package/src/actions/charts/interval-regression.js +7 -4
  31. package/src/actions/charts/line.js +4 -2
  32. package/src/actions/charts/parallel.js +9 -3
  33. package/src/actions/charts/pie.js +11 -3
  34. package/src/actions/charts/polar.js +17 -6
  35. package/src/actions/charts/radar.js +9 -3
  36. package/src/actions/charts/radial.js +12 -4
  37. package/src/actions/charts/raincloud.js +2 -2
  38. package/src/actions/charts/rug-strip.js +8 -4
  39. package/src/actions/charts/scatter.js +4 -2
  40. package/src/actions/composition/actions.js +29 -24
  41. package/src/actions/coordinates/actions.js +2 -2
  42. package/src/actions/coordinates/edit.js +76 -0
  43. package/src/actions/data/basicBin2d.js +4 -5
  44. package/src/actions/data/bin.js +3 -3
  45. package/src/actions/data/bin2d.js +63 -193
  46. package/src/actions/data/bin2dMaterialize.js +4 -5
  47. package/src/actions/data/box.js +4 -4
  48. package/src/actions/data/complete.js +26 -0
  49. package/src/actions/data/computed.js +4 -4
  50. package/src/actions/data/create.js +7 -14
  51. package/src/actions/data/density.js +15 -23
  52. package/src/actions/data/derived.js +57 -31
  53. package/src/actions/data/ecdf.js +2 -2
  54. package/src/actions/data/edit.js +551 -0
  55. package/src/actions/data/filter.js +28 -22
  56. package/src/actions/data/fold.js +2 -2
  57. package/src/actions/data/gradientProfile.js +2 -2
  58. package/src/actions/data/horizon.js +2 -2
  59. package/src/actions/data/impute.js +26 -0
  60. package/src/actions/data/index.js +62 -47
  61. package/src/actions/data/interval.js +2 -2
  62. package/src/actions/data/intervalEdit.js +26 -18
  63. package/src/actions/data/normalize.js +27 -0
  64. package/src/actions/data/regression.js +21 -27
  65. package/src/actions/data/revise.js +48 -0
  66. package/src/actions/data/revisionBindings.js +125 -0
  67. package/src/actions/data/shared.js +20 -4
  68. package/src/actions/data/stack.js +2 -2
  69. package/src/actions/data/summary.js +3 -3
  70. package/src/actions/data/timeUnit.js +6 -3
  71. package/src/actions/data/window.js +3 -3
  72. package/src/actions/encodings/angle.js +1 -1
  73. package/src/actions/encodings/appearance.js +4 -4
  74. package/src/actions/encodings/barWidth.js +1 -1
  75. package/src/actions/encodings/channels.js +415 -0
  76. package/src/actions/encodings/color/index.js +3 -2
  77. package/src/actions/encodings/density.js +7 -4
  78. package/src/actions/encodings/histogram.js +4 -3
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/index.js +4 -0
  81. package/src/actions/encodings/offset.js +22 -15
  82. package/src/actions/encodings/parallel.js +4 -5
  83. package/src/actions/encodings/pathOrder.js +6 -11
  84. package/src/actions/encodings/position/apply.js +53 -7
  85. package/src/actions/encodings/position/index.js +14 -6
  86. package/src/actions/encodings/position/policies/bar.js +9 -0
  87. package/src/actions/encodings/position/resolve.js +24 -7
  88. package/src/actions/encodings/ranged.js +5 -5
  89. package/src/actions/encodings/remove.js +13 -9
  90. package/src/actions/encodings/ruleAppearance.js +2 -24
  91. package/src/actions/encodings/seriesLayout.js +2 -1
  92. package/src/actions/encodings/shared.js +82 -14
  93. package/src/actions/encodings/stroke.js +226 -0
  94. package/src/actions/encodings/strokeDash.js +2 -2
  95. package/src/actions/encodings/text.js +1 -1
  96. package/src/actions/errorBands/create.js +34 -20
  97. package/src/actions/errorBands/edit.js +34 -137
  98. package/src/actions/errorBars/create.js +43 -20
  99. package/src/actions/errorBars/edit.js +29 -125
  100. package/src/actions/facets/actions.js +267 -73
  101. package/src/actions/facets/derive.js +99 -28
  102. package/src/actions/facets/guides.js +4 -5
  103. package/src/actions/facets/replay.js +4 -5
  104. package/src/actions/gradientPlots/components.js +15 -8
  105. package/src/actions/gradientPlots/create.js +4 -5
  106. package/src/actions/gradientPlots/edit.js +18 -9
  107. package/src/actions/gradientPlots/materialize.js +5 -3
  108. package/src/actions/gradientPlots/options.js +10 -2
  109. package/src/actions/gradientPlots/rebind.js +4 -5
  110. package/src/actions/guides/applicability.js +8 -2
  111. package/src/actions/guides/axes/axes.js +23 -4
  112. package/src/actions/guides/axes/axis.js +2 -2
  113. package/src/actions/guides/axes/edit.js +2 -2
  114. package/src/actions/guides/axes/labels.js +67 -21
  115. package/src/actions/guides/axes/lines.js +15 -9
  116. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  117. package/src/actions/guides/axes/parallel/resolve.js +6 -2
  118. package/src/actions/guides/axes/parallel.js +5 -5
  119. package/src/actions/guides/axes/remove.js +4 -4
  120. package/src/actions/guides/axes/tickGroups.js +6 -5
  121. package/src/actions/guides/axes/ticks.js +19 -6
  122. package/src/actions/guides/axes/titles.js +21 -13
  123. package/src/actions/guides/grids/grid.js +18 -29
  124. package/src/actions/guides/grids/resolve.js +7 -2
  125. package/src/actions/guides/guides.js +20 -4
  126. package/src/actions/guides/legends/blocks.js +426 -0
  127. package/src/actions/guides/legends/categorical/actions.js +138 -52
  128. package/src/actions/guides/legends/categorical/components.js +55 -45
  129. package/src/actions/guides/legends/categorical/index.js +37 -33
  130. package/src/actions/guides/legends/categorical/layout.js +98 -16
  131. package/src/actions/guides/legends/categorical/options.js +2 -2
  132. package/src/actions/guides/legends/categorical/recipes.js +111 -20
  133. package/src/actions/guides/legends/categorical/resolve.js +12 -8
  134. package/src/actions/guides/legends/categorical/symbols.js +104 -52
  135. package/src/actions/guides/legends/continuous/common.js +76 -20
  136. package/src/actions/guides/legends/continuous/gradient.js +95 -158
  137. package/src/actions/guides/legends/continuous/index.js +2 -0
  138. package/src/actions/guides/legends/continuous/interval.js +21 -16
  139. package/src/actions/guides/legends/continuous/opacity.js +37 -83
  140. package/src/actions/guides/legends/continuous/stroke.js +225 -0
  141. package/src/actions/guides/legends/creation.js +14 -1
  142. package/src/actions/guides/legends/edit.js +159 -60
  143. package/src/actions/guides/legends/focused.js +5 -5
  144. package/src/actions/guides/legends/index.js +2 -0
  145. package/src/actions/guides/legends/lane.js +59 -24
  146. package/src/actions/guides/legends/lifecycle.js +19 -8
  147. package/src/actions/guides/legends/remove.js +17 -8
  148. package/src/actions/guides/legends/sampling.js +140 -0
  149. package/src/actions/guides/legends/size.js +100 -108
  150. package/src/actions/guides/legends/strokeWidth.js +38 -92
  151. package/src/actions/guides/legends/target.js +112 -1
  152. package/src/actions/guides/legends/transition.js +165 -16
  153. package/src/actions/guides/polar/axes/facade.js +8 -8
  154. package/src/actions/guides/polar/axes/labels.js +29 -10
  155. package/src/actions/guides/polar/axes/lines.js +19 -19
  156. package/src/actions/guides/polar/axes/shared.js +16 -0
  157. package/src/actions/guides/polar/axes/ticks.js +10 -16
  158. package/src/actions/guides/polar/axes/titles.js +6 -6
  159. package/src/actions/guides/polar/grids.js +12 -14
  160. package/src/actions/guides/polar/resolve.js +12 -8
  161. package/src/actions/index.js +16 -3
  162. package/src/actions/intervals/revision.js +126 -0
  163. package/src/actions/marks/arc/actions.js +80 -22
  164. package/src/actions/marks/area/actions.js +38 -13
  165. package/src/actions/marks/area/materialize.js +17 -1
  166. package/src/actions/marks/bar/create.js +12 -3
  167. package/src/actions/marks/bar/edit.js +21 -4
  168. package/src/actions/marks/bar/materialize.js +34 -40
  169. package/src/actions/marks/index.js +10 -1
  170. package/src/actions/marks/line/actions.js +40 -17
  171. package/src/actions/marks/line/materialize.js +29 -10
  172. package/src/actions/marks/point/create.js +9 -3
  173. package/src/actions/marks/point/edit.js +19 -7
  174. package/src/actions/marks/point/jitter.js +6 -8
  175. package/src/actions/marks/point/materialize.js +48 -22
  176. package/src/actions/marks/point/packing.js +6 -8
  177. package/src/actions/marks/rect/actions.js +36 -15
  178. package/src/actions/marks/references.js +602 -33
  179. package/src/actions/marks/remove.js +67 -41
  180. package/src/actions/marks/rule/actions.js +51 -14
  181. package/src/actions/marks/shared.js +1 -1
  182. package/src/actions/marks/text/actions.js +79 -14
  183. package/src/actions/marks/text/index.js +4 -0
  184. package/src/actions/marks/text/layout.js +157 -17
  185. package/src/actions/marks/text/removal.js +173 -0
  186. package/src/actions/marks/text/selection.js +72 -0
  187. package/src/actions/marks/tick/actions.js +25 -8
  188. package/src/actions/primitives/createGraphics.js +5 -4
  189. package/src/actions/primitives/editGraphics.js +31 -4
  190. package/src/actions/primitives/graphicProperties.js +6 -0
  191. package/src/actions/primitives/semantic.js +11 -3
  192. package/src/actions/primitives/semanticAction.js +90 -14
  193. package/src/actions/primitives/semanticValidation/dataset.js +2 -4
  194. package/src/actions/primitives/semanticValidation/guide.js +1 -1
  195. package/src/actions/primitives/semanticValidation/index.js +9 -1
  196. package/src/actions/primitives/semanticValidation/layer.js +5 -0
  197. package/src/actions/primitives/semanticValidation/scale.js +28 -7
  198. package/src/actions/regression/components.js +36 -25
  199. package/src/actions/regression/create.js +13 -6
  200. package/src/actions/regression/edit.js +24 -11
  201. package/src/actions/resources/index.js +7 -0
  202. package/src/actions/resources/remove.js +173 -0
  203. package/src/actions/scales/channels.js +212 -0
  204. package/src/actions/scales/consumers/common.js +1 -1
  205. package/src/actions/scales/consumers/families.js +3 -2
  206. package/src/actions/scales/consumers/index.js +19 -2
  207. package/src/actions/scales/consumers/seriesLayout.js +28 -14
  208. package/src/actions/scales/create.js +55 -23
  209. package/src/actions/scales/definitions.js +94 -40
  210. package/src/actions/scales/edit.js +67 -12
  211. package/src/actions/scales/editPolicy.js +81 -11
  212. package/src/actions/scales/index.js +6 -0
  213. package/src/actions/scales/materialize.js +5 -4
  214. package/src/actions/scales/offset.js +48 -0
  215. package/src/actions/scales/parallel.js +71 -0
  216. package/src/actions/scales/preview.js +111 -10
  217. package/src/actions/scales/quantitativeColor.js +4 -5
  218. package/src/actions/selection/actions.js +179 -66
  219. package/src/actions/selection/index.js +3 -0
  220. package/src/actions/textMetrics/index.js +56 -0
  221. package/src/actions/theme/actions.js +70 -15
  222. package/src/actions/theme/composition.js +238 -0
  223. package/src/actions/theme/index.js +9 -2
  224. package/src/actions/theme/reconcile.js +313 -121
  225. package/src/actions/theme/state.js +160 -0
  226. package/src/actions/titles/actions.js +8 -8
  227. package/src/actions/titles/resolve.js +3 -3
  228. package/src/actions/violinPlots/create.js +6 -3
  229. package/src/actions/violinPlots/edit.js +2 -1
  230. package/src/core/action.js +117 -35
  231. package/src/core/compositionState.js +6 -1
  232. package/src/core/diagnostics.js +16 -0
  233. package/src/core/extensionRegistry.js +27 -0
  234. package/src/core/font.js +18 -0
  235. package/src/core/identifiers.js +5 -4
  236. package/src/core/immutable.js +10 -3
  237. package/src/core/optionRequirements.js +8 -0
  238. package/src/core/programState.js +21 -1
  239. package/src/core/resourceReferences.js +341 -0
  240. package/src/core/textMetricProfile.js +28 -0
  241. package/src/core/textMetrics.js +24 -3
  242. package/src/core/validation.js +22 -10
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/diagnostics.js +1 -0
  245. package/src/grammar/bars/aggregate.js +4 -1
  246. package/src/grammar/bars/geometry.js +44 -0
  247. package/src/grammar/bin.js +54 -9
  248. package/src/grammar/complete.js +279 -0
  249. package/src/grammar/computed.js +265 -64
  250. package/src/grammar/coordinates.js +37 -0
  251. package/src/grammar/density.js +239 -37
  252. package/src/grammar/displayLabels.js +57 -0
  253. package/src/grammar/facets/dependencies.js +133 -1
  254. package/src/grammar/facets/guides.js +3 -0
  255. package/src/grammar/facets/index.js +7 -47
  256. package/src/grammar/facets/scales.js +15 -18
  257. package/src/grammar/impute.js +341 -0
  258. package/src/grammar/markLabelSelection.js +57 -0
  259. package/src/grammar/markLabels.js +18 -1
  260. package/src/grammar/normalize.js +366 -0
  261. package/src/grammar/pathSeries.js +26 -8
  262. package/src/grammar/pointShapes.js +13 -4
  263. package/src/grammar/polar.js +90 -8
  264. package/src/grammar/regression/index.js +1 -0
  265. package/src/grammar/regression/parameters.js +23 -0
  266. package/src/grammar/roundedRect.js +180 -0
  267. package/src/grammar/rules.js +1 -0
  268. package/src/grammar/scales/colorConsumers.js +20 -1
  269. package/src/grammar/scales/continuous.js +2 -2
  270. package/src/grammar/scales/definition.js +27 -4
  271. package/src/grammar/scales/index.js +1 -0
  272. package/src/grammar/scales/ordinal.js +12 -3
  273. package/src/grammar/scales/policies.js +2 -2
  274. package/src/grammar/scales/size.js +456 -0
  275. package/src/grammar/scales/types.js +8 -0
  276. package/src/grammar/schemas/concreteGraphic.js +14 -2
  277. package/src/grammar/schemas/graphic.js +15 -0
  278. package/src/grammar/schemas/graphicBounds.js +190 -41
  279. package/src/grammar/schemas/semanticPath.js +7 -2
  280. package/src/grammar/statisticalReference.js +103 -0
  281. package/src/grammar/strokeStyle.js +77 -0
  282. package/src/grammar/summary.js +78 -16
  283. package/src/grammar/timeUnit.js +216 -5
  284. package/src/grammar/transformTopology.js +4 -0
  285. package/src/grammar/transforms.js +394 -28
  286. package/src/grammar/weightedStatistics.js +374 -0
  287. package/src/grammar/window.js +221 -16
  288. package/src/layout/aspect.js +80 -0
  289. package/src/layout/facets.js +59 -5
  290. package/src/layout/labels.js +351 -7
  291. package/src/layout/legend.js +4 -4
  292. package/src/layout/legendItems.js +6 -6
  293. package/src/layout/text.js +10 -9
  294. package/src/layout/title.js +9 -9
  295. package/src/materialization/bars/aggregate.js +25 -2
  296. package/src/materialization/bars/histogram.js +84 -24
  297. package/src/materialization/bars/ranged.js +19 -3
  298. package/src/materialization/bars/resolve.js +7 -4
  299. package/src/materialization/composition.js +3 -1
  300. package/src/materialization/coordinateBounds.js +204 -0
  301. package/src/materialization/dependencies.js +36 -0
  302. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  303. package/src/materialization/facetGuides/placement.js +9 -3
  304. package/src/materialization/facetGuides/preparation.js +13 -8
  305. package/src/materialization/facetHeaders.js +347 -0
  306. package/src/materialization/facets.js +15 -65
  307. package/src/materialization/guides/layout.js +3 -3
  308. package/src/materialization/guides/resources.js +26 -0
  309. package/src/materialization/legends.js +1 -1
  310. package/src/materialization/marks/index.js +11 -5
  311. package/src/materialization/marks/policies.js +2 -1
  312. package/src/materialization/rect.js +25 -12
  313. package/src/materialization/rectConfig.js +5 -1
  314. package/src/materialization/scaleGuideDependencies.js +10 -0
  315. package/src/materialization/scales/map.js +5 -3
  316. package/src/materialization/scales/policies/binnedPosition.js +2 -2
  317. package/src/materialization/scales/policies/offset.js +50 -8
  318. package/src/materialization/scales/policies/series.js +4 -1
  319. package/src/materialization/scales/resolve.js +22 -6
  320. package/src/materialization/selection/items/arc.js +28 -4
  321. package/src/materialization/selection/items/bar.js +30 -3
  322. package/src/materialization/selection/items/common.js +20 -5
  323. package/src/materialization/selection/items/rect.js +28 -4
  324. package/src/materialization/selection/path.js +1 -1
  325. package/src/materialization/selection/point.js +3 -1
  326. package/src/materialization/text.js +265 -14
  327. package/src/materialization/typography.js +31 -0
  328. package/src/mcp/cli.js +5 -3
  329. package/src/persistence/codec.js +65 -0
  330. package/src/persistence/validation.js +185 -0
  331. package/src/persistence.js +74 -0
  332. package/src/renderers/canvas/circle.js +2 -3
  333. package/src/renderers/canvas/index.js +5 -4
  334. package/src/renderers/canvas/line.js +2 -3
  335. package/src/renderers/canvas/native.js +4 -4
  336. package/src/renderers/canvas/path.js +2 -3
  337. package/src/renderers/canvas/rect.js +2 -3
  338. package/src/renderers/canvas/stroke.js +11 -0
  339. package/src/renderers/nativeBackend.js +10 -0
  340. package/src/renderers/options.js +13 -0
  341. package/src/renderers/pdf.js +25 -54
  342. package/src/renderers/png.js +22 -17
  343. package/src/renderers/svg.js +29 -14
  344. package/src/renderers/text.js +1 -11
  345. package/src/selectors/coordinates.js +3 -1
  346. package/src/selectors/datasets.js +38 -5
  347. package/src/selectors/layers.js +10 -4
  348. package/src/selectors/markOwners.js +46 -0
  349. package/src/selectors/scales.js +6 -4
  350. package/src/theme/defaults.js +103 -1
  351. package/src/version.js +2 -0
  352. package/types/accessibility.d.ts +37 -0
  353. package/types/basic.d.ts +10 -2
  354. package/types/diagnostics.d.ts +15 -0
  355. package/types/index.d.ts +90 -0
  356. package/types/pdf.d.ts +9 -0
  357. package/types/persistence.d.ts +7 -0
  358. package/types/png.d.ts +9 -0
  359. package/types/program.d.ts +944 -118
@@ -1,14 +1,20 @@
1
+ import { ownedChildren, ownership } from "../../selectors/markOwners.js";
1
2
  import { isSourceOwnedText } from "../../grammar/text.js";
2
- import { action } from "../../core/action.js";
3
+ import { closedAction } from "../../core/action.js";
3
4
  import { validateUserId } from "../../core/identifiers.js";
4
- import { validateKeys } from "../../core/validation.js";
5
+
5
6
  import { findDataset } from "../../selectors/datasets.js";
6
7
  import { findLayer } from "../../selectors/layers.js";
7
8
  import { applyDetachedScaleRematerialization } from "../../materialization/dependencies.js";
9
+ import { markLabelPlacementLeaderId } from "../../layout/labels.js";
8
10
  import {
9
11
  getPositionChannelDefinition,
10
12
  POSITION_CHANNELS
11
13
  } from "../../core/vocabulary.js";
14
+ import {
15
+ canonicalResourcePath,
16
+ collectResourceReferences
17
+ } from "../../core/resourceReferences.js";
12
18
 
13
19
  const OPTIONS = Object.freeze(["target"]);
14
20
  const REMOVE_AXIS = Object.freeze({
@@ -18,41 +24,6 @@ const REMOVE_AXIS = Object.freeze({
18
24
  radius: "removeRadialAxis"
19
25
  });
20
26
 
21
- function ownedChildren(program, id) {
22
- const config = program.markConfigs[id] ?? {};
23
- return [
24
- config.errorBar?.lowerCapId,
25
- config.errorBar?.upperCapId,
26
- config.errorBand?.lowerBoundaryId,
27
- config.errorBand?.upperBoundaryId,
28
- config.regression?.bandId,
29
- config.regression?.lineId,
30
- config.boxPlot?.whiskerId,
31
- config.boxPlot?.medianId,
32
- config.boxPlot?.outlierId,
33
- config.gradientPlot?.centerId,
34
- config.intervalPlot?.intervalId,
35
- config.endpointPlot?.roles?.stemId,
36
- config.endpointPlot?.roles?.startId,
37
- config.endpointPlot?.roles?.connectorId,
38
- ...(config.raincloudPlot?.ownedChildIds ?? [])
39
- ].concat(
40
- program.semanticSpec.layers
41
- .filter(layer => layer.source === id)
42
- .map(layer => layer.id)
43
- ).filter(child => child !== undefined && child !== id && findLayer(program, child) !== undefined);
44
- }
45
-
46
- function ownership(program) {
47
- const ownerByChild = new Map();
48
- for (const layer of program.semanticSpec.layers) {
49
- for (const child of ownedChildren(program, layer.id)) {
50
- ownerByChild.set(child, layer.id);
51
- }
52
- }
53
- return ownerByChild;
54
- }
55
-
56
27
  function resolveOwner(program, requested) {
57
28
  const ownerByChild = ownership(program);
58
29
  const stable = program.semanticSpec.layers.filter(
@@ -86,6 +57,37 @@ function collectClosure(program, root) {
86
57
  return result;
87
58
  }
88
59
 
60
+ function externalMarkReferences(program, ids) {
61
+ const closure = new Set(ids);
62
+ const references = ids.flatMap(id => collectResourceReferences(program, {
63
+ kind: "mark",
64
+ id
65
+ })).filter(reference => {
66
+ if (reference.strength === "context") return false;
67
+ if (["layer", "markConfig"].includes(reference.ownerKind)) {
68
+ return !closure.has(reference.ownerId);
69
+ }
70
+ if (["selection", "highlight", "legend"].includes(reference.ownerKind)) {
71
+ return false;
72
+ }
73
+ if (
74
+ reference.ownerKind === "axis" &&
75
+ reference.ownerId === "parallel.axes"
76
+ ) {
77
+ return false;
78
+ }
79
+ return true;
80
+ });
81
+ const unique = new Map(references.map(reference => [[
82
+ reference.ownerKind,
83
+ reference.ownerId,
84
+ canonicalResourcePath(reference.path)
85
+ ].join("\u0000"), reference]));
86
+ return [...unique.entries()]
87
+ .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
88
+ .map(([, reference]) => reference);
89
+ }
90
+
89
91
  function ownedDerivedData(program, ids) {
90
92
  const candidates = new Set();
91
93
  for (const id of ids) {
@@ -100,7 +102,8 @@ function ownedDerivedData(program, ids) {
100
102
  config.gradientPlot?.profileId,
101
103
  config.violinPlot?.materialized === true ? layer?.data : undefined,
102
104
  config.ecdfPlot?.data,
103
- config.endpointPlot?.data
105
+ config.endpointPlot?.data,
106
+ config.statisticalReference?.dataId
104
107
  ]) {
105
108
  if (candidate !== undefined) candidates.add(candidate);
106
109
  }
@@ -202,17 +205,31 @@ function cleanupPositionGuides(program, scales) {
202
205
  return next;
203
206
  }
204
207
 
205
- export const removeMark = action(
206
- { op: "removeMark", description: "Remove one stable mark owner and owned state." },
208
+ export const removeMark = /* @__PURE__ */ closedAction(
209
+ { op: "removeMark", description: "Remove one stable mark owner and owned state." }, OPTIONS,
207
210
  function (args = {}) {
208
- validateKeys(args, OPTIONS, "removeMark");
209
211
  const owner = resolveOwner(this, args.target);
210
212
  const ids = collectClosure(this, owner.id);
213
+ const external = externalMarkReferences(this, ids);
214
+ if (external.length > 0) {
215
+ throw new Error(
216
+ `removeMark cannot remove externally referenced marks: ${external.map(
217
+ reference => `${reference.ownerKind} "${reference.ownerId}" at ` +
218
+ canonicalResourcePath(reference.path)
219
+ ).join("; ")}.`
220
+ );
221
+ }
211
222
  const previousLayers = ids.map(id => findLayer(this, id));
212
223
  const derived = ownedDerivedData(this, ids);
213
224
  const positionScales = usedPositionScales(this, ids);
214
225
  let next = this;
215
226
 
227
+ if (ids.includes(next.guideConfigs.axis?.parallel?.axes?.target)) {
228
+ next = next.removeParallelAxes({
229
+ target: next.guideConfigs.axis.parallel.axes.target
230
+ });
231
+ }
232
+
216
233
  const legendTargets = [...new Set(Object.values(next.guideConfigs.legend ?? {})
217
234
  .filter(config => ids.includes(config?.target))
218
235
  .map(config => config.target))];
@@ -230,6 +247,15 @@ export const removeMark = action(
230
247
  ) {
231
248
  next = next.editGraphics({ target: labelLayout.leaderId, remove: true });
232
249
  }
250
+ const placement = next.markConfigs[id]?.labelAuthoring?.placement;
251
+ const placementLeaderId = markLabelPlacementLeaderId(id);
252
+ if (
253
+ placement?.leader !== undefined &&
254
+ placement.leader !== false &&
255
+ next.graphicSpec.objects[placementLeaderId] !== undefined
256
+ ) {
257
+ next = next.editGraphics({ target: placementLeaderId, remove: true });
258
+ }
233
259
  if (findLayer(next, id) !== undefined) {
234
260
  next = next.editSemantic({ property: `layer[${id}]`, remove: true });
235
261
  }
@@ -9,11 +9,13 @@ import {
9
9
  normalizeStrokeDashPattern
10
10
  } from "../../../grammar/scales/index.js";
11
11
  import { deriveRuleValues, resolveRuleMode } from "../../../grammar/rules.js";
12
- import { resolveGraphicBounds } from "../../../layout/canvas.js";
12
+ import { resolveCoordinateBounds } from "../../../materialization/coordinateBounds.js";
13
+ import { readRectItemGeometry } from "../../../grammar/roundedRect.js";
13
14
  import { findDataset } from "../../../selectors/datasets.js";
14
15
  import { findLayer, resolveEligibleLayer } from "../../../selectors/layers.js";
15
16
  import { applyRuleAppearance, planRuleAppearance } from "./appearance.js";
16
17
  import { DEFAULT_COLORS } from "../../../theme/defaults.js";
18
+ import { rematerializeExistingLegend } from "../../encodings/shared.js";
17
19
  import {
18
20
  assertMarkAvailable,
19
21
  applyLayeredMarkInheritance,
@@ -31,9 +33,19 @@ import { applyOffsetPositionValues } from
31
33
  "../../../materialization/rowEncoding.js";
32
34
  import { resolveCategorySlotOffset } from
33
35
  "../../../materialization/categorySlotOffset.js";
36
+ import { mapScaleConsumerValues } from
37
+ "../../../materialization/scales/map.js";
38
+ import {
39
+ requestedStrokeDetails,
40
+ STROKE_STYLE_PROPERTIES
41
+ } from "../../../grammar/strokeStyle.js";
34
42
 
35
- const CREATE_OPTIONS = Object.freeze(["id", "data", "stroke", "strokeWidth", "strokeDash", "opacity"]);
36
- const EDIT_OPTIONS = Object.freeze(["target", "stroke", "strokeWidth", "strokeDash", "opacity"]);
43
+ const APPEARANCE_OPTIONS = Object.freeze([
44
+ "stroke", "strokeWidth", "strokeDash", "opacity",
45
+ ...STROKE_STYLE_PROPERTIES
46
+ ]);
47
+ const CREATE_OPTIONS = Object.freeze(["id", "data", ...APPEARANCE_OPTIONS]);
48
+ const EDIT_OPTIONS = Object.freeze(["target", ...APPEARANCE_OPTIONS]);
37
49
  const REMATERIALIZE_OPTIONS = Object.freeze(["id"]);
38
50
  const SPAN_OPTIONS = Object.freeze(["id", "orientation", "size"]);
39
51
  const DEFAULT_RULE_CONFIG = Object.freeze({
@@ -80,13 +92,14 @@ function validateEndpointBindings(layer) {
80
92
  }
81
93
  }
82
94
 
83
- const createRuleMark = action(
95
+ const createRuleMark = /* @__PURE__ */ action(
84
96
  {
85
97
  op: "createRuleMark",
86
98
  description: "Create a semantic rule mark and empty line collection."
87
99
  },
88
100
  function (args = {}) {
89
101
  validateMarkOptions(args, CREATE_OPTIONS, "createRuleMark");
102
+ const strokeDetails = requestedStrokeDetails(args, "createRuleMark");
90
103
  const id = resolveMarkId(this, args.id, {
91
104
  defaultId: "rule",
92
105
  label: "Rule mark id",
@@ -115,6 +128,7 @@ const createRuleMark = action(
115
128
  })
116
129
  ._withMarkConfig(id, {
117
130
  ...DEFAULT_RULE_CONFIG,
131
+ ...strokeDetails,
118
132
  ...(inherited === undefined
119
133
  ? {}
120
134
  : {
@@ -128,10 +142,11 @@ const createRuleMark = action(
128
142
  }
129
143
  );
130
144
 
131
- const editRuleMark = action(
145
+ const editRuleMark = /* @__PURE__ */ action(
132
146
  { op: "editRuleMark", description: "Edit constant rule appearance through its encoding owners." },
133
147
  function (args = {}) {
134
148
  validateMarkOptions(args, EDIT_OPTIONS, "editRuleMark");
149
+ const strokeDetails = requestedStrokeDetails(args, "editRuleMark");
135
150
  if (args.target !== undefined) validateUserId(args.target, "Rule mark id");
136
151
  const layer = resolveEligibleLayer(this, {
137
152
  target: args.target,
@@ -139,12 +154,21 @@ const editRuleMark = action(
139
154
  label: "rule mark"
140
155
  });
141
156
  const appearance = planRuleAppearance(args, layer);
142
- if (appearance.length === 0) throw new Error("editRuleMark requires an appearance change.");
143
- return applyRuleAppearance(this, layer.id, appearance);
157
+ if (appearance.length === 0 && Object.keys(strokeDetails).length === 0) {
158
+ throw new Error("editRuleMark requires an appearance change.");
159
+ }
160
+ const configured = this._withMarkConfig(layer.id, {
161
+ ...this.markConfigs[layer.id],
162
+ ...strokeDetails
163
+ });
164
+ const materialized = appearance.length === 0
165
+ ? configured.rematerializeRuleMark({ id: layer.id })
166
+ : applyRuleAppearance(configured, layer.id, appearance);
167
+ return rematerializeExistingLegend(materialized);
144
168
  }
145
169
  );
146
170
 
147
- const rematerializeRuleMark = action(
171
+ const rematerializeRuleMark = /* @__PURE__ */ action(
148
172
  {
149
173
  op: "rematerializeRuleMark",
150
174
  description: "Recompute concrete rule endpoints and appearance."
@@ -180,7 +204,7 @@ const rematerializeRuleMark = action(
180
204
  const scaleIds = [...new Set(
181
205
  [
182
206
  "x", "y", "x2", "y2", "xOffset", "yOffset",
183
- "strokeDash", "strokeWidth", "opacity"
207
+ "stroke", "strokeDash", "strokeWidth", "opacity"
184
208
  ]
185
209
  .map(channel => layer.encoding?.[channel]?.scale)
186
210
  .filter(scale => scale !== undefined)
@@ -224,7 +248,7 @@ const rematerializeRuleMark = action(
224
248
  channel
225
249
  });
226
250
  }
227
- const bounds = resolveGraphicBounds(resolved);
251
+ const bounds = resolveCoordinateBounds(resolved, layer.coordinate);
228
252
  const x1 = [];
229
253
  const y1 = [];
230
254
  const x2 = [];
@@ -252,7 +276,11 @@ const rematerializeRuleMark = action(
252
276
  startY = endY = mapped.y[index];
253
277
  } else if (mode === "box-span") {
254
278
  const owner = resolved.graphicSpec.objects[boxSpan];
255
- const box = owner?.items?.[index]?.properties;
279
+ const item = owner?.items?.[index];
280
+ const box = readRectItemGeometry({
281
+ type: item?.type ?? owner?.type,
282
+ properties: item?.properties
283
+ });
256
284
  if (box === undefined) throw new Error(`Rule mark "${id}" requires box span owner "${boxSpan}".`);
257
285
  if (layer.encoding?.x?.fieldType === "quantitative") {
258
286
  startX = endX = mapped.x[index];
@@ -299,6 +327,7 @@ const rematerializeRuleMark = action(
299
327
  ...resolved.markConfigs[id]
300
328
  };
301
329
  const dashEncoding = layer.encoding?.strokeDash;
330
+ const strokeEncoding = layer.encoding?.stroke;
302
331
  const opacityEncoding = layer.encoding?.opacity;
303
332
  const strokeWidthEncoding = layer.encoding?.strokeWidth;
304
333
  const strokeDash = dashEncoding?.scale === undefined
@@ -325,6 +354,13 @@ const rematerializeRuleMark = action(
325
354
  derived.values.strokeWidth,
326
355
  resolved.resolvedScales[strokeWidthEncoding.scale]
327
356
  );
357
+ const stroke = strokeEncoding?.scale === undefined
358
+ ? resolvedConfig.stroke
359
+ : mapScaleConsumerValues(
360
+ derived.values.stroke,
361
+ resolved.resolvedScales[strokeEncoding.scale],
362
+ "stroke"
363
+ );
328
364
 
329
365
  return editMarkGraphic(resolved, id, {
330
366
  length: derived.length,
@@ -332,15 +368,16 @@ const rematerializeRuleMark = action(
332
368
  y1,
333
369
  x2,
334
370
  y2,
335
- stroke: resolvedConfig.stroke,
371
+ stroke,
336
372
  strokeWidth,
337
373
  strokeDash,
338
- opacity
374
+ opacity,
375
+ ...requestedStrokeDetails(resolvedConfig, "Rule mark")
339
376
  });
340
377
  }
341
378
  );
342
379
 
343
- export const materializeRuleSpan = action(
380
+ export const materializeRuleSpan = /* @__PURE__ */ action(
344
381
  {
345
382
  op: "materializeRuleSpan",
346
383
  description: "Materialize a fixed-pixel span around a rule anchor."
@@ -29,7 +29,7 @@ export function resolveMarkData(program, requested) {
29
29
  }
30
30
 
31
31
  const dataset = requireMaterializedDataset(program, data);
32
- return { data, dataset };
32
+ return { data: dataset.id, dataset };
33
33
  }
34
34
 
35
35
  export function resolveMarkId(program, requested, {
@@ -13,7 +13,17 @@ import {
13
13
  canMaterializeText,
14
14
  isTextSource
15
15
  } from "../../../materialization/marks/index.js";
16
- import { resolveTextGraphicItems } from "../../../materialization/text.js";
16
+ import {
17
+ resolveTextGraphicItems,
18
+ validateSourceMarkLabelPlacement
19
+ } from "../../../materialization/text.js";
20
+ import { markLabelPlacementLeaderId } from "../../../layout/labels.js";
21
+ import { resolveMarkSelection } from
22
+ "../../../materialization/selection/state.js";
23
+ import { rematerializeLabelsBeforeHighlights } from
24
+ "../../selection/actions.js";
25
+ import { normalizeLabelSelectionCreate } from
26
+ "../../../grammar/markLabelSelection.js";
17
27
  import { findLayer, resolveEligibleLayer } from "../../../selectors/layers.js";
18
28
  import {
19
29
  applyLayeredMarkInheritance,
@@ -49,6 +59,13 @@ function eligibleSource(program, layer, requestedData) {
49
59
  !sourceMatchesData(program, layer, requestedData)
50
60
  ) return false;
51
61
  if (layer.mark.type === "arc") return canMaterializeArc(program, layer);
62
+ if (
63
+ layer.mark.type === "point" &&
64
+ findCoordinate(program, layer.coordinate)?.type === "polar"
65
+ ) {
66
+ return layer.encoding?.theta?.scale !== undefined &&
67
+ layer.encoding?.radius?.scale !== undefined;
68
+ }
52
69
  if (layer.mark.type === "rect" && (canMaterializeRect(program, layer) ||
53
70
  program.markConfigs[layer.id]?.gradientPlot?.materialized === true)) return true;
54
71
  const encodings = resolveCompatibleEncodings(program, layer, "text");
@@ -91,7 +108,9 @@ function textInheritance(program, source) {
91
108
  source: source.id,
92
109
  data: source.data,
93
110
  coordinate: source.coordinate,
94
- encoding: source.mark.type === "arc"
111
+ encoding: source.mark.type === "arc" ||
112
+ (source.mark.type === "point" &&
113
+ findCoordinate(program, source.coordinate)?.type === "polar")
95
114
  ? {}
96
115
  : resolveCompatibleEncodings(program, source, "text")
97
116
  };
@@ -106,7 +125,7 @@ function requireTextLayer(program, requested, operation) {
106
125
  });
107
126
  }
108
127
 
109
- const createTextMark = action(
128
+ const createTextMark = /* @__PURE__ */ action(
110
129
  {
111
130
  op: "createTextMark",
112
131
  description: "Create a semantic text annotation layer."
@@ -161,6 +180,7 @@ const createTextMark = action(
161
180
 
162
181
  const LABEL_OPTIONS = Object.freeze([
163
182
  "id", "source", "field", "value", "content", "normalizeBy", "format", "layout",
183
+ "select", "selection", "placement",
164
184
  ...STYLE_OPTIONS
165
185
  ]);
166
186
 
@@ -234,7 +254,7 @@ function resolveAnnotationBinding(program, args) {
234
254
  };
235
255
  }
236
256
 
237
- const createAnnotation = action(
257
+ const createAnnotation = /* @__PURE__ */ action(
238
258
  {
239
259
  op: "createAnnotation",
240
260
  description: "Create text at a final mark, data, or plot anchor."
@@ -307,7 +327,7 @@ const createAnnotation = action(
307
327
  }
308
328
  );
309
329
 
310
- const createMarkLabels = action(
330
+ const createMarkLabels = /* @__PURE__ */ action(
311
331
  {
312
332
  op: "createMarkLabels",
313
333
  description: "Create final-item text labels attached to an existing mark."
@@ -320,6 +340,32 @@ const createMarkLabels = action(
320
340
  }
321
341
  const id = validateUserId(args.id === undefined ? `${inherited.source}-labels` : args.id, "Text mark id");
322
342
  assertMarkAvailable(this, id);
343
+ const selection = normalizeLabelSelectionCreate(args);
344
+ const placement = args.placement === undefined
345
+ ? undefined
346
+ : validateSourceMarkLabelPlacement(this, inherited.source, args.placement);
347
+ if (
348
+ placement?.leader !== undefined && placement.leader !== false &&
349
+ this.graphicSpec.objects[markLabelPlacementLeaderId(id)] !== undefined
350
+ ) {
351
+ throw new Error(
352
+ `Mark label placement leader graphic "${markLabelPlacementLeaderId(id)}" already exists.`
353
+ );
354
+ }
355
+ if (selection.kind === "inline") {
356
+ resolveMarkSelection(this, inherited.source, selection.selector);
357
+ } else if (selection.kind === "named") {
358
+ const definition = this.materializationConfigs.selections?.[selection.id];
359
+ if (definition === undefined) {
360
+ throw new Error(`Unknown label selection "${selection.id}".`);
361
+ }
362
+ if (definition.target !== inherited.source) {
363
+ throw new Error(
364
+ `Label selection "${selection.id}" must target source mark "${inherited.source}".`
365
+ );
366
+ }
367
+ resolveMarkSelection(this, inherited.source, definition.selector);
368
+ }
323
369
  const style = Object.fromEntries(STYLE_OPTIONS
324
370
  .filter(option => Object.hasOwn(args, option))
325
371
  .map(option => [option, args[option]]));
@@ -337,9 +383,23 @@ const createMarkLabels = action(
337
383
  throw new Error("createMarkLabels layout target is owned by the created label layer.");
338
384
  }
339
385
  const apply = program => {
340
- const next = program.createTextMark({
386
+ let next = program.createTextMark({
341
387
  id, source: inherited.source, align: "center", baseline: "middle", ...style
342
- }).encodeText({ target: id, ...encoding });
388
+ });
389
+ next = next._withMarkConfig(id, {
390
+ ...next.markConfigs[id],
391
+ labelAuthoring: {
392
+ selection,
393
+ ...(placement === undefined ? {} : { placement })
394
+ }
395
+ });
396
+ next = next.encodeText({ target: id, ...encoding });
397
+ next = rematerializeLabelsBeforeHighlights(
398
+ next,
399
+ inherited.source,
400
+ [id],
401
+ { skipWithoutHighlights: true }
402
+ );
343
403
  return layout === undefined ? next : next.layoutLabels({ ...layout, target: id });
344
404
  };
345
405
  if (layout !== undefined) {
@@ -352,7 +412,7 @@ const createMarkLabels = action(
352
412
  }
353
413
  );
354
414
 
355
- const rematerializeTextMark = action(
415
+ const rematerializeTextMark = /* @__PURE__ */ action(
356
416
  {
357
417
  op: "rematerializeTextMark",
358
418
  description: "Recompute concrete text content, anchors, and typography."
@@ -376,9 +436,11 @@ const rematerializeTextMark = action(
376
436
  : this.editGraphics({ target: id, property: "length", value: 0 });
377
437
  return replayLayout && next.materializationConfigs.labelLayouts?.[id] !== undefined
378
438
  ? next.materializeLabelLayout({ id, rematerializeBase: false })
379
- : next;
439
+ : layer.source === undefined
440
+ ? next
441
+ : next.materializeMarkLabelPlacement({ id });
380
442
  }
381
- const next = this.editGraphics({
443
+ let next = this.editGraphics({
382
444
  target: id,
383
445
  property: "items",
384
446
  value: resolveTextGraphicItems(
@@ -387,13 +449,16 @@ const rematerializeTextMark = action(
387
449
  this.markConfigs[id] ?? DEFAULT_TEXT_MARK
388
450
  )
389
451
  });
390
- return replayLayout && next.materializationConfigs.labelLayouts?.[id] !== undefined
391
- ? next.materializeLabelLayout({ id, rematerializeBase: false })
392
- : next;
452
+ if (replayLayout && next.materializationConfigs.labelLayouts?.[id] !== undefined) {
453
+ return next.materializeLabelLayout({ id, rematerializeBase: false });
454
+ }
455
+ return layer.source === undefined
456
+ ? next
457
+ : next.materializeMarkLabelPlacement({ id });
393
458
  }
394
459
  );
395
460
 
396
- const editTextMark = action(
461
+ const editTextMark = /* @__PURE__ */ action(
397
462
  {
398
463
  op: "editTextMark",
399
464
  description: "Edit text typography, alignment, rotation, and offsets."
@@ -1,7 +1,11 @@
1
1
  import { registerTextMarkActions as registerTextCoreActions } from "./actions.js";
2
2
  import { registerTextLabelLayoutActions } from "./layout.js";
3
+ import { registerTextLabelRemovalActions } from "./removal.js";
4
+ import { registerTextLabelSelectionActions } from "./selection.js";
3
5
 
4
6
  export function registerTextMarkActions(ProgramClass) {
5
7
  registerTextCoreActions(ProgramClass);
6
8
  registerTextLabelLayoutActions(ProgramClass);
9
+ registerTextLabelRemovalActions(ProgramClass);
10
+ registerTextLabelSelectionActions(ProgramClass);
7
11
  }