ggaction 0.0.17 → 0.0.19

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 (315) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +6 -2
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/intent-taxonomy.json +1 -1
  6. package/knowledge/mcp-resources.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +3 -3
  9. package/src/BasicChartProgram.js +1 -1
  10. package/src/accessibility.js +3 -2
  11. package/src/actions/boxPlots/create.js +14 -4
  12. package/src/actions/boxPlots/edit.js +39 -33
  13. package/src/actions/boxPlots/materialize.js +7 -3
  14. package/src/actions/boxPlots/options.js +37 -11
  15. package/src/actions/canvas/actions.js +70 -95
  16. package/src/actions/canvas/fitting.js +20 -1
  17. package/src/actions/canvas/index.js +3 -0
  18. package/src/actions/canvas/plotBorder.js +28 -0
  19. package/src/actions/charts/area.js +2 -2
  20. package/src/actions/charts/bar.js +34 -7
  21. package/src/actions/charts/ecdf.js +4 -18
  22. package/src/actions/charts/endpoints.js +4 -18
  23. package/src/actions/charts/heatmap.js +8 -7
  24. package/src/actions/charts/index.js +2 -0
  25. package/src/actions/charts/interval-regression.js +2 -2
  26. package/src/actions/charts/line.js +1 -1
  27. package/src/actions/charts/pie.js +3 -47
  28. package/src/actions/charts/radial.js +34 -12
  29. package/src/actions/charts/scatter.js +16 -2
  30. package/src/actions/charts/shared.js +2 -2
  31. package/src/actions/charts/text.js +35 -0
  32. package/src/actions/composition/actions.js +6 -2
  33. package/src/actions/data/box.js +2 -1
  34. package/src/actions/data/derived.js +11 -1
  35. package/src/actions/data/edit.js +3 -1
  36. package/src/actions/data/index.js +4 -0
  37. package/src/actions/data/regression.js +25 -16
  38. package/src/actions/data/regressionStatistics.js +73 -0
  39. package/src/actions/data/shared.js +8 -4
  40. package/src/actions/data/stack.js +3 -1
  41. package/src/actions/distributions/revision.js +24 -18
  42. package/src/actions/encodings/appearance.js +37 -29
  43. package/src/actions/encodings/barWidth.js +36 -11
  44. package/src/actions/encodings/channels.js +0 -1
  45. package/src/actions/encodings/color/continuous.js +1 -1
  46. package/src/actions/encodings/color/index.js +1 -1
  47. package/src/actions/encodings/color/policy.js +10 -3
  48. package/src/actions/encodings/density/resolve.js +1 -6
  49. package/src/actions/encodings/position/policies/arc.js +12 -10
  50. package/src/actions/encodings/position/policies/area.js +3 -3
  51. package/src/actions/encodings/position/policies/bar.js +15 -6
  52. package/src/actions/encodings/position/policies/line.js +17 -6
  53. package/src/actions/encodings/position/resolve.js +3 -2
  54. package/src/actions/encodings/ranged.js +17 -0
  55. package/src/actions/encodings/remove.js +14 -8
  56. package/src/actions/encodings/seriesLayout.js +1 -0
  57. package/src/actions/encodings/shared.js +26 -24
  58. package/src/actions/encodings/text.js +6 -3
  59. package/src/actions/errorBands/create.js +2 -2
  60. package/src/actions/errorBands/edit.js +2 -1
  61. package/src/actions/facets/actions.js +176 -247
  62. package/src/actions/facets/guides.js +2 -1
  63. package/src/actions/gradientPlots/resolve.js +2 -17
  64. package/src/actions/guides/applicability.js +1 -1
  65. package/src/actions/guides/axes/axes.js +30 -11
  66. package/src/actions/guides/axes/axis.js +72 -10
  67. package/src/actions/guides/axes/components.js +13 -13
  68. package/src/actions/guides/axes/edit.js +36 -24
  69. package/src/actions/guides/axes/labels.js +155 -71
  70. package/src/actions/guides/axes/lines.js +27 -24
  71. package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
  72. package/src/actions/guides/axes/parallel/policy.js +8 -7
  73. package/src/actions/guides/axes/remove.js +13 -8
  74. package/src/actions/guides/axes/tickGroups.js +11 -7
  75. package/src/actions/guides/axes/ticks.js +36 -24
  76. package/src/actions/guides/axes/titles.js +107 -69
  77. package/src/actions/guides/basic.js +2 -0
  78. package/src/actions/guides/facadeAxes.js +33 -24
  79. package/src/actions/guides/facadeLegend.js +12 -4
  80. package/src/actions/guides/grids/grid.js +8 -2
  81. package/src/actions/guides/grids/resolve.js +102 -58
  82. package/src/actions/guides/legends/blocks.js +46 -20
  83. package/src/actions/guides/legends/categorical/actions.js +95 -19
  84. package/src/actions/guides/legends/categorical/components.js +15 -18
  85. package/src/actions/guides/legends/categorical/index.js +8 -0
  86. package/src/actions/guides/legends/categorical/layout.js +45 -20
  87. package/src/actions/guides/legends/categorical/options.js +39 -22
  88. package/src/actions/guides/legends/categorical/recipes.js +7 -3
  89. package/src/actions/guides/legends/categorical/resolve.js +11 -4
  90. package/src/actions/guides/legends/categorical/symbols.js +38 -18
  91. package/src/actions/guides/legends/categorical/validation.js +1 -6
  92. package/src/actions/guides/legends/colorSize.js +110 -0
  93. package/src/actions/guides/legends/combine.js +37 -0
  94. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
  95. package/src/actions/guides/legends/continuous/common.js +1 -447
  96. package/src/actions/guides/legends/continuous/gradient.js +73 -46
  97. package/src/actions/guides/legends/continuous/interval.js +4 -4
  98. package/src/actions/guides/legends/continuous/opacity.js +33 -21
  99. package/src/actions/guides/legends/creation.js +2 -0
  100. package/src/actions/guides/legends/edit.js +1 -604
  101. package/src/actions/guides/legends/focused.js +8 -7
  102. package/src/actions/guides/legends/group.js +1 -0
  103. package/src/actions/guides/legends/lane.js +1 -516
  104. package/src/actions/guides/legends/lifecycle.js +2 -1
  105. package/src/actions/guides/legends/remove.js +9 -3
  106. package/src/actions/guides/legends/sampling.js +1 -1
  107. package/src/actions/guides/legends/size.js +65 -47
  108. package/src/actions/guides/legends/strokeWidth.js +13 -29
  109. package/src/actions/guides/legends/target.js +2 -79
  110. package/src/actions/guides/legends/transition.js +44 -11
  111. package/src/actions/guides/polar/axes/labels.js +4 -2
  112. package/src/actions/guides/polar/axes/titles.js +7 -5
  113. package/src/actions/guides/polar/resolve.js +6 -14
  114. package/src/actions/guides/tickValues.js +8 -5
  115. package/src/actions/index.js +16 -16
  116. package/src/actions/marks/arc/actions.js +3 -10
  117. package/src/actions/marks/area/actions.js +8 -5
  118. package/src/actions/marks/area/materialize.js +8 -9
  119. package/src/actions/marks/bar/create.js +10 -4
  120. package/src/actions/marks/bar/edit.js +29 -14
  121. package/src/actions/marks/bar/materialize.js +15 -7
  122. package/src/actions/marks/line/actions.js +11 -4
  123. package/src/actions/marks/line/materialize.js +9 -5
  124. package/src/actions/marks/point/edit.js +1 -1
  125. package/src/actions/marks/point/materialize.js +5 -1
  126. package/src/actions/marks/rect/actions.js +3 -3
  127. package/src/actions/marks/remove.js +12 -10
  128. package/src/actions/marks/rule/actions.js +7 -4
  129. package/src/actions/marks/text/actions.js +17 -5
  130. package/src/actions/marks/text/layout.js +3 -0
  131. package/src/actions/primitives/createGraphics.js +3 -2
  132. package/src/actions/primitives/editGraphics.js +15 -11
  133. package/src/actions/primitives/graphicProperties.js +1 -6
  134. package/src/actions/primitives/semanticAction.js +27 -1
  135. package/src/actions/primitives/semanticValidation/guide.js +11 -1
  136. package/src/actions/primitives/semanticValidation/layer.js +8 -0
  137. package/src/actions/regression/create.js +1 -1
  138. package/src/actions/regression/edit.js +18 -6
  139. package/src/actions/regression/resolve.js +2 -6
  140. package/src/actions/scales/channels.js +9 -4
  141. package/src/actions/scales/consumers/families.js +16 -5
  142. package/src/actions/scales/create.js +10 -5
  143. package/src/actions/scales/definitions.js +1 -425
  144. package/src/actions/scales/edit.js +3 -3
  145. package/src/actions/scales/editPolicy.js +12 -3
  146. package/src/actions/scales/materialize.js +1 -1
  147. package/src/actions/scales/patch.js +1 -1
  148. package/src/actions/scales/preview.js +21 -1
  149. package/src/actions/scales/quantitativeColor.js +8 -3
  150. package/src/actions/selection/actions.js +1 -13
  151. package/src/actions/theme/reconcile.js +126 -45
  152. package/src/actions/theme/state.js +12 -0
  153. package/src/actions/titles/actions.js +5 -18
  154. package/src/actions/titles/resolve.js +41 -19
  155. package/src/actions/violinPlots/edit.js +3 -20
  156. package/src/core/compositionState.js +6 -3
  157. package/src/core/font.js +9 -2
  158. package/src/core/graphicProperties.js +1 -0
  159. package/src/core/optionRequirements.js +1 -0
  160. package/src/core/programState.js +4 -0
  161. package/src/core/resourceReferences.js +5 -2
  162. package/src/core/textMetricProfile.js +3 -2
  163. package/src/core/textMetrics.js +41 -4
  164. package/src/core/validation.js +35 -0
  165. package/src/core/vocabulary.js +7 -0
  166. package/src/grammar/aggregate.js +4 -18
  167. package/src/grammar/arcs.js +1 -386
  168. package/src/grammar/areaPaint.js +10 -0
  169. package/src/grammar/areaSeries.js +18 -10
  170. package/src/grammar/axisTextPlacement.js +1 -0
  171. package/src/grammar/bars/geometry.js +30 -3
  172. package/src/grammar/bars/policy.js +12 -1
  173. package/src/grammar/bin.js +2 -12
  174. package/src/grammar/bin2d.js +1 -6
  175. package/src/grammar/boxPlot.js +56 -9
  176. package/src/grammar/categoryOrder.js +1 -6
  177. package/src/grammar/colorSizeLegend.js +39 -0
  178. package/src/grammar/complete.js +15 -19
  179. package/src/grammar/computed.js +115 -26
  180. package/src/grammar/curveCommands.js +29 -16
  181. package/src/grammar/datasetSchema.js +13 -9
  182. package/src/grammar/displayLabels.js +36 -0
  183. package/src/grammar/ecdf.js +2 -9
  184. package/src/grammar/facets/dependencies.js +6 -2
  185. package/src/grammar/facets/guides.js +58 -30
  186. package/src/grammar/facets/index.js +2 -5
  187. package/src/grammar/facets/legendIntent.js +65 -0
  188. package/src/grammar/fold.js +2 -12
  189. package/src/grammar/gradientProfile.js +1 -6
  190. package/src/grammar/guideDash.js +1 -0
  191. package/src/grammar/impute.js +21 -48
  192. package/src/grammar/interval.js +2 -9
  193. package/src/grammar/legendBlocks.js +1 -0
  194. package/src/grammar/legendBorder.js +9 -0
  195. package/src/grammar/legendClip.js +19 -0
  196. package/src/grammar/legendCombinations.js +45 -0
  197. package/src/grammar/legendGradient.js +27 -0
  198. package/src/grammar/legendGroups.js +1 -0
  199. package/src/grammar/legendOpacity.js +6 -0
  200. package/src/grammar/legendTitleStyle.js +22 -0
  201. package/src/grammar/lineSeries.js +32 -17
  202. package/src/grammar/markLabels.js +5 -2
  203. package/src/grammar/normalize.js +4 -54
  204. package/src/grammar/numeric.js +1 -290
  205. package/src/grammar/parallelCoordinates.js +1 -6
  206. package/src/grammar/parseTime.js +41 -0
  207. package/src/grammar/pathCommands.js +11 -4
  208. package/src/grammar/pointShapes.js +16 -2
  209. package/src/grammar/polar.js +1 -184
  210. package/src/grammar/positionCompatibility.js +3 -3
  211. package/src/grammar/regression/derive.js +16 -2
  212. package/src/grammar/regression/models.js +142 -54
  213. package/src/grammar/regression/parameters.js +23 -4
  214. package/src/grammar/regression/statistics.js +64 -0
  215. package/src/grammar/roundedRect.js +42 -54
  216. package/src/grammar/scales/appearance.js +1 -206
  217. package/src/grammar/scales/color.js +1 -382
  218. package/src/grammar/scales/colorConsumers.js +5 -2
  219. package/src/grammar/scales/definition.js +1 -197
  220. package/src/grammar/scales/discretized.js +1 -195
  221. package/src/grammar/scales/size.js +1 -456
  222. package/src/grammar/scales/temporal.js +2 -13
  223. package/src/grammar/scales/transformed.js +1 -383
  224. package/src/grammar/scales/types.js +1 -1
  225. package/src/grammar/scales/validation.js +1 -66
  226. package/src/grammar/schemas/concreteGraphic.js +14 -0
  227. package/src/grammar/schemas/graphic.js +6 -0
  228. package/src/grammar/schemas/graphicBounds.js +31 -7
  229. package/src/grammar/schemas/semanticPath.js +19 -3
  230. package/src/grammar/stack.js +2 -10
  231. package/src/grammar/statisticalReference.js +1 -6
  232. package/src/grammar/summary.js +4 -27
  233. package/src/grammar/text.js +19 -0
  234. package/src/grammar/timeUnit.js +22 -11
  235. package/src/grammar/transformKeys.js +47 -0
  236. package/src/grammar/transformTopology.js +1 -0
  237. package/src/grammar/transforms.js +15 -2
  238. package/src/grammar/valueFormat.js +17 -5
  239. package/src/grammar/weightedStatistics.js +1 -6
  240. package/src/grammar/window.js +4 -58
  241. package/src/inspectionDescriptors.js +1 -1
  242. package/src/layout/canvas.js +1 -110
  243. package/src/layout/facets.js +1 -244
  244. package/src/layout/guideCollisions.js +9 -1
  245. package/src/layout/labels.js +1 -0
  246. package/src/layout/legend.js +13 -0
  247. package/src/layout/legendGroup.js +1 -0
  248. package/src/layout/legendItems.js +59 -15
  249. package/src/layout/legendLane.js +1 -441
  250. package/src/layout/legendText.js +1 -0
  251. package/src/layout/title.js +35 -13
  252. package/src/materialization/bars/aggregate.js +11 -5
  253. package/src/materialization/bars/histogram.js +4 -1
  254. package/src/materialization/bars/ranged.js +19 -6
  255. package/src/materialization/bars/resolve.js +22 -1
  256. package/src/materialization/compositionSnapshot.js +2 -7
  257. package/src/materialization/coordinateBounds.js +21 -0
  258. package/src/materialization/dependencies.js +4 -44
  259. package/src/materialization/facetGuides/edit.js +30 -0
  260. package/src/materialization/facetGuides/layout.js +1 -0
  261. package/src/materialization/facetGuides/owner.js +125 -0
  262. package/src/materialization/facetGuides/ownerLayout.js +71 -0
  263. package/src/materialization/facetGuides/placement.js +71 -14
  264. package/src/materialization/facetGuides/preparation.js +22 -7
  265. package/src/materialization/facetHeaders.js +1 -347
  266. package/src/materialization/facets.js +1 -185
  267. package/src/materialization/graphicHierarchy.js +5 -1
  268. package/src/materialization/graphicTranslation.js +73 -0
  269. package/src/materialization/guides/layout.js +1 -57
  270. package/src/materialization/guides/resources.js +1 -123
  271. package/src/materialization/layout.js +53 -0
  272. package/src/materialization/legendBorder.js +28 -0
  273. package/src/materialization/legendClip.js +76 -0
  274. package/src/materialization/legendGroups.js +1 -0
  275. package/src/materialization/legendText.js +1 -0
  276. package/src/materialization/legends.js +2 -2
  277. package/src/materialization/marks/capabilities.js +5 -6
  278. package/src/materialization/rect.js +1 -1
  279. package/src/materialization/scaleGuideDependencies.js +14 -15
  280. package/src/materialization/scales/map.js +6 -0
  281. package/src/materialization/scales/policies/arc.js +8 -7
  282. package/src/materialization/scales/policies/bar.js +19 -7
  283. package/src/materialization/scales/policies/series.js +2 -1
  284. package/src/materialization/scales/resolve.js +7 -3
  285. package/src/materialization/selection/items/arc.js +1 -1
  286. package/src/materialization/selection/items/bar.js +7 -6
  287. package/src/materialization/selection/items/path.js +7 -6
  288. package/src/materialization/selection/items/rect.js +2 -2
  289. package/src/materialization/text.js +13 -4
  290. package/src/materialization/textLines.js +15 -0
  291. package/src/materialization/typography.js +10 -2
  292. package/src/persistence/codec.js +37 -6
  293. package/src/persistence/validation.js +220 -0
  294. package/src/persistence.js +19 -6
  295. package/src/renderers/canvas/circle.js +6 -3
  296. package/src/renderers/canvas/fill.js +7 -1
  297. package/src/renderers/canvas/index.js +22 -11
  298. package/src/renderers/canvas/isolated.js +166 -0
  299. package/src/renderers/canvas/limits.js +3 -0
  300. package/src/renderers/canvas/line.js +1 -0
  301. package/src/renderers/canvas/path.js +1 -1
  302. package/src/renderers/canvas/rect.js +11 -8
  303. package/src/renderers/canvas/stroke.js +2 -1
  304. package/src/renderers/canvas/text.js +5 -3
  305. package/src/renderers/pdf.js +2 -2
  306. package/src/renderers/png.js +2 -2
  307. package/src/renderers/svg.js +14 -4
  308. package/src/selectors/axes.js +1 -0
  309. package/src/selectors/graphicCapacity.js +38 -0
  310. package/src/selectors/layers.js +19 -0
  311. package/src/theme/defaults.js +8 -2
  312. package/src/version.js +1 -1
  313. package/types/basic.d.ts +3 -0
  314. package/types/index.d.ts +12 -0
  315. package/types/program.d.ts +536 -129
