ggaction 0.0.12 → 0.0.15

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 (378) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +20 -11
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/action-cards.schema.json +1 -1
  6. package/knowledge/extension-authoring.md +2 -1
  7. package/knowledge/intent-taxonomy.json +1 -1
  8. package/knowledge/mcp-resources.json +1 -1
  9. package/knowledge/task-resolver.js +1 -1
  10. package/package.json +10 -3
  11. package/src/actions/basic.js +4 -2
  12. package/src/actions/boxPlots/components.js +14 -4
  13. package/src/actions/boxPlots/edit.js +31 -5
  14. package/src/actions/boxPlots/materialize.js +13 -4
  15. package/src/actions/boxPlots/options.js +48 -16
  16. package/src/actions/canvas/actions.js +3 -1
  17. package/src/actions/canvas/fitting.js +211 -0
  18. package/src/actions/canvas/index.js +2 -0
  19. package/src/actions/categoryOrder/index.js +18 -6
  20. package/src/actions/charts/area.js +51 -0
  21. package/src/actions/charts/bar.js +13 -6
  22. package/src/actions/charts/beeswarm.js +70 -0
  23. package/src/actions/charts/density.js +48 -0
  24. package/src/actions/charts/ecdf.js +186 -0
  25. package/src/actions/charts/endpoints.js +473 -0
  26. package/src/actions/charts/heatmap.js +4 -3
  27. package/src/actions/charts/histogram.js +5 -4
  28. package/src/actions/charts/horizon.js +73 -0
  29. package/src/actions/charts/index.js +37 -0
  30. package/src/actions/charts/interval-regression.js +172 -0
  31. package/src/actions/charts/line.js +8 -6
  32. package/src/actions/charts/parallel.js +8 -2
  33. package/src/actions/charts/pie.js +52 -0
  34. package/src/actions/charts/polar.js +134 -0
  35. package/src/actions/charts/radar.js +305 -0
  36. package/src/actions/charts/radial.js +50 -0
  37. package/src/actions/charts/raincloud.js +422 -0
  38. package/src/actions/charts/rug-strip.js +283 -0
  39. package/src/actions/charts/scatter.js +11 -3
  40. package/src/actions/charts/shared.js +69 -10
  41. package/src/actions/composition/actions.js +129 -3
  42. package/src/actions/coordinates/actions.js +1 -1
  43. package/src/actions/coordinates/edit.js +76 -0
  44. package/src/actions/data/bin.js +29 -0
  45. package/src/actions/data/bin2d.js +57 -185
  46. package/src/actions/data/complete.js +26 -0
  47. package/src/actions/data/computed.js +24 -0
  48. package/src/actions/data/create.js +2 -2
  49. package/src/actions/data/density.js +12 -20
  50. package/src/actions/data/derived.js +111 -15
  51. package/src/actions/data/ecdf.js +38 -0
  52. package/src/actions/data/edit.js +539 -0
  53. package/src/actions/data/filter.js +226 -30
  54. package/src/actions/data/fold.js +24 -0
  55. package/src/actions/data/impute.js +26 -0
  56. package/src/actions/data/index.js +38 -0
  57. package/src/actions/data/interval.js +2 -0
  58. package/src/actions/data/intervalEdit.js +86 -4
  59. package/src/actions/data/normalize.js +27 -0
  60. package/src/actions/data/regression.js +19 -21
  61. package/src/actions/data/revisionBindings.js +145 -0
  62. package/src/actions/data/shared.js +20 -4
  63. package/src/actions/data/stack.js +26 -0
  64. package/src/actions/data/summary.js +26 -0
  65. package/src/actions/data/timeUnit.js +4 -1
  66. package/src/actions/data/window.js +1 -1
  67. package/src/actions/encodings/appearance.js +32 -13
  68. package/src/actions/encodings/barWidth.js +8 -0
  69. package/src/actions/encodings/basic.js +2 -0
  70. package/src/actions/encodings/channels.js +415 -0
  71. package/src/actions/encodings/color/continuous.js +19 -59
  72. package/src/actions/encodings/color/index.js +30 -23
  73. package/src/actions/encodings/color/layout.js +3 -97
  74. package/src/actions/encodings/color/policy.js +5 -8
  75. package/src/actions/encodings/density/resolve.js +37 -5
  76. package/src/actions/encodings/density.js +124 -34
  77. package/src/actions/encodings/histogram.js +3 -2
  78. package/src/actions/encodings/horizon/resolve.js +8 -4
  79. package/src/actions/encodings/horizon.js +18 -8
  80. package/src/actions/encodings/index.js +6 -0
  81. package/src/actions/encodings/offset.js +30 -29
  82. package/src/actions/encodings/parallel.js +2 -8
  83. package/src/actions/encodings/position/apply.js +63 -11
  84. package/src/actions/encodings/position/index.js +52 -6
  85. package/src/actions/encodings/position/policies/arc.js +14 -0
  86. package/src/actions/encodings/position/policies/area.js +4 -42
  87. package/src/actions/encodings/position/policies/bar.js +57 -13
  88. package/src/actions/encodings/position/resolve.js +88 -26
  89. package/src/actions/encodings/ranged.js +123 -97
  90. package/src/actions/encodings/remove.js +44 -34
  91. package/src/actions/encodings/ruleAppearance.js +24 -32
  92. package/src/actions/encodings/seriesLayout.js +105 -0
  93. package/src/actions/encodings/shared.js +107 -37
  94. package/src/actions/encodings/stroke.js +226 -0
  95. package/src/actions/encodings/strokeDash.js +9 -6
  96. package/src/actions/encodings/temporal.js +7 -0
  97. package/src/actions/encodings/text.js +30 -15
  98. package/src/actions/errorBands/create.js +41 -8
  99. package/src/actions/errorBands/edit.js +360 -25
  100. package/src/actions/errorBars/create.js +56 -17
  101. package/src/actions/errorBars/edit.js +372 -21
  102. package/src/actions/facets/actions.js +619 -41
  103. package/src/actions/facets/derive.js +139 -25
  104. package/src/actions/gradientPlots/components.js +43 -13
  105. package/src/actions/gradientPlots/edit.js +14 -4
  106. package/src/actions/gradientPlots/materialize.js +8 -8
  107. package/src/actions/gradientPlots/options.js +10 -2
  108. package/src/actions/guides/applicability.js +28 -19
  109. package/src/actions/guides/axes/axes.js +4 -3
  110. package/src/actions/guides/axes/axis.js +18 -18
  111. package/src/actions/guides/axes/components.js +43 -0
  112. package/src/actions/guides/axes/edit.js +16 -51
  113. package/src/actions/guides/axes/labels.js +184 -28
  114. package/src/actions/guides/axes/lines.js +13 -7
  115. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  116. package/src/actions/guides/axes/parallel/policy.js +137 -0
  117. package/src/actions/guides/axes/parallel/resolve.js +50 -25
  118. package/src/actions/guides/axes/parallel.js +93 -217
  119. package/src/actions/guides/axes/policy.js +21 -67
  120. package/src/actions/guides/axes/tickGroups.js +15 -8
  121. package/src/actions/guides/axes/ticks.js +11 -6
  122. package/src/actions/guides/axes/titles.js +32 -11
  123. package/src/actions/guides/basic.js +2 -0
  124. package/src/actions/guides/facade.js +106 -0
  125. package/src/actions/guides/facadeAxes.js +200 -0
  126. package/src/actions/guides/facadeLegend.js +63 -0
  127. package/src/actions/guides/grids/grid.js +37 -33
  128. package/src/actions/guides/grids/resolve.js +17 -4
  129. package/src/actions/guides/guides.js +54 -31
  130. package/src/actions/guides/legends/blocks.js +426 -0
  131. package/src/actions/guides/legends/categorical/actions.js +309 -279
  132. package/src/actions/guides/legends/categorical/components.js +21 -17
  133. package/src/actions/guides/legends/categorical/index.js +37 -33
  134. package/src/actions/guides/legends/categorical/layout.js +123 -354
  135. package/src/actions/guides/legends/categorical/options.js +32 -16
  136. package/src/actions/guides/legends/categorical/recipes.js +114 -9
  137. package/src/actions/guides/legends/categorical/resolve.js +30 -15
  138. package/src/actions/guides/legends/categorical/symbols.js +87 -33
  139. package/src/actions/guides/legends/continuous/common.js +99 -26
  140. package/src/actions/guides/legends/continuous/gradient.js +120 -89
  141. package/src/actions/guides/legends/continuous/index.js +8 -2
  142. package/src/actions/guides/legends/continuous/interval.js +79 -95
  143. package/src/actions/guides/legends/continuous/opacity.js +113 -89
  144. package/src/actions/guides/legends/continuous/stroke.js +525 -0
  145. package/src/actions/guides/legends/creation.js +40 -0
  146. package/src/actions/guides/legends/edit.js +321 -90
  147. package/src/actions/guides/legends/focused.js +2 -2
  148. package/src/actions/guides/legends/index.js +2 -0
  149. package/src/actions/guides/legends/lane.js +149 -102
  150. package/src/actions/guides/legends/lifecycle.js +143 -0
  151. package/src/actions/guides/legends/remove.js +43 -78
  152. package/src/actions/guides/legends/sampling.js +140 -0
  153. package/src/actions/guides/legends/size.js +213 -116
  154. package/src/actions/guides/legends/strokeWidth.js +121 -73
  155. package/src/actions/guides/legends/target.js +123 -0
  156. package/src/actions/guides/legends/transition.js +192 -0
  157. package/src/actions/guides/polar/axes/facade.js +81 -66
  158. package/src/actions/guides/polar/axes/labels.js +28 -8
  159. package/src/actions/guides/polar/axes/lines.js +12 -5
  160. package/src/actions/guides/polar/axes/shared.js +22 -0
  161. package/src/actions/guides/polar/axes/ticks.js +6 -5
  162. package/src/actions/guides/polar/axes/titles.js +6 -5
  163. package/src/actions/guides/polar/grids.js +8 -8
  164. package/src/actions/guides/polar/resolve.js +12 -8
  165. package/src/actions/guides/reuse.js +44 -0
  166. package/src/actions/guides/tickValues.js +19 -21
  167. package/src/actions/index.js +15 -2
  168. package/src/actions/intervals/resolve.js +35 -15
  169. package/src/actions/marks/arc/actions.js +78 -20
  170. package/src/actions/marks/area/actions.js +51 -12
  171. package/src/actions/marks/area/materialize.js +22 -3
  172. package/src/actions/marks/bar/create.js +11 -2
  173. package/src/actions/marks/bar/edit.js +20 -3
  174. package/src/actions/marks/bar/materialize.js +33 -9
  175. package/src/actions/marks/index.js +14 -0
  176. package/src/actions/marks/line/actions.js +71 -113
  177. package/src/actions/marks/line/materialize.js +57 -56
  178. package/src/actions/marks/point/create.js +8 -2
  179. package/src/actions/marks/point/edit.js +29 -21
  180. package/src/actions/marks/point/index.js +1 -0
  181. package/src/actions/marks/point/jitter.js +3 -0
  182. package/src/actions/marks/point/materialize.js +154 -16
  183. package/src/actions/marks/point/packing.js +68 -0
  184. package/src/actions/marks/rect/actions.js +33 -12
  185. package/src/actions/marks/references.js +658 -0
  186. package/src/actions/marks/remove.js +87 -4
  187. package/src/actions/marks/rule/actions.js +77 -9
  188. package/src/actions/marks/rule/appearance.js +26 -0
  189. package/src/actions/marks/shared.js +13 -13
  190. package/src/actions/marks/text/actions.js +296 -13
  191. package/src/actions/marks/text/index.js +4 -0
  192. package/src/actions/marks/text/layout.js +156 -8
  193. package/src/actions/marks/text/removal.js +173 -0
  194. package/src/actions/marks/text/selection.js +73 -0
  195. package/src/actions/marks/tick/actions.js +22 -5
  196. package/src/actions/primitives/createGraphics.js +3 -1
  197. package/src/actions/primitives/editGraphics.js +26 -0
  198. package/src/actions/primitives/semantic.js +3 -2
  199. package/src/actions/primitives/semanticAction.js +120 -10
  200. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  201. package/src/actions/primitives/semanticValidation/index.js +9 -1
  202. package/src/actions/primitives/semanticValidation/layer.js +23 -1
  203. package/src/actions/primitives/semanticValidation/scale.js +38 -8
  204. package/src/actions/regression/components.js +26 -11
  205. package/src/actions/regression/create.js +11 -3
  206. package/src/actions/regression/edit.js +40 -13
  207. package/src/actions/regression/resolve.js +1 -1
  208. package/src/actions/resources/index.js +7 -0
  209. package/src/actions/resources/remove.js +172 -0
  210. package/src/actions/scales/channels.js +212 -0
  211. package/src/actions/scales/consumers/common.js +21 -54
  212. package/src/actions/scales/consumers/families.js +28 -14
  213. package/src/actions/scales/consumers/index.js +42 -4
  214. package/src/actions/scales/consumers/seriesLayout.js +57 -12
  215. package/src/actions/scales/create.js +58 -26
  216. package/src/actions/scales/definitions.js +109 -39
  217. package/src/actions/scales/edit.js +101 -34
  218. package/src/actions/scales/editPolicy.js +95 -35
  219. package/src/actions/scales/index.js +6 -0
  220. package/src/actions/scales/materialize.js +14 -49
  221. package/src/actions/scales/offset.js +48 -0
  222. package/src/actions/scales/parallel.js +71 -0
  223. package/src/actions/scales/patch.js +1 -1
  224. package/src/actions/scales/preview.js +140 -0
  225. package/src/actions/scales/quantitativeColor.js +9 -5
  226. package/src/actions/selection/actions.js +154 -30
  227. package/src/actions/selection/index.js +3 -0
  228. package/src/actions/theme/actions.js +98 -0
  229. package/src/actions/theme/composition.js +238 -0
  230. package/src/actions/theme/index.js +16 -0
  231. package/src/actions/theme/reconcile.js +1130 -0
  232. package/src/actions/theme/state.js +160 -0
  233. package/src/actions/titles/actions.js +11 -10
  234. package/src/actions/titles/resolve.js +1 -40
  235. package/src/actions/violinPlots/create.js +90 -43
  236. package/src/actions/violinPlots/edit.js +192 -0
  237. package/src/actions/violinPlots/index.js +2 -0
  238. package/src/core/ChartProgram.js +7 -0
  239. package/src/core/action.js +32 -2
  240. package/src/core/compositionState.js +141 -8
  241. package/src/core/programState.js +20 -0
  242. package/src/core/resourceReferences.js +332 -0
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/grammar/aggregate.js +66 -23
  245. package/src/grammar/arcs.js +58 -1
  246. package/src/grammar/areaEndpoints.js +46 -0
  247. package/src/grammar/areaSeries.js +87 -41
  248. package/src/grammar/bars/aggregate.js +37 -13
  249. package/src/grammar/bars/geometry.js +44 -0
  250. package/src/grammar/bars/policy.js +9 -7
  251. package/src/grammar/bin.js +226 -0
  252. package/src/grammar/categoryOrder.js +34 -0
  253. package/src/grammar/complete.js +279 -0
  254. package/src/grammar/computed.js +351 -0
  255. package/src/grammar/coordinates.js +37 -0
  256. package/src/grammar/density.js +239 -37
  257. package/src/grammar/displayLabels.js +57 -0
  258. package/src/grammar/ecdf.js +198 -0
  259. package/src/grammar/facets/dependencies.js +133 -1
  260. package/src/grammar/facets/guides.js +3 -0
  261. package/src/grammar/facets/index.js +123 -52
  262. package/src/grammar/facets/scales.js +15 -18
  263. package/src/grammar/fold.js +123 -0
  264. package/src/grammar/horizon.js +6 -4
  265. package/src/grammar/impute.js +340 -0
  266. package/src/grammar/interval.js +37 -20
  267. package/src/grammar/lineSeries.js +20 -63
  268. package/src/grammar/markFilter.js +22 -4
  269. package/src/grammar/markLabelSelection.js +57 -0
  270. package/src/grammar/markLabels.js +110 -0
  271. package/src/grammar/normalize.js +366 -0
  272. package/src/grammar/parallelAxisTitles.js +16 -0
  273. package/src/grammar/pathSeries.js +109 -0
  274. package/src/grammar/pointPacking.js +183 -0
  275. package/src/grammar/pointShapes.js +13 -4
  276. package/src/grammar/polar.js +90 -8
  277. package/src/grammar/positionDatum.js +12 -0
  278. package/src/grammar/rects.js +9 -1
  279. package/src/grammar/regression/derive.js +4 -0
  280. package/src/grammar/regression/index.js +8 -2
  281. package/src/grammar/regression/models.js +22 -7
  282. package/src/grammar/regression/parameters.js +63 -13
  283. package/src/grammar/rotation.js +29 -0
  284. package/src/grammar/roundedRect.js +180 -0
  285. package/src/grammar/rules.js +8 -21
  286. package/src/grammar/scales/color.js +23 -1
  287. package/src/grammar/scales/colorConsumers.js +57 -0
  288. package/src/grammar/scales/continuous.js +2 -2
  289. package/src/grammar/scales/definition.js +42 -5
  290. package/src/grammar/scales/discretized.js +11 -5
  291. package/src/grammar/scales/fields.js +38 -8
  292. package/src/grammar/scales/index.js +1 -0
  293. package/src/grammar/scales/mapping.js +2 -0
  294. package/src/grammar/scales/ordinal.js +12 -3
  295. package/src/grammar/scales/policies.js +2 -2
  296. package/src/grammar/scales/radial.js +55 -0
  297. package/src/grammar/scales/size.js +456 -0
  298. package/src/grammar/scales/types.js +1 -0
  299. package/src/grammar/schemas/concreteGraphic.js +14 -2
  300. package/src/grammar/schemas/graphic.js +15 -0
  301. package/src/grammar/schemas/graphicBounds.js +179 -30
  302. package/src/grammar/schemas/semanticPath.js +23 -4
  303. package/src/grammar/stack.js +170 -0
  304. package/src/grammar/statisticalReference.js +103 -0
  305. package/src/grammar/statistics/confidenceInterval.js +101 -0
  306. package/src/grammar/strokeStyle.js +77 -0
  307. package/src/grammar/summary.js +245 -0
  308. package/src/grammar/text.js +22 -16
  309. package/src/grammar/timeUnit.js +234 -13
  310. package/src/grammar/transformTopology.js +10 -0
  311. package/src/grammar/transforms.js +418 -16
  312. package/src/grammar/valueFormat.js +112 -0
  313. package/src/grammar/weightedStatistics.js +374 -0
  314. package/src/grammar/window.js +232 -21
  315. package/src/layout/aspect.js +80 -0
  316. package/src/layout/facets.js +100 -12
  317. package/src/layout/guideCollisions.js +12 -0
  318. package/src/layout/labels.js +343 -0
  319. package/src/layout/legend.js +2 -10
  320. package/src/layout/legendItems.js +102 -0
  321. package/src/layout/legendLane.js +100 -11
  322. package/src/materialization/bars/aggregate.js +56 -147
  323. package/src/materialization/bars/histogram.js +104 -20
  324. package/src/materialization/bars/ranged.js +19 -3
  325. package/src/materialization/bars/resolve.js +7 -4
  326. package/src/materialization/categorySlotOffset.js +40 -0
  327. package/src/materialization/composition.js +3 -1
  328. package/src/materialization/compositionSnapshot.js +1 -1
  329. package/src/materialization/coordinateBounds.js +204 -0
  330. package/src/materialization/dependencies.js +93 -16
  331. package/src/materialization/encodings.js +1 -1
  332. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  333. package/src/materialization/facetGuides/placement.js +8 -2
  334. package/src/materialization/facetGuides/preparation.js +12 -7
  335. package/src/materialization/facetHeaders.js +346 -0
  336. package/src/materialization/facets.js +23 -66
  337. package/src/materialization/guides/layout.js +57 -0
  338. package/src/materialization/guides/resources.js +28 -2
  339. package/src/materialization/legends.js +11 -2
  340. package/src/materialization/marks/capabilities.js +20 -5
  341. package/src/materialization/marks/grouping.js +52 -0
  342. package/src/materialization/marks/index.js +68 -27
  343. package/src/materialization/marks/policies.js +4 -1
  344. package/src/materialization/rect.js +55 -42
  345. package/src/materialization/rectConfig.js +5 -1
  346. package/src/materialization/rowEncoding.js +13 -13
  347. package/src/materialization/scaleGuideDependencies.js +8 -5
  348. package/src/materialization/scales/map.js +6 -3
  349. package/src/materialization/scales/policies/arc.js +42 -0
  350. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  351. package/src/materialization/scales/policies/offset.js +50 -8
  352. package/src/materialization/scales/policies/series.js +5 -1
  353. package/src/materialization/scales/resolve.js +65 -17
  354. package/src/materialization/selection/compatibility.js +11 -0
  355. package/src/materialization/selection/items/arc.js +28 -4
  356. package/src/materialization/selection/items/bar.js +42 -71
  357. package/src/materialization/selection/items/common.js +27 -6
  358. package/src/materialization/selection/items/path.js +3 -13
  359. package/src/materialization/selection/items/rect.js +35 -8
  360. package/src/materialization/selection/path.js +1 -1
  361. package/src/materialization/selection/point.js +3 -1
  362. package/src/materialization/text.js +324 -14
  363. package/src/mcp/adapter.js +2 -2
  364. package/src/renderers/canvas/circle.js +2 -3
  365. package/src/renderers/canvas/line.js +2 -3
  366. package/src/renderers/canvas/native.js +4 -4
  367. package/src/renderers/canvas/path.js +2 -3
  368. package/src/renderers/canvas/rect.js +2 -3
  369. package/src/renderers/canvas/stroke.js +11 -0
  370. package/src/renderers/svg.js +19 -0
  371. package/src/selectors/datasets.js +44 -0
  372. package/src/selectors/layers.js +7 -2
  373. package/src/selectors/scales.js +6 -0
  374. package/src/theme/defaults.js +140 -0
  375. package/types/basic.d.ts +17 -2
  376. package/types/index.d.ts +211 -0
  377. package/types/program.d.ts +2182 -204
  378. package/src/materialization/bars/grouped.js +0 -144
