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 { resolveGraphicBounds } from "../../layout/canvas.js";
1
2
  import { action, closedAction } from "../../core/action.js";
2
3
  import { registerFacetDataRevision, reviseUnitData } from "../data/revise.js";
3
4
  import { freezeOwned, isPlainObject } from "../../core/immutable.js";
@@ -38,16 +39,16 @@ import {
38
39
  } from "../../materialization/facetHeaders.js";
39
40
 
40
41
  const FACET_OPTIONS = Object.freeze([
41
- "id", "field", "data", "values", "columns", "gap", "align", "padding", "scales",
42
- "guides"
42
+ "id", "field", "data", "values", "columns", "gap", "spacing", "align", "padding", "scales",
43
+ "guides", "headers"
43
44
  ]);
44
45
  const FACET_GRID_OPTIONS = Object.freeze([
45
- "id", "data", "rows", "columns", "combinations", "gap", "align",
46
- "padding", "scales", "guides"
46
+ "id", "data", "rows", "columns", "combinations", "gap", "spacing", "align",
47
+ "padding", "scales", "guides", "headers"
47
48
  ]);
48
49
  const REPEAT_OPTIONS = Object.freeze([
49
- "id", "target", "channel", "fields", "columns", "gap", "align",
50
- "padding", "scales", "guides"
50
+ "id", "target", "channel", "fields", "columns", "gap", "spacing", "align",
51
+ "padding", "scales", "guides", "headers"
51
52
  ]);
52
53
  const SOURCE_EDIT_OPTIONS = Object.freeze(["program"]);
53
54
  const GUIDE_OPTIONS = Object.freeze(["axes", "legend"]);
@@ -59,7 +60,7 @@ const HEADER_STYLE_OPTIONS = Object.freeze([
59
60
  "fontSize", "fontFamily", "fontWeight", "color", "offset", "align"
60
61
  ]);
61
62
 
62
- function normalizeGuides(guides) {
63
+ function normalizeGuides(guides, allowStored = false) {
63
64
  if (guides === undefined) return { axes: "each", legend: false };
64
65
  validateOptionObject(guides, GUIDE_OPTIONS, "facet.guides");
65
66
  const axes = guides.axes ?? "each";
@@ -67,8 +68,11 @@ function normalizeGuides(guides) {
67
68
  throw new Error('facet guides.axes must be "each" or "outer".');
68
69
  }
69
70
  const legend = guides.legend ?? false;
70
- if (legend !== false && legend !== "shared") {
71
- throw new Error('facet guides.legend must be false or "shared".');
71
+ if (legend !== false && legend !== "shared" && !isPlainObject(legend)) {
72
+ throw new Error('facet guides.legend must be false, "shared", or legend options.');
73
+ }
74
+ if (!allowStored && isPlainObject(legend) && Object.hasOwn(legend, "type")) {
75
+ throw new Error("Facet legend options do not accept internal type tags.");
72
76
  }
73
77
  return { axes, legend };
74
78
  }
@@ -184,65 +188,149 @@ function rederiveFacet(program, { scales, guides }) {
184
188
  }, compositionSpec.children);
185
189
  }
186
190
 