@@ -1,3 +1,4 @@
1
+ import { cartesianAxisOwners, axisTargetArgs } from "../../selectors/axes.js";
1
2
  import { ownedChildren, ownership } from "../../selectors/markOwners.js";
2
3
  import { isSourceOwnedText } from "../../grammar/text.js";
3
4
  import { closedAction } from "../../core/action.js";
@@ -163,6 +164,7 @@ export function removeOwnedMark(program, id, partial = false) {
163
164
  next = next.editGraphics({ target: id, remove: true });
164
165
  }
165
166
  return next
167
+ ._withoutMaterializationConfig(["guides", "legendGroups", id])
166
168
  ._withoutMaterializationConfig(["marks", id])
167
169
  ._withContext({
168
170
  ...(program.context.currentMark === id ? { currentMark: undefined } : {}),
@@ -182,16 +184,15 @@ function cleanupPositionGuides(program, scales) {
182
184
  let next = program;
183
185
  for (const channel of POSITION_CHANNELS) {
184
186
  const definition = getPositionChannelDefinition(channel);
185
- const axis = next.semanticSpec.guides.axis?.[channel];
186
- if (
187
- axis !== undefined &&
188
- scales[channel].has(axis.scale) &&
189
- !hasScaleConsumer(next, channel, axis.scale)
190
- ) {
191
- next = next[REMOVE_AXIS[channel]]({
192
- scale: axis.scale,
193
- ...(axis.coordinate === undefined ? {} : { coordinate: axis.coordinate })
194
- });
187
+ const owners = ["x", "y"].includes(channel)
188
+ ? cartesianAxisOwners(next).filter(owner => owner.channel === channel)
189
+ : [{ id: channel, channel, semantic: next.semanticSpec.guides.axis?.[channel] }];
190
+ for (const owner of owners) {
191
+ const axis = owner.semantic;
192
+ if (axis !== undefined && scales[channel].has(axis.scale) && !hasScaleConsumer(next, channel, axis.scale)) {
193
+ next = next[REMOVE_AXIS[channel]]({ ...axisTargetArgs(owner.id, channel), scale: axis.scale,
194
+ ...(axis.coordinate === undefined ? {} : { coordinate: axis.coordinate }) });
195
+ }
195
196
  }
196
197
  const grid = next.semanticSpec.guides.grid?.[definition.gridDirection];
197
198
  if (
@@ -262,6 +263,7 @@ export const removeMark = /* @__PURE__ */ closedAction(
262
263
  if (next.graphicSpec.objects[id] !== undefined) {
263
264
  next = next.editGraphics({ target: id, remove: true });
264
265
  }
266
+ next = next._withoutMaterializationConfig(["guides", "legendGroups", id]);
265
267
  next = next._withoutMaterializationConfig(["marks", id]);
266
268
  next = next._withoutMaterializationConfig(["jitters", id]);
267
269
  next = next._withoutMaterializationConfig(["pointPacking", id]);
@@ -298,13 +298,16 @@ const rematerializeRuleMark = /* @__PURE__ */ action(
298
298
  properties: item?.properties
299
299
  });
300
300
  if (box === undefined) throw new Error(`Rule mark "${id}" requires box span owner "${boxSpan}".`);
301
+ const pixels = resolved.markConfigs[boxSpan]?.boxPlot?.median?.width?.pixels;
301
302
  if (layer.encoding?.x?.fieldType === "quantitative") {
302
303
  startX = endX = mapped.x[index];
303
- startY = box.y;
304
- endY = box.y + box.height;
304
+ const height = pixels ?? box.height;
305
+ startY = box.y + (box.height - height) / 2;
306
+ endY = startY + height;
305
307
  } else {
306
- startX = box.x;
307
- endX = box.x + box.width;
308
+ const width = pixels ?? box.width;
309
+ startX = box.x + (box.width - width) / 2;
310
+ endX = startX + width;
308
311
  startY = endY = mapped.y[index];
309
312
  }
310
313
  } else if (mode === "fixed-span") {
@@ -3,6 +3,7 @@ import { action } from "../../../core/action.js";
3
3
  import { validateUserId } from "../../../core/identifiers.js";
4
4
  import {
5
5
  DEFAULT_TEXT_MARK,
6
+ TEXT_MARK_STYLE_OPTIONS as STYLE_OPTIONS,
6
7
  isSourceOwnedText,
7
8
  normalizeTextMarkConfig
8
9
  } from "../../../grammar/text.js";
@@ -41,10 +42,6 @@ import { resolveMarkGraphicPlacement } from
41
42
  "../../../materialization/graphicHierarchy.js";
42
43
  import { validateItemMissing } from "../../../grammar/itemMissing.js";
43
44
 
44
- const STYLE_OPTIONS = Object.freeze([
45
- "fill", "opacity", "fontSize", "fontFamily", "fontWeight",
46
- "align", "baseline", "rotation", "dx", "dy"
47
- ]);
48
45
  const CREATE_OPTIONS = Object.freeze(["id", "data", "source", "text", "missing", ...STYLE_OPTIONS]);
49
46
  const EDIT_OPTIONS = Object.freeze(["target", "missing", ...STYLE_OPTIONS]);
50
47
  const REMATERIALIZE_OPTIONS = Object.freeze(["id", "replayLayout"]);
@@ -195,7 +192,7 @@ const LABEL_OPTIONS = Object.freeze([
195
192
  ]);
196
193
 
197
194
  const ANNOTATION_OPTIONS = Object.freeze([
198
- "id", "text", "format", "x", "y", "space", "source", "data", "coordinate", "layout",
195
+ "id", "text", "format", "anchor", "x", "y", "space", "source", "data", "coordinate", "layout",
199
196
  ...STYLE_OPTIONS
200
197
  ]);
201
198
 
@@ -271,6 +268,15 @@ const createAnnotation = /* @__PURE__ */ action(
271
268
  },
272
269
  function (args = {}) {
273
270
  validateMarkOptions(args, ANNOTATION_OPTIONS, "createAnnotation");
271
+ if (Object.hasOwn(args, "anchor")) {
272
+ if (args.anchor !== "plot-center") {
273
+ throw new Error('createAnnotation anchor must be "plot-center".');
274
+ }
275
+ if (["x", "y", "space", "source", "align", "baseline"].some(key => Object.hasOwn(args, key))) {
276
+ throw new Error("createAnnotation plot-center owns x, y, space, align, and baseline and does not accept source.");
277
+ }
278
+ args = { ...args, space: "plot", x: 0.5, y: 0.5, align: "center", baseline: "middle" };
279
+ }
274
280
  if (!Object.hasOwn(args, "text")) {
275
281
  throw new Error("createAnnotation requires text.");
276
282
  }
@@ -482,6 +488,12 @@ const editTextMark = /* @__PURE__ */ action(
482
488
  if (Object.hasOwn(args, "missing") && layer.source !== undefined) {
483
489
  throw new Error("Text missing policy requires a row-backed text mark, not source-owned labels.");
484
490
  }
491
+ if (args.inheritColor && layer.source === undefined) {
492
+ throw new Error("Text inheritColor requires a source-owned label.");
493
+ }
494
+ if (Object.hasOwn(args, "fill") && layer.encoding?.color !== undefined) {
495
+ throw new Error("Text fill conflicts with its color encoding.");
496
+ }
485
497
  const semantic = Object.hasOwn(args, "missing")
486
498
  ? this.editSemantic({
487
499
  property: `layer[${layer.id}].mark.missing`,
@@ -144,6 +144,9 @@ function layoutInputs(program, layer) {
144
144
  sourceX: entry.anchor.x,
145
145
  sourceY: entry.anchor.y,
146
146
  text: entry.graphic.properties.text,
147
+ lines: entry.graphic.properties.lines,
148
+ fontFamily: entry.graphic.properties.fontFamily,
149
+ fontWeight: entry.graphic.properties.fontWeight,
147
150
  fontSize: entry.graphic.properties.fontSize,
148
151
  textAlign: entry.graphic.properties.textAlign,
149
152
  textBaseline: entry.graphic.properties.textBaseline,
@@ -1,7 +1,8 @@
1
+ import { validateGraphicItemLimit } from "../../core/validation.js";
1
2
  import { closedAction } from "../../core/action.js";
2
3
  import { validateUserId } from "../../core/identifiers.js";
3
4
  import { freezeOwned } from "../../core/immutable.js";
4
- import { validateGeneratedItemLimit } from "../../core/validation.js";
5
+ import { resolveGraphicItemCapacity } from "../../selectors/graphicCapacity.js";
5
6
  import {
6
7
  isDrawableGraphicType,
7
8
  isGraphicContainerType,
@@ -118,7 +119,7 @@ const createGraphics = /* @__PURE__ */ closedAction(
118
119
  throw new TypeError("createGraphics length must be a non-negative integer.");
119
120
  }
120
121
  if (length !== undefined) {
121
- validateGeneratedItemLimit(length, "createGraphics length");
122
+ validateGraphicItemLimit(length, resolveGraphicItemCapacity(this, { id, label: "createGraphics length", type: validatedType }));
122
123
  }
123
124
  if (validatedType === "collection" && length !== undefined) {
124
125
  throw new Error("Heterogeneous collections use editGraphics items instead of length.");
@@ -1,6 +1,7 @@
1
+ import { validateGraphicItemLimit } from "../../core/validation.js";
1
2
  import { closedAction } from "../../core/action.js";
2
3
  import { cloneAndFreeze, freezeOwned, isPlainObject } from "../../core/immutable.js";
3
- import { validateGeneratedItemLimit } from "../../core/validation.js";
4
+ import { resolveGraphicItemCapacity } from "../../selectors/graphicCapacity.js";
4
5
  import {
5
6
  isPrimitiveDrawableGraphicType,
6
7
  isStructuralGraphicType,
@@ -43,14 +44,14 @@ function validateCollectionItem(item, ownerId, index) {
43
44
  });
44
45
  }
45
46
 
46
- function replaceCollectionItems(value, id) {
47
+ function replaceCollectionItems(value, id, program) {
47
48
  if (!Array.isArray(value)) throw new TypeError("collection.items must be an array.");
48
- validateGeneratedItemLimit(value.length, "collection.items length");
49
+ validateGraphicItemLimit(value.length, resolveGraphicItemCapacity(program, { id, label: "collection.items length" }));
49
50
  return freezeOwned(value.map((item, index) => validateCollectionItem(item, id, index)));
50
51
  }
51
52
 
52
- function replaceDrawableItems(value, id, type) {
53
- const items = replaceCollectionItems(value, id);
53
+ function replaceDrawableItems(value, id, type, program) {
54
+ const items = replaceCollectionItems(value, id, program);
54
55
  if (!items.every(item => item.type === type)) {
55
56
  return freezeOwned({ type: "collection", items });
56
57
  }
@@ -102,15 +103,15 @@ function editGeneratedItem(owner, itemIndex, property, value) {
102
103
  return freezeOwned({ ...owner, items: freezeOwned(items) });
103
104
  }
104
105
 
105
- function editItems(owner, property, value, id) {
106
+ function editItems(owner, property, value, id, program) {
106
107
  if (owner.type === "collection" && property === "items") {
107
- return freezeOwned({ ...owner, items: replaceCollectionItems(value, id) });
108
+ return freezeOwned({ ...owner, items: replaceCollectionItems(value, id, program) });
108
109
  }
109
110
  if (property === "length") {
110
111
  if (!Number.isInteger(value) || value < 0) {
111
112
  throw new TypeError("Graphic collection length must be a non-negative integer.");
112
113
  }
113
- validateGeneratedItemLimit(value, "Graphic collection length");
114
+ validateGraphicItemLimit(value, resolveGraphicItemCapacity(program, { id, label: "Graphic collection length" }));
114
115
  const items = owner.items.slice(0, value);
115
116
  for (let index = items.length; index < value; index += 1) {
116
117
  if (owner.type === "collection") {
@@ -189,6 +190,9 @@ const editGraphics = /* @__PURE__ */ closedAction(
189
190
 
190
191
  const ownerId = found.kind === "item" ? found.ownerId : found.id;
191
192
  const owner = found.kind === "item" ? found.owner : found.object;
193
+ if (owner.items !== undefined) validateGraphicItemLimit(owner.items.length, resolveGraphicItemCapacity(this, {
194
+ id: ownerId, label: "Graphic collection length"
195
+ }));
192
196
  const replacesDrawableItems =
193
197
  found.kind === "object" && property === "items" &&
194
198
  owner.items !== undefined && owner.type !== "collection";
@@ -199,7 +203,7 @@ const editGraphics = /* @__PURE__ */ closedAction(
199
203
  : owner.type, property);
200
204
  let updated;
201
205
  if (replacesDrawableItems) {
202
- updated = replaceDrawableItems(value, ownerId, owner.type);
206
+ updated = replaceDrawableItems(value, ownerId, owner.type, this);
203
207
  } else if (found.kind === "item") {
204
208
  if (validatedProperty === "length" || validatedProperty === "items") {
205
209
  throw new Error(`${validatedProperty} can only target an owning graphic.`);
@@ -212,12 +216,12 @@ const editGraphics = /* @__PURE__ */ closedAction(
212
216
  properties: setGraphicProperty(owner.properties, validatedProperty, value)
213
217
  });
214
218
  } else if (owner.items !== undefined) {
215
- updated = editItems(owner, validatedProperty, value, ownerId);
219
+ updated = editItems(owner, validatedProperty, value, ownerId, this);
216
220
  } else if (validatedProperty === "length") {
217
221
  if (!Number.isInteger(value) || value < 0) {
218
222
  throw new TypeError("Graphic collection length must be a non-negative integer.");
219
223
  }
220
- validateGeneratedItemLimit(value, "Graphic collection length");
224
+ validateGraphicItemLimit(value, resolveGraphicItemCapacity(this, { id: ownerId, label: "Graphic collection length" }));
221
225
  updated = freezeOwned({
222
226
  type: owner.type,
223
227
  items: freezeOwned(Array.from({ length: value }, (_, index) => freezeOwned({
@@ -1,6 +1 @@
1
- export function editGraphicProperties(program, target, properties) {
2
- for (const [property, value] of Object.entries(properties)) {
3
- program = program.editGraphics({ target, property, value });
4
- }
5
- return program;
6
- }
1
+ export { editGraphicProperties } from "../../core/graphicProperties.js";
@@ -57,7 +57,7 @@ function updateEntity(spec, parsed, value) {
57
57
  source.schema,
58
58
  value[0],
59
59
  undefined,
60
- { sourceId: source.id, ownerId: parsed.id }
60
+ { sourceId: source.id, ownerId: parsed.id, sourceTransform: source.transform?.[0] }
61
61
  ));
62
62
  }
63
63
  }
@@ -72,6 +72,7 @@ function updateEntity(spec, parsed, value) {
72
72
  const schema = source?.schema !== undefined && current.transform?.length === 1
73
73
  ? deriveTransformSchema(source.schema, current.transform[0], value, {
74
74
  sourceId: source.id,
75
+ sourceTransform: source.transform?.[0],
75
76
  ownerId: parsed.id
76
77
  })
77
78
  : inferDatasetSchema(value);
@@ -225,6 +226,31 @@ export function withoutPreviewLayerEncodings(program, { id, channels }) {
225
226
  return program._clone({ semanticSpec });
226
227
  }
227
228
 
229
+ // Composition materialization may retain this native two-scale guide meaning.
230
+ // Use the existing semantic primitives on a private unit adapter; this does not
231
+ // relax the public composition guard or accept arbitrary semantic paths.
232
+ export function synchronizeFacetColorSizeMeaning(program, meaning) {
233
+ if (program.compositionSpec?.type !== "facet") {
234
+ throw new Error("Color-size parent meaning requires a facet composition.");
235
+ }
236
+ if (meaning !== undefined && (!isPlainObject(meaning) ||
237
+ Object.keys(meaning).some(key => !["colorScale", "sizeScale", "title"].includes(key)))) {
238
+ throw new Error("Color-size parent meaning requires its native scale bindings and title.");
239
+ }
240
+ let unit = new program.constructor({ semanticSpec: program.semanticSpec, graphicSpec: program.graphicSpec,
241
+ resolvedScales: program.resolvedScales, materializationConfigs: program.materializationConfigs,
242
+ context: program.context, trace: program.trace, actionStack: program.actionStack,
243
+ actionSequence: program._actionSequence });
244
+ if (unit.semanticSpec.guides.legend?.colorSize !== undefined) {
245
+ unit = unit.editSemantic({ property: "guide.legend.colorSize", remove: true });
246
+ }
247
+ if (meaning !== undefined) for (const [property, value] of Object.entries(meaning)) {
248
+ unit = unit.editSemantic({ property: `guide.legend.colorSize.${property}`, value });
249
+ }
250
+ return program._clone({ semanticSpec: unit.semanticSpec, context: unit.context,
251
+ trace: unit.trace, actionStack: unit.actionStack, actionSequence: unit._actionSequence });
252
+ }
253
+
228
254
  export function createSemanticPrimitiveAction(validateSemanticValue, planNamedRemoval) {
229
255
  return closedAction(
230
256
  {
@@ -1,3 +1,4 @@
1
+ import {isSizeShapeChannels} from "../../../grammar/legendCombinations.js";
1
2
  import { normalizeLegendOrder } from "../../../grammar/categoryOrder.js";
2
3
  import { validateUserId } from "../../../core/identifiers.js";
3
4
  import { CATEGORICAL_LEGEND_CHANNELS } from "../../../core/vocabulary.js";
@@ -16,7 +17,7 @@ function validateLegend(property, value) {
16
17
  throw new Error(`Legend ${property} must not contain duplicates.`);
17
18
  }
18
19
  if (property === "channels") {
19
- if (!value.every(channel => CATEGORICAL_LEGEND_CHANNELS.includes(channel))) {
20
+ if (!isSizeShapeChannels(value) && !value.every(channel => CATEGORICAL_LEGEND_CHANNELS.includes(channel))) {
20
21
  throw new Error("Legend channels support only color, strokeDash, and shape.");
21
22
  }
22
23
  return;
@@ -26,6 +27,11 @@ function validateLegend(property, value) {
26
27
 
27
28
  export function validateGuideSemanticValue(_program, parsed, value) {
28
29
  const property = parsed.path.at(-1);
30
+ if (parsed.id === "legend.colorSize") {
31
+ if(property === "title") validateNonEmptySemanticString(value,"Legend title");
32
+ else validateUserId(value,"Legend scale id");
33
+ return;
34
+ }
29
35
  if (parsed.id === "axis.parallel" && property === "titles") {
30
36
  validateParallelAxisTitles(value);
31
37
  return;
@@ -44,6 +50,10 @@ export function validateGuideSemanticValue(_program, parsed, value) {
44
50
  throw new TypeError("Parallel axis scales must contain at least two ids.");
45
51
  }
46
52
  value.forEach(id => validateUserId(id, "Parallel axis scale id"));
53
+ } else if (parsed.id.startsWith("axis.") && property === "channel") {
54
+ if (!["x", "y"].includes(value)) throw new Error("Named Cartesian axis channel requires x or y.");
55
+ } else if (parsed.id.startsWith("axis.") && property === "position") {
56
+ if (!["top", "bottom", "left", "right"].includes(value)) throw new Error("Named Cartesian axis position requires a physical side.");
47
57
  } else if (property === "title") {
48
58
  validateNonEmptySemanticString(value, "Axis title");
49
59
  } else {
@@ -66,6 +66,14 @@ export function validateLayerSemanticValue(
66
66
  throw new Error(`Mark type "${markType}" does not support a missing policy.`);
67
67
  }
68
68
  }
69
+ if (property === "mark.orientation" && (findLayer(program, parsed.id)?.mark?.type !== "bar" ||
70
+ !["vertical", "horizontal"].includes(value))) {
71
+ throw new Error("Bar orientation must be vertical or horizontal.");
72
+ }
73
+ if (property === "mark.blend" &&
74
+ (findLayer(program, parsed.id)?.mark?.type !== "bar" || value !== "difference")) {
75
+ throw new Error("Bar blend must be difference; remove the optional intent for normal painting.");
76
+ }
69
77
  if (property === "derivedBindings.regression") {
70
78
  if (!value || typeof value !== "object" || Array.isArray(value) ||
71
79
  Object.keys(value).length !== 2 || value.mode !== "follow" ||
@@ -10,7 +10,7 @@ import {
10
10
  } from "./resolve.js";
11
11
 
12
12
  const REGRESSION_OPTIONS = Object.freeze([
13
- "target", "x", "y", "groupBy", "method", "degree", "span",
13
+ "target", "x", "y", "groupBy", "method", "degree", "span", "robustIterations",
14
14
  "confidenceMethod", "level", "confidence", "interval", "band", "line"
15
15
  , "predict", "sourceBinding", "missing"
16
16
  ]);
@@ -19,9 +19,10 @@ import { findLayer } from "../../selectors/layers.js";
19
19
  import { removeOwnedMark } from "../marks/remove.js";
20
20
  import { removeOwnedColorLegends } from "../guides/legends/remove.js";
21
21
  import { requireRegressionField } from "./resolve.js";
22
+ import { buildSourceRevisionPlan, applyRevisionPlan } from "../data/edit.js";
22
23
 
23
24
  const OPTIONS = Object.freeze([
24
- "target", "data", "x", "y", "groupBy", "method", "degree", "span",
25
+ "target", "data", "x", "y", "groupBy", "method", "degree", "span", "robustIterations",
25
26
  "confidenceMethod", "level", "confidence", "interval", "band", "line"
26
27
  , "predict", "sourceBinding", "missing"
27
28
  ]);
@@ -111,14 +112,14 @@ function resolveParameters(previous, args) {
111
112
  const method = args.method ?? previous.method;
112
113
  const raw = { method };
113
114
  for (const key of [
114
- "degree", "span", "confidenceMethod", "level", "confidence", "interval"
115
+ "degree", "span", "robustIterations", "confidenceMethod", "level", "confidence", "interval"
115
116
  ]) {
116
117
  if (Object.hasOwn(args, key)) raw[key] = args[key];
117
118
  }
118
119
  const inheritsLevel = !Object.hasOwn(raw, "confidence");
119
120
  if (method === previous.method && args.interval !== false) {
120
121
  for (const key of [
121
- "degree", "span", "confidenceMethod", "level", "interval"
122
+ "degree", "span", "robustIterations", "confidenceMethod", "level", "interval"
122
123
  ]) {
123
124
  if (
124
125
  !Object.hasOwn(raw, key) &&
@@ -128,7 +129,7 @@ function resolveParameters(previous, args) {
128
129
  raw[key] = previous[key];
129
130
  }
130
131
  }
131
- } else if (method !== "loess" && previous.method !== "loess" && args.interval !== false) {
132
+ } else if (!["loess", "exp"].includes(method) && !["loess", "exp"].includes(previous.method) && args.interval !== false) {
132
133
  for (const key of ["confidenceMethod", "level", "interval"]) {
133
134
  if (
134
135
  !Object.hasOwn(raw, key) &&
@@ -295,6 +296,11 @@ export const editRegression = /* @__PURE__ */ closedAction(
295
296
  const changesParameters = JSON.stringify(parameters) !==
296
297
  JSON.stringify(current.parameters);
297
298
  const changesStatistics = changesProvenance || changesParameters;
299
+ const hasStatistics = this.semanticSpec.datasets.some(dataset =>
300
+ dataset.source === current.dataId && dataset.transform?.[0]?.type === "regressionStatistics");
301
+ if (hasStatistics && parameters.method !== "linear") {
302
+ throw new Error("Remove dependent regression statistics before changing the linear fit method.");
303
+ }
298
304
  const bandPatch = Object.hasOwn(args, "band")
299
305
  ? validateBandPatch(args.band)
300
306
  : undefined;
@@ -302,14 +308,16 @@ export const editRegression = /* @__PURE__ */ closedAction(
302
308
  ? validateLinePatch(args.line)
303
309
  : undefined;
304
310
  if ((parameters.method === "loess" || parameters.interval === false) && bandPatch !== undefined && bandPatch !== false) {
305
- throw new Error("LOESS regression does not support a band object.");
311
+ throw new Error(parameters.method === "loess"
312
+ ? "LOESS regression does not support a band object."
313
+ : "Regression interval false cannot include a band object.");
306
314
  }
307
315
 
308
316
  const hadBand = current.bandId !== undefined;
309
317
  const wantsBand = parameters.method !== "loess" && parameters.interval !== false &&
310
318
  (bandPatch === false
311
319
  ? false
312
- : hadBand || bandPatch !== undefined || current.parameters.method === "loess");
320
+ : hadBand || bandPatch !== undefined || ["loess", "exp"].includes(current.parameters.method));
313
321
  const revision = changesStatistics
314
322
  ? planDerivedDataRevision(this, {
315
323
  owner: owner.id,
@@ -321,6 +329,8 @@ export const editRegression = /* @__PURE__ */ closedAction(
321
329
  ]
322
330
  })
323
331
  : undefined;
332
+ const statisticsRevision = changesStatistics && hasStatistics
333
+ ? buildSourceRevisionPlan(this, current.dataId, revision.id) : undefined;
324
334
 
325
335
  const applyEdit = program => {
326
336
  let dataId = current.dataId;
@@ -412,6 +422,8 @@ export const editRegression = /* @__PURE__ */ closedAction(
412
422
  }
413
423
  });
414
424
  if (changesStatistics) {
425
+ if (statisticsRevision !== undefined) next = applyRevisionPlan(next, undefined,
426
+ statisticsRevision, { retain: new Set([current.dataId]) });
415
427
  next = next.releaseDerivedData(revision.release);
416
428
  }
417
429
  const binding = findLayer(next, owner.id)?.derivedBindings?.regression;
@@ -1,3 +1,5 @@
1
+ import { requireStringValue as requireRegressionField } from "../../core/validation.js";
2
+ export { requireStringValue as requireRegressionField } from "../../core/validation.js";
1
3
  import { isPlainObject } from "../../core/immutable.js";
2
4
  import { findLayer } from "../../selectors/layers.js";
3
5
 
@@ -8,12 +10,6 @@ export function requireRegressionObject(value, label) {
8
10
  return value;
9
11
  }
10
12
 
11
- export function requireRegressionField(value, label) {
12
- if (typeof value !== "string" || value.length === 0) {
13
- throw new TypeError(`${label} must be a non-empty string.`);
14
- }
15
- return value;
16
- }
17
13
 
18
14
  export function findRegressionPoint(program, requested) {
19
15
  const eligible = program.semanticSpec.layers.filter(layer =>
@@ -5,6 +5,7 @@ import { normalizePositionScaleChannel } from "../../core/vocabulary.js";
5
5
  import { findLayer } from "../../selectors/layers.js";
6
6
  import { requireSemanticScale } from "../../selectors/scales.js";
7
7
  import { findScaleConsumers } from "./consumers/index.js";
8
+ import { axisScaleBindings } from "../../materialization/coordinateBounds.js";
8
9
 
9
10
  const DEFINITIONS = Object.freeze({
10
11
  editXScale: Object.freeze({
@@ -41,7 +42,7 @@ const DEFINITIONS = Object.freeze({
41
42
  channel: "color",
42
43
  options: Object.freeze([
43
44
  "type", "domain", "range", "emptyDomain", "clamp", "reverse", "palette",
44
- "interpolate", "midpoint", "unknown"
45
+ "interpolate", "whitePoint", "gamma", "midpoint", "base", "exponent", "constant", "unknown"
45
46
  ])
46
47
  }),
47
48
  editStrokeScale: Object.freeze({
@@ -49,7 +50,7 @@ const DEFINITIONS = Object.freeze({
49
50
  targetOnly: true,
50
51
  options: Object.freeze([
51
52
  "type", "domain", "range", "emptyDomain", "clamp", "reverse", "palette",
52
- "interpolate", "midpoint", "unknown"
53
+ "interpolate", "whitePoint", "gamma", "midpoint", "base", "exponent", "constant", "unknown"
53
54
  ])
54
55
  }),
55
56
  editSizeScale: Object.freeze({
@@ -99,9 +100,11 @@ function allConsumers(program) {
99
100
  }
100
101
 
101
102
  function channelScaleIds(program, channel) {
102
- return [...new Set(allConsumers(program)
103
+ return [...new Set([...allConsumers(program)
103
104
  .filter(consumer => normalizePositionScaleChannel(consumer.channel) === channel)
104
- .map(consumer => consumer.encoding.scale))];
105
+ .map(consumer => consumer.encoding.scale),
106
+ ...program.semanticSpec.scales.filter(scale => axisScaleBindings(program, scale.id)
107
+ .some(binding => binding.channel === channel)).map(scale => scale.id)])];
105
108
  }
106
109
 
107
110
  function requireSingleScale(ids, operation, scope) {
@@ -115,6 +118,8 @@ function requireSingleScale(ids, operation, scope) {
115
118
  function validateScaleChannel(program, id, channel, operation) {
116
119
  requireSemanticScale(program, id);
117
120
  const consumers = findScaleConsumers(program, id);
121
+ const axes = axisScaleBindings(program, id);
122
+ if (consumers.length === 0 && axes.length === 1 && axes[0].channel === channel) return id;
118
123
  if (consumers.length === 0) {
119
124
  throw new Error(`${operation} requires a scale bound to the ${channel} channel.`);
120
125
  }
@@ -1,8 +1,9 @@
1
1
  import { isPendingMeasuredRadiusConsumer } from "../../../materialization/scales/policies/arc.js";
2
- import { deriveMeasuredArcValues } from "../../../grammar/arcs.js";
2
+ import { deriveMeasuredArcValues, deriveProportionalArcRadiusValues } from "../../../grammar/arcs.js";
3
3
  import { deriveBarAggregates } from "../../../grammar/bars/aggregate.js";
4
4
  import {
5
5
  BAR_GRAINS,
6
+ resolveBarChannels,
6
7
  resolveBarGrain
7
8
  } from "../../../grammar/bars/policy.js";
8
9
  import {
@@ -28,9 +29,15 @@ function lineDerivationOptions(program, consumer, dataset) {
28
29
  }
29
30
 
30
31
  export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
31
- if (consumer.layer.mark?.type === "arc" && consumer.channel === "radius" &&
32
- findScale(program, consumer.encoding.scale).radialMapping !== undefined) {
33
- return { matched: true, values: isPendingMeasuredRadiusConsumer(consumer) ? [] : deriveMeasuredArcValues(dataset.values, consumer.layer).map(item => item.radius) };
32
+ if (consumer.layer.mark?.type === "arc" && consumer.channel === "radius") {
33
+ let derive;
34
+ if (findScale(program, consumer.encoding.scale).radialMapping !== undefined) derive = deriveMeasuredArcValues;
35
+ else if (consumer.layer.encoding?.theta?.fieldType === "quantitative" ||
36
+ consumer.layer.encoding?.theta?.aggregate !== undefined || consumer.encoding.aggregate !== undefined) {
37
+ derive = deriveProportionalArcRadiusValues;
38
+ }
39
+ if (derive !== undefined) return { matched: true, values: isPendingMeasuredRadiusConsumer(consumer)
40
+ ? [] : derive(dataset.values, consumer.layer).map(item => item.radius) };
34
41
  }
35
42
 
36
43
  if (consumer.layer.mark?.type === "rect") {
@@ -44,6 +51,10 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
44
51
  };
45
52
  }
46
53
  const { layer, channel } = consumer;
54
+ if (resolveBarGrain(layer) === BAR_GRAINS.centered &&
55
+ channel === resolveBarChannels(layer).measure && layer.encoding[`${channel}2`] === undefined) {
56
+ return { matched: true, values: [0, ...dataset.values.map(row => row[consumer.encoding.field])] };
57
+ }
47
58
  const encoding = layer.encoding ?? {};
48
59
  const appearance = ["stroke", "strokeWidth", "opacity"].includes(channel);
49
60
  if (
@@ -75,7 +86,7 @@ export function resolveMarkFamilyConsumerValues(program, consumer, dataset) {
75
86
  : channel === "x" ? derived.xValues : derived.yValues
76
87
  };
77
88
  }
78
- if (resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate) {
89
+ if (resolveBarGrain(consumer.layer) === BAR_GRAINS.aggregate && ["x", "y", "color"].includes(consumer.channel)) {
79
90
  const derived = deriveBarAggregates(dataset.values, consumer.layer);
80
91
  if (consumer.channel === "color") {
81
92
  return {
@@ -38,7 +38,7 @@ const CREATE_SCALE_OPTIONS = Object.freeze([
38
38
  "padding",
39
39
  "align",
40
40
  "palette",
41
- "interpolate", "midpoint",
41
+ "interpolate", "whitePoint", "gamma", "midpoint",
42
42
  "radialMapping", "unknown"
43
43
  ]);
44
44
 
@@ -87,14 +87,18 @@ export const createScale = /* @__PURE__ */ action(
87
87
  validateOptions(args);
88
88
  const id = validateUserId(args.id, "Scale id");
89
89
  const type = validateScaleType(args.type ?? "linear");
90
- const colorType = isColorScaleType(type);
90
+ const colorType = isColorScaleType(type) && (
91
+ !["log", "pow", "sqrt", "symlog"].includes(type) || args.palette !== undefined ||
92
+ args.interpolate !== undefined || (Array.isArray(args.range) && typeof args.range[0] === "string") ||
93
+ args.range?.palette !== undefined
94
+ );
91
95
  if (args.palette !== undefined && args.range !== undefined) {
92
96
  throw new Error("Color scale cannot specify both palette and range.");
93
97
  }
94
98
  if (args.palette !== undefined && !colorType) {
95
99
  throw new Error(`Scale type "${type}" does not support palette.`);
96
100
  }
97
- if (args.interpolate !== undefined && type !== "sequential") {
101
+ if (args.interpolate !== undefined && !colorType) {
98
102
  throw new Error(`Scale type "${type}" does not support interpolate.`);
99
103
  }
100
104
  const requestedRange = args.palette === undefined
@@ -129,6 +133,7 @@ export const createScale = /* @__PURE__ */ action(
129
133
  })
130
134
  : normalizeScaleDefinition({
131
135
  type,
136
+ color: colorType,
132
137
  patch: {
133
138
  ...args,
134
139
  ...(requestedRange === undefined ? {} : { range: requestedRange })
@@ -143,7 +148,7 @@ export const createScale = /* @__PURE__ */ action(
143
148
  validateRange: (scaleType, value) =>
144
149
  isDiscretizedColorScaleType(scaleType)
145
150
  ? validateDiscretizedColorRange(value)
146
- : isContinuousColorScaleType(scaleType)
151
+ : colorType && isContinuousColorScaleType(scaleType)
147
152
  ? validateSequentialColorRange(value)
148
153
  : scaleType === "ordinal"
149
154
  ? validateOrdinalRange(value)
@@ -166,7 +171,7 @@ export const createScale = /* @__PURE__ */ action(
166
171
 
167
172
  for (const property of [
168
173
  "emptyDomain", "nice", "zero", "clamp", "reverse", "base", "exponent", "constant",
169
- "paddingInner", "paddingOuter", "padding", "align", "interpolate", "midpoint",
174
+ "paddingInner", "paddingOuter", "padding", "align", "interpolate", "whitePoint", "gamma", "midpoint",
170
175
  "radialMapping", "unknown"
171
176
  ]) {
172
177
  if (definition[property] === undefined) continue;