@@ -0,0 +1,160 @@
1
+ import {
2
+ cloneAndFreeze,
3
+ freezeOwned,
4
+ isPlainObject
5
+ } from "../../core/immutable.js";
6
+ import {
7
+ normalizeThemeDefinition,
8
+ resolveThemeTokens
9
+ } from "../../theme/defaults.js";
10
+
11
+ export const LOCAL_THEME_OWNER = "local";
12
+
13
+ function ownArray(value) {
14
+ return freezeOwned([...(value ?? [])]);
15
+ }
16
+
17
+ function requireThemeScope(value) {
18
+ if (!["self", "descendants"].includes(value)) {
19
+ throw new Error(`Unsupported theme scope "${value}".`);
20
+ }
21
+ return value;
22
+ }
23
+
24
+ function requireThemeOwner(value) {
25
+ if (typeof value !== "string" || value.length === 0) {
26
+ throw new TypeError("Theme frame owner must be a non-empty string.");
27
+ }
28
+ return value;
29
+ }
30
+
31
+ export function compositionThemeOwner(id, scope = "descendants") {
32
+ if (typeof id !== "string" || id.length === 0) {
33
+ throw new TypeError("Composition theme owner requires a non-empty id.");
34
+ }
35
+ requireThemeScope(scope);
36
+ return scope === "self" ? `composition:${id}:self` : `composition:${id}`;
37
+ }
38
+
39
+ export function createThemeFrame({
40
+ owner = LOCAL_THEME_OWNER,
41
+ scope = "self",
42
+ definition
43
+ }) {
44
+ const normalized = normalizeThemeDefinition(definition);
45
+ return freezeOwned({
46
+ owner: requireThemeOwner(owner),
47
+ scope: requireThemeScope(scope),
48
+ name: normalized.name,
49
+ tokens: normalized.tokens
50
+ });
51
+ }
52
+
53
+ export function upsertThemeFrame(frames = [], frame) {
54
+ if (!Array.isArray(frames)) {
55
+ throw new TypeError("Theme frames must be an array.");
56
+ }
57
+ if (!isPlainObject(frame)) {
58
+ throw new TypeError("Theme frame must be a plain object.");
59
+ }
60
+ return freezeOwned([
61
+ ...frames.filter(current => current.owner !== frame.owner),
62
+ frame
63
+ ]);
64
+ }
65
+
66
+ export function removeThemeFrameOwner(frames = [], owner) {
67
+ if (!Array.isArray(frames)) {
68
+ throw new TypeError("Theme frames must be an array.");
69
+ }
70
+ requireThemeOwner(owner);
71
+ const next = frames.filter(frame => frame.owner !== owner);
72
+ return freezeOwned({
73
+ frames: freezeOwned(next),
74
+ removed: next.length !== frames.length
75
+ });
76
+ }
77
+
78
+ export function moveLocalThemeScope(localOrder = [], scope) {
79
+ if (!Array.isArray(localOrder)) {
80
+ throw new TypeError("Theme localOrder must be an array.");
81
+ }
82
+ requireThemeScope(scope);
83
+ return freezeOwned([
84
+ ...localOrder.filter(current => current !== scope),
85
+ scope
86
+ ]);
87
+ }
88
+
89
+ function ownCanonicalThemeState(state, composition) {
90
+ const frames = ownArray(state.frames);
91
+ const localOrder = ownArray(state.localOrder);
92
+ const overrides = ownArray(state.overrides);
93
+ const result = { frames, localOrder, overrides };
94
+ if (composition || state.descendantFrames !== undefined) {
95
+ result.descendantFrames = ownArray(state.descendantFrames);
96
+ }
97
+ return cloneAndFreeze(result);
98
+ }
99
+
100
+ export function normalizeThemeState(state, { composition = false } = {}) {
101
+ if (state === undefined) {
102
+ return freezeOwned({
103
+ frames: freezeOwned([]),
104
+ ...(composition ? { descendantFrames: freezeOwned([]) } : {}),
105
+ localOrder: freezeOwned([]),
106
+ overrides: freezeOwned([])
107
+ });
108
+ }
109
+ if (!isPlainObject(state)) {
110
+ throw new TypeError("Program theme state must be a plain object.");
111
+ }
112
+ if (Array.isArray(state.frames)) {
113
+ return ownCanonicalThemeState(state, composition);
114
+ }
115
+
116
+ const name = state.name ?? "light";
117
+ const tokens = state.tokens ?? {};
118
+ const scope = state.scope ?? "self";
119
+ const origin = state.origin?.ownerCompositionId;
120
+ const owner = origin === undefined
121
+ ? LOCAL_THEME_OWNER
122
+ : compositionThemeOwner(origin, "descendants");
123
+ const frame = createThemeFrame({
124
+ owner,
125
+ scope,
126
+ definition: { base: name, tokens }
127
+ });
128
+ return freezeOwned({
129
+ frames: freezeOwned([frame]),
130
+ ...(composition && scope === "descendants"
131
+ ? { descendantFrames: freezeOwned([frame]) }
132
+ : composition
133
+ ? { descendantFrames: freezeOwned([]) }
134
+ : {}),
135
+ localOrder: origin === undefined ? freezeOwned([scope]) : freezeOwned([]),
136
+ overrides: cloneAndFreeze(state.overrides ?? [])
137
+ });
138
+ }
139
+
140
+ export function resolveEffectiveThemeFrame(state) {
141
+ const frames = state?.frames;
142
+ return Array.isArray(frames) ? frames.at(-1) : undefined;
143
+ }
144
+
145
+ export function resolveEffectiveThemeTokens(state) {
146
+ const frame = resolveEffectiveThemeFrame(state);
147
+ return frame === undefined
148
+ ? resolveThemeTokens("light")
149
+ : resolveThemeTokens({ base: frame.name, tokens: frame.tokens });
150
+ }
151
+
152
+ export function setThemeStateOverrides(state, overrides) {
153
+ if (!Array.isArray(overrides)) {
154
+ throw new TypeError("Theme overrides must be an array.");
155
+ }
156
+ return freezeOwned({
157
+ ...state,
158
+ overrides: cloneAndFreeze([...overrides].sort())
159
+ });
160
+ }
@@ -1,3 +1,4 @@
1
+ import { withGuideLayoutValidation } from "../../materialization/guides/layout.js";
1
2
  import { action } from "../../core/action.js";