187
- export const facet = /* @__PURE__ */ closedAction(
188
- {
189
- op: "facet",
190
- description: "Repeat one direct-source chart by field value."
191
- }, FACET_OPTIONS,
192
- function (args = {}) {
193
- const guides = normalizeGuides(args.guides);
194
- const definition = resolveFacetDefinition(this.semanticSpec, args);
195
- if (definition.family !== "cartesian" && guides.axes === "outer") {
196
- throw new Error("Polar and Parallel facets do not support outer axes.");
191
+ function resolveEditedFacetHeaders(currentHeaders, args, grid, operation = "editFacetHeaders") {
192
+ validateOptionObject(args, HEADER_OPTIONS, operation, {
193
+ allowEmpty: false,
194
+ emptyMessage: `${operation} requires at least one change.`
195
+ });
196
+ const role = args.role ?? "all";
197
+ if (!["all", "row", "column"].includes(role)) {
198
+ throw new Error(`Unknown facet header role "${role}".`);
199
+ }
200
+ if (role === "all" && Object.hasOwn(args, "side")) {
201
+ throw new Error(`${operation} side requires an explicit row or column role.`);
202
+ }
203
+ if (role === "row" && grid === undefined) {
204
+ throw new Error(`${operation} row role requires a row-column facet grid.`);
205
+ }
206
+ if (Object.hasOwn(args, "side")) {
207
+ const sides = role === "row" ? ["left", "right"]
208
+ : grid === undefined
209
+ ? ["top", "bottom", "left", "right"] : ["top", "bottom"];
210
+ if (!sides.includes(args.side)) {
211
+ throw new Error(`${operation} ${role} side must be ${sides.join(" or ")}.`);
197
212
  }
198
- const scalePolicies = normalizeFacetScalePolicies(
199
- this.semanticSpec,
200
- args.scales ?? {}
201
- );
202
- const derived = deriveFacetChildren(this, definition, {
203
- closeInheritedAction: true,
204
- stripTitle: true,
205
- scales: args.scales ?? {}
206
- });
207
- const preflight = resolveFacetLayout({
208
- children: definition.cells.map(cell => ({
209
- ...compositionChildDescriptor(cell.id, derived.children[cell.id]),
210
- value: cell.value
211
- })),
212
- ...(Object.hasOwn(args, "columns") ? { columns: args.columns } : {}),
213
- ...(Object.hasOwn(args, "gap") ? { gap: args.gap } : {}),
214
- ...(Object.hasOwn(args, "align") ? { align: args.align } : {}),
215
- ...(Object.hasOwn(args, "padding") ? { padding: args.padding } : {}),
216
- sharedLegend: guides.legend === "shared"
217
- });
218
- const compositionSpec = {
219
- id: definition.id,
220
- type: "facet",
221
- children: definition.cells.map(cell => cell.id),
222
- columns: preflight.columns,
223
- gap: preflight.gap,
224
- align: preflight.align,
225
- padding: preflight.padding,
226
- facet: {
227
- data: definition.data,
228
- field: definition.field,
229
- values: definition.values,
230
- scales: scalePolicies.channels,
231
- guides
232
- }
233
- };
234
- return applyCompositionState(
235
- this._withMaterializationConfig(["facets", definition.id], {
236
- headers: createDefaultFacetHeaders()
237
- }),
238
- {
239
- children: derived.children,
240
- compositionSpec
241
- },
242
- compositionSpec.children
243
- );
244
213
  }
245
- );
214
+ if (Object.hasOwn(args, "align") &&
215
+ !["start", "center", "end"].includes(args.align)) {
216
+ throw new Error(`${operation} align must be start, center, or end.`);
217
+ }
218
+ if (role === "all" && Object.keys(args).every(key => key === "role")) {
219
+ throw new Error(`${operation} requires at least one change.`);
220
+ }
221
+ const current = normalizeFacetHeadersConfig(currentHeaders);
222
+ const owner = role === "all" ? "common" : role;
223
+ const patch = { ...current[owner] };
224
+ for (const key of HEADER_STYLE_OPTIONS) {
225
+ if (Object.hasOwn(args, key)) patch[key] = args[key];
226
+ }
227
+ if (Object.hasOwn(args, "side")) patch.side = args.side;
228
+ if (Object.hasOwn(args, "labelMap")) {
229
+ if (args.labelMap === "auto") delete patch.labelMap;
230
+ else {
231
+ patch.labelMap = normalizeDisplayLabelMap(
232
+ args.labelMap,
233
+ `${operation} ${role} labelMap`
234
+ );
235
+ }
236
+ }
237
+ const headers = {
238
+ ...current,
239
+ ...(role === "all" ? {} : { mode: "roles" }),
240
+ [owner]: patch
241
+ };
242
+ for (const headerRole of ["all", "row", "column"]) {
243
+ const resolved = resolveFacetHeaderConfig(headers, headerRole);
244
+ validatePositiveFinite(resolved.fontSize, `Facet ${headerRole} header fontSize`);
245
+ validateNonEmptyString(resolved.fontFamily, `Facet ${headerRole} header fontFamily`);
246
+ validateNonEmptyString(resolved.color, `Facet ${headerRole} header color`);
247
+ validateNonNegativeFinite(resolved.offset, `Facet ${headerRole} header offset`);
248
+ if (!(
249
+ (typeof resolved.fontWeight === "string" && resolved.fontWeight.length > 0) ||
250
+ Number.isFinite(resolved.fontWeight)
251
+ )) {
252
+ throw new TypeError(
253
+ `Facet ${headerRole} header fontWeight must be a non-empty string or number.`
254
+ );
255
+ }
256
+ if (!["start", "center", "end"].includes(resolved.align)) {
257
+ throw new Error(`Facet ${headerRole} header align must be start, center, or end.`);
258
+ }
259
+ if (resolved.labelMap !== undefined) {
260
+ normalizeDisplayLabelMap(
261
+ resolved.labelMap,
262
+ `Facet ${headerRole} header labelMap`
263
+ );
264
+ }
265
+ }
266
+ return headers;
267
+ }
268
+
269
+ function initialFacetHeaders(value, grid) {
270
+ let headers = createDefaultFacetHeaders();
271
+ if (value === undefined) return headers;
272
+ validateOptionObject(value, [...HEADER_STYLE_OPTIONS, "labelMap", "row", "column"], "facet.headers");
273
+ const { row, column, ...common } = value;
274
+ if (Object.keys(common).length > 0) headers = resolveEditedFacetHeaders(headers, common, grid, "facet.headers");
275
+ for (const [role, patch] of [["row", row], ["column", column]]) {
276
+ if (patch === undefined) continue;
277
+ validateOptionObject(patch, HEADER_OPTIONS.filter(key => key !== "role"), `facet.headers.${role}`);
278
+ headers = resolveEditedFacetHeaders(headers, { ...patch, role }, grid, `facet.headers.${role}`);
279
+ }
280
+ return headers;
281
+ }
282
+
283
+ function composeDerivedFacet(program, args, { definition, derived, guides, scalePolicies, facet }) {
284
+ const headers = initialFacetHeaders(args.headers, definition.grid);
285
+ const preflight = resolveFacetLayout({
286
+ spacing: args.spacing,
287
+ plots: Object.entries(derived.children).map(([id, child]) => ({ id, ...resolveGraphicBounds(child) })),
288
+ children: definition.cells.map(cell => ({
289
+ ...compositionChildDescriptor(cell.id, derived.children[cell.id]), value: cell.value,
290
+ ...(definition.grid === undefined ? {} : { row: cell.row, column: cell.column })
291
+ })),
292
+ columns: definition.grid?.columns.values.length ?? args.columns,
293
+ gap: args.gap, align: args.align, padding: args.padding,
294
+ sharedLegend: guides.legend === "shared"
295
+ });
296
+ const compositionSpec = {
297
+ id: definition.id, type: "facet", children: definition.cells.map(cell => cell.id),
298
+ columns: preflight.columns, gap: preflight.gap, align: preflight.align, padding: preflight.padding,
299
+ ...(preflight.spacing === undefined ? {} : { spacing: preflight.spacing }),
300
+ facet: { data: definition.data, ...facet, scales: scalePolicies.channels, guides }
301
+ };
302
+ return applyCompositionState(
303
+ program._withMaterializationConfig(["facets", definition.id], { headers }),
304
+ { children: derived.children, compositionSpec }, compositionSpec.children
305
+ );
306
+ }
307
+
308
+ function partitionFacetAction(op, description, definitionResolver) {
309
+ return closedAction({ op, description }, op === "facet" ? FACET_OPTIONS : FACET_GRID_OPTIONS,
310
+ function (args = {}) {
311
+ const guides = normalizeGuides(args.guides);
312
+ const definition = definitionResolver(this.semanticSpec, args);
313
+ if (definition.family !== "cartesian" && guides.axes === "outer") {
314
+ throw new Error("Polar and Parallel facets do not support outer axes.");
315
+ }
316
+ const scalePolicies = normalizeFacetScalePolicies(this.semanticSpec, args.scales ?? {});
317
+ const derived = deriveFacetChildren(this, definition, {
318
+ closeInheritedAction: true, stripTitle: true, scales: args.scales ?? {}
319
+ });
320
+ const facet = definition.grid === undefined
321
+ ? { field: definition.field, values: definition.values }
322
+ : { values: definition.cells.map(cell => cell.value), grid: {
323
+ ...definition.grid, cells: definition.cells.map(cell => ({
324
+ id: cell.id, row: cell.row, column: cell.column,
325
+ rowValue: cell.rowValue, columnValue: cell.columnValue, empty: cell.empty
326
+ }))
327
+ } };
328
+ return composeDerivedFacet(this, args, { definition, derived, guides, scalePolicies, facet });
329
+ });
330
+ }
331
+
332
+ export const facet = /* @__PURE__ */ partitionFacetAction("facet",
333
+ "Repeat one direct-source chart by field value.", resolveFacetDefinition);
246
334
 
247
335
  function resolveRepeatDefinition(program, args) {
248
336
  if (!isPlainObject(args)) {
@@ -453,115 +541,16 @@ export const repeatCharts = /* @__PURE__ */ closedAction(
453
541
  requestedScales,
454
542
  true
455
543
  );
456
- const preflight = resolveFacetLayout({
457
- children: definition.cells.map(cell => ({
458
- ...compositionChildDescriptor(cell.id, derived.children[cell.id]),
459
- value: cell.value
460
- })),
461
- ...(Object.hasOwn(args, "columns") ? { columns: args.columns } : {}),
462
- ...(Object.hasOwn(args, "gap") ? { gap: args.gap } : {}),
463
- ...(Object.hasOwn(args, "align") ? { align: args.align } : {}),
464
- ...(Object.hasOwn(args, "padding") ? { padding: args.padding } : {}),
465
- sharedLegend: guides.legend === "shared"
544
+ return composeDerivedFacet(this, args, {
545
+ definition, derived, guides, scalePolicies,
546
+ facet: { values: definition.fields,
547
+ repeat: { target: definition.target, channel: definition.channel, fields: definition.fields } }
466
548
  });
467
- const compositionSpec = {
468
- id: definition.id,
469
- type: "facet",
470
- children: definition.cells.map(cell => cell.id),
471
- columns: preflight.columns,
472
- gap: preflight.gap,
473
- align: preflight.align,
474
- padding: preflight.padding,
475
- facet: {
476
- data: definition.data,
477
- values: definition.fields,
478
- repeat: {
479
- target: definition.target,
480
- channel: definition.channel,
481
- fields: definition.fields
482
- },
483
- scales: scalePolicies.channels,
484
- guides
485
- }
486
- };
487
- return applyCompositionState(
488
- this._withMaterializationConfig(["facets", definition.id], {
489
- headers: createDefaultFacetHeaders()
490
- }),
491
- { children: derived.children, compositionSpec },
492
- compositionSpec.children
493
- );
494
549
  }
495
550
  );
496
551
 
497
- export const facetGrid = /* @__PURE__ */ closedAction(
498
- {
499
- op: "facetGrid",
500
- description: "Repeat one direct-source Cartesian chart across a row and column field grid."
501
- }, FACET_GRID_OPTIONS,
502
- function (args = {}) {
503
- const guides = normalizeGuides(args.guides);
504
- const definition = resolveFacetGridDefinition(this.semanticSpec, args);
505
- if (definition.family !== "cartesian" && guides.axes === "outer") {
506
- throw new Error("Polar and Parallel facets do not support outer axes.");
507
- }
508
- const scalePolicies = normalizeFacetScalePolicies(
509
- this.semanticSpec,
510
- args.scales ?? {}
511
- );
512
- const derived = deriveFacetChildren(this, definition, {
513
- closeInheritedAction: true,
514
- stripTitle: true,
515
- scales: args.scales ?? {}
516
- });
517
- const preflight = resolveFacetLayout({
518
- children: definition.cells.map(cell => ({
519
- ...compositionChildDescriptor(cell.id, derived.children[cell.id]),
520
- value: cell.value,
521
- row: cell.row,
522
- column: cell.column
523
- })),
524
- columns: definition.grid.columns.values.length,
525
- ...(Object.hasOwn(args, "gap") ? { gap: args.gap } : {}),
526
- ...(Object.hasOwn(args, "align") ? { align: args.align } : {}),
527
- ...(Object.hasOwn(args, "padding") ? { padding: args.padding } : {}),
528
- sharedLegend: guides.legend === "shared"
529
- });
530
- const compositionSpec = {
531
- id: definition.id,
532
- type: "facet",
533
- children: definition.cells.map(cell => cell.id),
534
- columns: preflight.columns,
535
- gap: preflight.gap,
536
- align: preflight.align,
537
- padding: preflight.padding,
538
- facet: {
539
- data: definition.data,
540
- values: definition.cells.map(cell => cell.value),
541
- grid: {
542
- ...definition.grid,
543
- cells: definition.cells.map(cell => ({
544
- id: cell.id,
545
- row: cell.row,
546
- column: cell.column,
547
- rowValue: cell.rowValue,
548
- columnValue: cell.columnValue,
549
- empty: cell.empty
550
- }))
551
- },
552
- scales: scalePolicies.channels,
553
- guides
554
- }
555
- };
556
- return applyCompositionState(
557
- this._withMaterializationConfig(["facets", definition.id], {
558
- headers: createDefaultFacetHeaders()
559
- }),
560
- { children: derived.children, compositionSpec },
561
- compositionSpec.children
562
- );
563
- }
564
- );
552
+ export const facetGrid = /* @__PURE__ */ partitionFacetAction("facetGrid",
553
+ "Repeat one direct-source Cartesian chart across a row and column field grid.", resolveFacetGridDefinition);
565
554
 
566
555
  export const editFacetHeaders = /* @__PURE__ */ action(
567
556
  {
@@ -576,78 +565,7 @@ export const editFacetHeaders = /* @__PURE__ */ action(
576
565
  if (!isPlainObject(config?.headers)) {
577
566
  throw new Error(`Facet "${id}" requires header configuration.`);
578
567
  }
579
- validateOptionObject(args, HEADER_OPTIONS, "editFacetHeaders", {
580
- allowEmpty: false,
581
- emptyMessage: "editFacetHeaders requires at least one change."
582
- });
583
- const role = args.role ?? "all";
584
- if (!["all", "row", "column"].includes(role)) {
585
- throw new Error(`Unknown facet header role "${role}".`);
586
- }
587
- if (role === "all" && Object.hasOwn(args, "side")) {
588
- throw new Error("editFacetHeaders side requires an explicit row or column role.");
589
- }
590
- if (role === "row" && this.compositionSpec.facet.grid === undefined) {
591
- throw new Error("editFacetHeaders row role requires a row-column facet grid.");
592
- }
593
- if (Object.hasOwn(args, "side")) {
594
- const sides = role === "row" ? ["left", "right"] : ["top", "bottom"];
595
- if (!sides.includes(args.side)) {
596
- throw new Error(`editFacetHeaders ${role} side must be ${sides.join(" or ")}.`);
597
- }
598
- }
599
- if (Object.hasOwn(args, "align") &&
600
- !["start", "center", "end"].includes(args.align)) {
601
- throw new Error("editFacetHeaders align must be start, center, or end.");
602
- }
603
- if (role === "all" && Object.keys(args).every(key => key === "role")) {
604
- throw new Error("editFacetHeaders requires at least one change.");
605
- }
606
- const current = normalizeFacetHeadersConfig(config.headers);
607
- const owner = role === "all" ? "common" : role;
608
- const patch = { ...current[owner] };
609
- for (const key of HEADER_STYLE_OPTIONS) {
610
- if (Object.hasOwn(args, key)) patch[key] = args[key];
611
- }
612
- if (Object.hasOwn(args, "side")) patch.side = args.side;
613
- if (Object.hasOwn(args, "labelMap")) {
614
- if (args.labelMap === "auto") delete patch.labelMap;
615
- else {
616
- patch.labelMap = normalizeDisplayLabelMap(
617
- args.labelMap,
618
- `editFacetHeaders ${role} labelMap`
619
- );
620
- }
621
- }
622
- const headers = {
623
- ...current,
624
- ...(role === "all" ? {} : { mode: "roles" }),
625
- [owner]: patch
626
- };
627
- for (const headerRole of ["all", "row", "column"]) {
628
- const resolved = resolveFacetHeaderConfig(headers, headerRole);
629
- validatePositiveFinite(resolved.fontSize, `Facet ${headerRole} header fontSize`);
630
- validateNonEmptyString(resolved.fontFamily, `Facet ${headerRole} header fontFamily`);
631
- validateNonEmptyString(resolved.color, `Facet ${headerRole} header color`);
632
- validateNonNegativeFinite(resolved.offset, `Facet ${headerRole} header offset`);
633
- if (!(
634
- (typeof resolved.fontWeight === "string" && resolved.fontWeight.length > 0) ||
635
- Number.isFinite(resolved.fontWeight)
636
- )) {
637
- throw new TypeError(
638
- `Facet ${headerRole} header fontWeight must be a non-empty string or number.`
639
- );
640
- }
641
- if (!["start", "center", "end"].includes(resolved.align)) {
642
- throw new Error(`Facet ${headerRole} header align must be start, center, or end.`);
643
- }
644
- if (resolved.labelMap !== undefined) {
645
- normalizeDisplayLabelMap(
646
- resolved.labelMap,
647
- `Facet ${headerRole} header labelMap`
648
- );
649
- }
650
- }
568
+ const headers = resolveEditedFacetHeaders(config.headers, args, this.compositionSpec.facet.grid);
651
569
  return this
652
570
  ._withMaterializationConfig(["facets", id], { ...config, headers })
653
571
  .materializeComposition();
@@ -702,7 +620,8 @@ export const editFacetGuides = /* @__PURE__ */ action(
702
620
  emptyMessage: "editFacetGuides requires at least one guide policy."
703
621
  });
704
622
  const current = this.compositionSpec.facet;
705
- const guides = normalizeGuides({ ...current.guides, ...args });
623
+ if (Object.hasOwn(args, "legend")) normalizeGuides({ legend: args.legend });
624
+ const guides = normalizeGuides({ ...current.guides, ...args }, true);
706
625
  const family = resolveFacetFamily(this.semanticSpec).family;
707
626
  if (family !== "cartesian" && guides.axes === "outer") {
708
627
  throw new Error("Polar and Parallel facets do not support outer axes.");
@@ -773,6 +692,8 @@ export const editFacetSource = /* @__PURE__ */ closedAction(
773
692
  const current = this.compositionSpec;
774
693
  const facetConfig = this.materializationConfigs.facets?.[current.id];
775
694
  const base = adoptUnitState(args.program, this);
695
+ const authored = isPlainObject(current.facet.guides.legend);
696
+ const guides = authored ? { ...current.facet.guides, legend: false } : current.facet.guides;
776
697
  let revised;
777
698
  const scales = usedFacetScalePolicies(base, current.facet.scales);
778
699
  if (current.facet.grid !== undefined) {
@@ -783,10 +704,11 @@ export const editFacetSource = /* @__PURE__ */ closedAction(
783
704
  columns: current.facet.grid.columns,
784
705
  combinations: current.facet.grid.combinations,
785
706
  gap: current.gap,
707
+ ...(current.spacing === undefined ? {} : { spacing: current.spacing }),
786
708
  align: current.align,
787
709
  padding: current.padding,
788
710
  scales,
789
- guides: current.facet.guides
711
+ guides
790
712
  });
791
713
  } else if (current.facet.repeat !== undefined) {
792
714
  revised = base.repeatCharts({
@@ -794,10 +716,11 @@ export const editFacetSource = /* @__PURE__ */ closedAction(
794
716
  ...current.facet.repeat,
795
717
  columns: current.columns,
796
718
  gap: current.gap,
719
+ ...(current.spacing === undefined ? {} : { spacing: current.spacing }),
797
720
  align: current.align,
798
721
  padding: current.padding,
799
722
  scales,
800
- guides: current.facet.guides
723
+ guides
801
724
  });
802
725
  } else {
803
726
  revised = base.facet({
@@ -807,12 +730,18 @@ export const editFacetSource = /* @__PURE__ */ closedAction(
807
730
  values: current.facet.values,
808
731
  columns: current.columns,
809
732
  gap: current.gap,
733
+ ...(current.spacing === undefined ? {} : { spacing: current.spacing }),
810
734
  align: current.align,
811
735
  padding: current.padding,
812
736
  scales,
813
- guides: current.facet.guides
737
+ guides
814
738
  });
815
739
  }
740
+ if (authored) {
741
+ const spec = revised.compositionSpec;
742
+ revised = revised._withCompositionState({ children: revised.children,
743
+ compositionSpec: { ...spec, facet: { ...spec.facet, guides: current.facet.guides } } });
744
+ }
816
745
  if (facetConfig !== undefined) {
817
746
  revised = revised
818
747
  ._withMaterializationConfig(["facets", current.id], facetConfig)
@@ -1,3 +1,4 @@
1
+ import { synchronizeFacetColorSizeMeaning } from "../primitives/semanticAction.js";
1
2
  import { closedAction } from "../../core/action.js";
2
3
 
3
4
  import { applyFacetGuideComposition } from
@@ -10,6 +11,6 @@ export const composeFacetGuides = /* @__PURE__ */ closedAction(
10
11
  scope: "composition"
11
12
  }, ["layout", "plot"],
12
13
  function ({ layout, plot } = {}) {
13
- return applyFacetGuideComposition(this, { layout, plot });
14
+ return applyFacetGuideComposition(this, { layout, plot, synchronizeColorSizeMeaning: synchronizeFacetColorSizeMeaning });
14
15
  }
15
16
  );
@@ -1,5 +1,5 @@
1
1
  import { resolveOptionalUserId, validateUserId } from "../../core/identifiers.js";
2
- import { findLayer, hasLayer } from "../../selectors/layers.js";
2
+ import { findLayer, hasLayer, resolveConfiguredOwner } from "../../selectors/layers.js";
3
3
 
4
4
  const CATEGORICAL = Object.freeze(["nominal", "ordinal"]);
5
5
 
@@ -62,20 +62,5 @@ export function resolveGradientPlotId(program, requested) {
62
62
  }
63
63
 
64
64
  export function resolveGradientOwner(program, requested, operation) {
65
- const eligible = program.semanticSpec.layers.filter(
66
- layer => program.markConfigs[layer.id]?.gradientPlot?.materialized === true
67
- );
68
- if (requested !== undefined) {
69
- const id = validateUserId(requested, "Gradient-plot owner id");
70
- const layer = findLayer(program, id);
71
- if (layer === undefined || !eligible.includes(layer)) {
72
- throw new Error(`Unknown gradient-plot owner "${id}".`);
73
- }
74
- return layer;
75
- }
76
- const current = findLayer(program, program.context.currentMark);
77
- if (current !== undefined && eligible.includes(current)) return current;
78
- if (eligible.length === 1) return eligible[0];
79
- if (eligible.length === 0) throw new Error(`${operation} requires a gradient plot.`);
80
- throw new Error(`${operation} target is ambiguous; provide target.`);
65
+ return resolveConfiguredOwner(program, requested, { config: "gradientPlot", operation, kind: "gradient", materialized: true }, validateUserId);
81
66
  }
@@ -56,7 +56,7 @@ export function hasInferableLegend(program, layers = program.semanticSpec.layers
56
56
  (layer.mark?.type === "point" &&
57
57
  layer.encoding?.color?.scale !== undefined &&
58
58
  (layer.encoding?.shape?.scale !== undefined ||
59
- ["sequential", "quantize", "quantile", "threshold"].includes(
59
+ ["sequential", "log", "sqrt", "symlog", "quantize", "quantile", "threshold"].includes(
60
60
  findSemanticScale(program, layer.encoding.color.scale)?.type
61
61
  ))) ||
62
62
  (layer.mark?.type === "line" &&
@@ -1,3 +1,4 @@
1
+ import { withGuideLayoutTransaction } from "../../../materialization/guides/layout.js";
1
2
  import { isSourceOwnedText } from "../../../grammar/text.js";
2
3
  import { action } from "../../../core/action.js";
3
4
  import { isPlainObject } from "../../../core/immutable.js";
@@ -15,6 +16,13 @@ const COORDINATE_API_TYPES = new Set([
15
16
  ]);
16
17
 
17
18
  function validateAxisOption(value, channel) {
19
+ if (["x", "y"].includes(channel) && Array.isArray(value)) {
20
+ if (value.length === 0 || value.some(entry => !isPlainObject(entry) || !Object.hasOwn(entry, "id"))) {
21
+ throw new TypeError(`createAxes ${channel} arrays require nonempty named axis descriptors.`);
22
+ }
23
+ for (const entry of value) validateUserId(entry.id, "Axis id");
24
+ return;
25
+ }
18
26
  if (value !== undefined && value !== false && !isPlainObject(value)) {
19
27
  throw new TypeError(`createAxes ${channel} must be false or a plain object.`);
20
28
  }
@@ -287,23 +295,34 @@ const createAxes = /* @__PURE__ */ action(
287
295
  coordinateDescriptor,
288
296
  cartesianLayers
289
297
  );
290
- const x = resolveAxisArgs(coordinate.layers, "x", args.x);
291
- const y = resolveAxisArgs(coordinate.layers, "y", args.y);
298
+ const resolve = (channel, value) => Array.isArray(value)
299
+ ? value.map(entry => resolveAxisArgs(coordinate.layers, channel, entry))
300
+ : resolveAxisArgs(coordinate.layers, channel, value);
301
+ const x = resolve("x", args.x);
302
+ const y = resolve("y", args.y);
292
303
 
293
304
  if (x === undefined && y === undefined) {
294
305
  throw new Error("createAxes requires at least one selected axis.");
295
306
  }
296
307
 
297
- let program = this;
298
-
299
- if (x !== undefined) {
300
- program = program.createXAxis({ ...x, coordinate: coordinate.id });
301
- }
302
- if (y !== undefined) {
303
- program = program.createYAxis({ ...y, coordinate: coordinate.id });
308
+ const apply = source => {
309
+ let program = source;
310
+ for (const [channel, option] of [["x", x], ["y", y]]) {
311
+ if (option === undefined) continue;
312
+ for (const entry of Array.isArray(option) ? option : [option]) {
313
+ program = program[`create${channel.toUpperCase()}Axis`]({ ...entry, coordinate: coordinate.id });
314
+ }
315
+ }
316
+ return program;
317
+ };
318
+ if (Array.isArray(x) || Array.isArray(y)) {
319
+ const ids = [...(Array.isArray(x) ? x : x === undefined ? [] : [x]),
320
+ ...(Array.isArray(y) ? y : y === undefined ? [] : [y])].map((entry, index) => entry.id ?? (index === 0 ? "x" : "y"));
321
+ if (new Set(ids).size !== ids.length) throw new Error("createAxes requires unique axis ids across its descriptors.");
322
+ withGuideLayoutTransaction(this, apply);
323
+ return withGuideLayoutTransaction(this, apply);
304
324
  }
305
-
306
- return program;
325
+ return apply(this);
307
326
  }
308
327
  );
309
328