2
3
  import { noOptions } from "../../core/validation.js";
3
4
  import {
@@ -92,7 +93,7 @@ function titleTextAction(kind, create) {
92
93
  return action({
93
94
  op,
94
95
  description: `${create ? "Create" : "Rematerialize"} chart ${kind} text.`
95
- }, function (args = {}) {
96
+ }, withGuideLayoutValidation(function (args = {}) {
96
97
  noOptions(args, op);
97
98
  const config = requireTitleConfig(this);
98
99
  if (create && this.graphicSpec.objects[id] !== undefined) {
@@ -111,7 +112,7 @@ function titleTextAction(kind, create) {
111
112
  ...(component.lines.length > 1 ? { length: component.lines.length } : {}),
112
113
  ...resolveCanvasGraphicPlacement(this)
113
114
  })[`edit${suffix}`]();
114
- });
115
+ }));
115
116
  }
116
117
 
117
118
  export const editTitleText = titleTextAction("title", false);
@@ -121,7 +122,7 @@ export const createSubtitleText = titleTextAction("subtitle", true);
121
122
 
122
123
  export const rematerializeTitle = action(
123
124
  { op: "rematerializeTitle", description: "Rematerialize chart title graphics." },
124
- function (args = {}) {
125
+ withGuideLayoutValidation(function (args = {}) {
125
126
  noOptions(args, "rematerializeTitle");
126
127
  requireTitleConfig(this);
127
128
  if (this.graphicSpec.objects.chartTitle?.type !== "text") {
@@ -138,7 +139,7 @@ export const rematerializeTitle = action(
138
139
  next = next.editGraphics({ target: "chartSubtitle", remove: true });
139
140
  }
140
141
  return next;
141
- }
142
+ })
142
143
  );
143
144
 
144
145
  export const createTitle = action(
@@ -147,7 +148,7 @@ export const createTitle = action(
147
148
  description: "Create a chart title and optional subtitle.",
148
149
  scope: "any"
149
150
  },
150
- function (args = {}) {
151
+ withGuideLayoutValidation(function (args = {}) {
151
152
  assertTitleScope(this, "createTitle");
152
153
  const options = normalizeTitleOptions(args);
153
154
  if (Object.keys(this.semanticSpec.title).length > 0) {
@@ -172,7 +173,7 @@ export const createTitle = action(
172
173
  next = next.createTitleText();
173
174
  if (subtitle !== undefined) next = next.createSubtitleText();
174
175
  return next;
175
- }
176
+ })
176
177
  );
177
178
 
178
179
  export const editTitle = action(
@@ -181,7 +182,7 @@ export const editTitle = action(
181
182
  description: "Edit one stable chart title resource.",
182
183
  scope: "any"
183
184
  },
184
- function (args = {}) {
185
+ withGuideLayoutValidation(function (args = {}) {
185
186
  assertTitleScope(this, "editTitle");
186
187
  if (this.semanticSpec.title.text === undefined) {
187
188
  throw new Error("editTitle requires an existing chart title.");
@@ -210,7 +211,7 @@ export const editTitle = action(
210
211
  }
211
212
  resolveTitleLayout(next, normalized.config);
212
213
  return next.rematerializeTitle();
213
- }
214
+ })
214
215
  );
215
216
 
216
217
  export const removeTitle = action(
@@ -219,7 +220,7 @@ export const removeTitle = action(
219
220
  description: "Remove the complete chart title resource.",
220
221
  scope: "any"
221
222
  },
222
- function (args = {}) {
223
+ withGuideLayoutValidation(function (args = {}) {
223
224
  assertTitleScope(this, "removeTitle");
224
225
  noOptions(args, "removeTitle");
225
226
  if (
@@ -246,5 +247,5 @@ export const removeTitle = action(
246
247
  return next.compositionSpec?.type === "facet"
247
248
  ? next.materializeComposition()
248
249
  : next;
249
- }
250
+ })
250
251
  );
@@ -7,12 +7,10 @@ import {
7
7
  validatePositiveFinite
8
8
  } from "../../core/validation.js";
9
9
  import { resolveGraphicBounds } from "../../layout/canvas.js";
10
- import { resolveConcreteGraphicBounds } from "../../grammar/schemas/graphicBounds.js";
11
10
  import {
12
11
  alignedTextAnchor,
13
12
  alignedTitleAnchor,
14
13
  buildTitleReadingBlock,
15
- layoutBoundsIntersect,
16
14
  resolveTitleComponentBounds,
17
15
  unionTitleBounds
18
16
  } from "../../layout/title.js";
@@ -176,35 +174,7 @@ export function requireTitleConfig(program) {
176
174
  return program.titleConfig;
177
175
  }
178
176
 
179
- function reservedGraphicIds(program, position) {
180
- const ids = [];
181
- const axisChannel = ["top", "bottom"].includes(position) ? "x" : "y";
182
- const axis = program.guideConfigs.axis?.[axisChannel];
183
- if (axis && Object.values(axis).some(config => config?.position === position)) {
184
- const prefix = axisChannel === "x" ? "xAxis" : "yAxis";
185
- ids.push(`${prefix}Line`, `${prefix}Ticks`, `${prefix}Labels`, `${prefix}Title`);
186
- }
187
- const prefixes = {
188
- series: "seriesLegend",
189
- color: "colorLegend",
190
- gradient: "colorGradient",
191
- opacity: "opacityLegend"
192
- };
193
- for (const [kind, config] of Object.entries(program.guideConfigs.legend ?? {})) {
194
- if (config?.position !== position || prefixes[kind] === undefined) continue;
195
- ids.push(...Object.keys(program.graphicSpec.objects).filter(
196
- id => id.startsWith(prefixes[kind])
197
- ));
198
- if (kind === "series" && program.guideConfigs.legend?.size !== undefined) {
199
- ids.push(...Object.keys(program.graphicSpec.objects).filter(
200
- id => id.startsWith("sizeLegend")
201
- ));
202
- }
203
- }
204
- return [...new Set(ids)];
205
- }
206
-
207
- function validateLayout(program, position, titleBounds, plot, canvas) {
177
+ function validateLayout(position, titleBounds, plot, canvas) {
208
178
  if (
209
179
  titleBounds.left < 0 || titleBounds.top < 0 ||
210
180
  titleBounds.right > canvas.width || titleBounds.bottom > canvas.height
@@ -220,14 +190,6 @@ function validateLayout(program, position, titleBounds, plot, canvas) {
220
190
  if (!outsidePlot) {
221
191
  throw new Error(`Chart title requires more ${position}-margin space.`);
222
192
  }
223
- for (const id of reservedGraphicIds(program, position)) {
224
- const bounds = program.graphicSpec.objects[id] === undefined
225
- ? undefined
226
- : resolveConcreteGraphicBounds(program.graphicSpec, id);
227
- if (bounds !== undefined && layoutBoundsIntersect(titleBounds, bounds)) {
228
- throw new Error(`Chart title and ${position} guides require more margin space.`);
229
- }
230
- }
231
193
  }
232
194
 
233
195
  export function resolveTitleLayout(program, config) {
@@ -293,7 +255,6 @@ export function resolveTitleLayout(program, config) {
293
255
  : [resolveTitleComponentBounds(subtitle, config.subtitleStyle)])
294
256
  ]);
295
257
  validateLayout(
296
- program,
297
258
  config.position,
298
259
  titleBounds,
299
260
  plot,
@@ -1,6 +1,8 @@
1
1
  import { action } from "../../core/action.js";
2
2
  import { validateOptionObject } from "../../core/validation.js";
3
3
  import { findDataset } from "../../selectors/datasets.js";
4
+ import { findLayer } from "../../selectors/layers.js";
5
+ import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
4
6
  import {
5
7
  applyFacadeGuides,
6
8
  normalizeAppearance,
@@ -19,16 +21,16 @@ const OPTIONS = Object.freeze([
19
21
  ]);
20
22
  const POSITION_OPTIONS = Object.freeze(["field", "fieldType", "scale"]);
21
23
  const DENSITY_OPTIONS = Object.freeze([
22
- "bandwidth", "extent", "steps", "kernel", "normalization", "width"
24
+ "bandwidth", "extent", "steps", "kernel", "normalization", "weight", "width", "side"
23
25
  ]);
24
26
  const WIDTH_OPTIONS = Object.freeze(["band", "resolve"]);
25
27
  const SPLIT_OPTIONS = Object.freeze(["field", "domain"]);
26
28
  const AREA_OPTIONS = Object.freeze([
27
- "fill", "opacity", "stroke", "strokeWidth", "curve"
29
+ "fill", "opacity", "stroke", "strokeWidth", "curve",
30
+ ...STROKE_STYLE_PROPERTIES
28
31
  ]);
29
32
  const CATEGORICAL_TYPES = Object.freeze(["nominal", "ordinal"]);
30
33
  const OPERATION = "createViolinPlot";
31
- const ROLE_ERROR = `${OPERATION} requires one categorical axis and one quantitative axis.`;
32
34
 
33
35
  function inferFieldType(dataset, encoding, label) {
34
36
  if (encoding.fieldType !== undefined) return encoding.fieldType;
@@ -44,7 +46,7 @@ function inferFieldType(dataset, encoding, label) {
44
46
  return values.every(Number.isFinite) ? "quantitative" : "nominal";
45
47
  }
46
48
 
47
- function normalizePosition(dataset, value, label) {
49
+ export function normalizeViolinPosition(dataset, value, label) {
48
50
  const encoding = normalizeFieldEncoding(value, label);
49
51
  validateOptionObject(encoding, POSITION_OPTIONS, label);
50
52
  return {
@@ -53,6 +55,54 @@ function normalizePosition(dataset, value, label) {
53
55
  };
54
56
  }
55
57
 
58
+ export function resolveViolinRoles(dataset, xValue, yValue, operation) {
59
+ const x = normalizeViolinPosition(dataset, xValue, `${operation} x`);
60
+ const y = normalizeViolinPosition(dataset, yValue, `${operation} y`);
61
+ const xCategorical = CATEGORICAL_TYPES.includes(x.fieldType);
62
+ const yCategorical = CATEGORICAL_TYPES.includes(y.fieldType);
63
+ const roleError = `${operation} requires one categorical axis and one quantitative axis.`;
64
+ if (xCategorical === yCategorical || (
65
+ x.fieldType !== "quantitative" && y.fieldType !== "quantitative"
66
+ )) {
67
+ throw new Error(roleError);
68
+ }
69
+ const category = xCategorical ? x : y;
70
+ const value = xCategorical ? y : x;
71
+ if (value.fieldType !== "quantitative") throw new Error(roleError);
72
+ return {
73
+ x,
74
+ y,
75
+ category,
76
+ value,
77
+ orientation: xCategorical ? "vertical" : "horizontal",
78
+ densityChannel: xCategorical ? "x" : "y"
79
+ };
80
+ }
81
+
82
+ export function resolveViolinSplit(value, category, operation, { removable = false } = {}) {
83
+ if (removable && value === false) return undefined;
84
+ if (value === undefined) return undefined;
85
+ validateOptionObject(value, SPLIT_OPTIONS, `${operation} split`);
86
+ if (typeof value.field !== "string" || value.field.length === 0) {
87
+ throw new TypeError(`${operation} split.field must be a non-empty string.`);
88
+ }
89
+ const split = { ...value };
90
+ if (split.field === category.field) {
91
+ throw new Error(`${operation} split field must differ from its category field.`);
92
+ }
93
+ return split;
94
+ }
95
+
96
+ export function resolveViolinDensity(value, current = {}, operation = OPERATION) {
97
+ if (value !== undefined) {
98
+ validateOptionObject(value, DENSITY_OPTIONS, `${operation} density`);
99
+ if (value.width !== undefined) {
100
+ validateOptionObject(value.width, WIDTH_OPTIONS, `${operation} density.width`);
101
+ }
102
+ }
103
+ return { ...current, ...(value ?? {}) };
104
+ }
105
+
56
106
  function axisGuideOptions(value, field) {
57
107
  if (value === false) return false;
58
108
  return {
@@ -95,31 +145,9 @@ export const createViolinPlot = action(
95
145
  });
96
146
  const data = resolveFacadeData(this, args.data, OPERATION);
97
147
  const dataset = findDataset(this, data);
98
- const x = normalizePosition(dataset, args.x, `${OPERATION} x`);
99
- const y = normalizePosition(dataset, args.y, `${OPERATION} y`);
100
- const xCategorical = CATEGORICAL_TYPES.includes(x.fieldType);
101
- const yCategorical = CATEGORICAL_TYPES.includes(y.fieldType);
102
- if (xCategorical === yCategorical || (
103
- x.fieldType !== "quantitative" && y.fieldType !== "quantitative"
104
- )) {
105
- throw new Error(ROLE_ERROR);
106
- }
107
- const category = xCategorical ? x : y;
108
- const value = xCategorical ? y : x;
109
- if (value.fieldType !== "quantitative") {
110
- throw new Error(ROLE_ERROR);
111
- }
112
- let split = args.split;
113
- if (split !== undefined) {
114
- validateOptionObject(split, SPLIT_OPTIONS, `${OPERATION} split`);
115
- if (typeof split.field !== "string" || split.field.length === 0) {
116
- throw new TypeError(`${OPERATION} split.field must be a non-empty string.`);
117
- }
118
- split = { ...split };
119
- }
120
- if (split?.field === category.field) {
121
- throw new Error(`${OPERATION} split field must differ from its category field.`);
122
- }
148
+ const roles = resolveViolinRoles(dataset, args.x, args.y, OPERATION);
149
+ const { x, y, category, value } = roles;
150
+ const split = resolveViolinSplit(args.split, category, OPERATION);
123
151
  const color = normalizeEncoding(args.color, `${OPERATION} color`);
124
152
  if (
125
153
  color !== undefined &&
@@ -129,18 +157,8 @@ export const createViolinPlot = action(
129
157
  `${OPERATION} color must encode its category or split field.`
130
158
  );
131
159
  }
132
- const density = args.density === undefined ? {} : args.density;
133
- if (args.density !== undefined) {
134
- validateOptionObject(density, DENSITY_OPTIONS, `${OPERATION} density`);
135
- if (density.width !== undefined) {
136
- validateOptionObject(
137
- density.width,
138
- WIDTH_OPTIONS,
139
- `${OPERATION} density.width`
140
- );
141
- }
142
- }
143
- const { width: densityWidth, ...densityOptions } = density;
160
+ const density = resolveViolinDensity(args.density);
161
+ const { width: densityWidth, side: densitySide, ...densityOptions } = density;
144
162
  const area = normalizeAppearance(
145
163
  args.area,
146
164
  AREA_OPTIONS,
@@ -174,18 +192,47 @@ export const createViolinPlot = action(
174
192
  source: data,
175
193
  field: value.field,
176
194
  groupBy: category.field,
177
- densityChannel: xCategorical ? "x" : "y",
195
+ densityChannel: roles.densityChannel,
178
196
  ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
179
197
  ...(value.scale === undefined ? {} : { valueScale: value.scale }),
180
198
  ...densityOptions,
181
199
  placement: {
182
200
  type: "category",
201
+ ...(densitySide === undefined ? {} : { side: densitySide }),
183
202
  ...(densityWidth === undefined ? {} : { width: densityWidth }),
184
203
  ...(split === undefined ? {} : { split }),
185
204
  ...(category.scale === undefined ? {} : { scale: category.scale })
186
205
  }
187
206
  });
188
207
  if (color !== undefined) next = next.encodeColor(targetArgs(color, id));
189
- return applyFacadeGuides(next, guides);
208
+ next = applyFacadeGuides(next, guides, id, args.guides ?? {});
209
+ const transform = findDataset(next, findLayer(next, id).data).transform[0];
210
+ return next._withMarkConfig(id, {
211
+ ...next.markConfigs[id],
212
+ violinPlot: {
213
+ materialized: true,
214
+ source: data,
215
+ orientation: roles.orientation,
216
+ category: category.field,
217
+ categoryType: category.fieldType,
218
+ value: value.field,
219
+ split: transform.placement.split,
220
+ density: {
221
+ bandwidth: transform.bandwidth,
222
+ extent: transform.extent,
223
+ steps: transform.steps,
224
+ kernel: transform.kernel,
225
+ normalization: transform.normalization,
226
+ ...(transform.weight === undefined ? {} : { weight: transform.weight }),
227
+ width: transform.placement.width,
228
+ side: transform.placement.side
229
+ },
230
+ colorRole: color === undefined
231
+ ? undefined
232
+ : color.field === category.field
233
+ ? "category"
234
+ : color.field === split?.field ? "split" : undefined
235
+ }
236
+ });
190
237
  }
191
238
  );