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,1130 @@
1
+ import { themeTokens } from "../../theme/defaults.js";
2
+ import {
3
+ normalizeThemeState,
4
+ resolveEffectiveThemeTokens,
5
+ setThemeStateOverrides
6
+ } from "./state.js";
7
+ import { findLayer } from "../../selectors/layers.js";
8
+
9
+ let rematerializeThemeHighlights = program => program;
10
+
11
+ export function registerThemeHighlightReconciler(reconciler) {
12
+ if (typeof reconciler !== "function") {
13
+ throw new TypeError("Theme highlight reconciler must be a function.");
14
+ }
15
+ rematerializeThemeHighlights = reconciler;
16
+ }
17
+
18
+ const THEME_STYLE_PROPERTIES = Object.freeze([
19
+ "background",
20
+ "fill",
21
+ "stroke",
22
+ "fontFamily"
23
+ ]);
24
+ const COLOR_TOKENS = Object.freeze([
25
+ "mark",
26
+ "text",
27
+ "strongText",
28
+ "mutedText",
29
+ "axis",
30
+ "axisTitle",
31
+ "grid",
32
+ "border",
33
+ "sizeSymbol",
34
+ "regressionBand",
35
+ "boxLine",
36
+ "boxMedian",
37
+ "referenceLine",
38
+ "referenceBand",
39
+ "gradientCenter"
40
+ ]);
41
+
42
+ function tokenFor(value, sourceTokens, targetTokens) {
43
+ const matches = COLOR_TOKENS.filter(token =>
44
+ value === sourceTokens[token] || value === themeTokens("light")[token]
45
+ );
46
+ if (matches.length === 0) return undefined;
47
+ const targets = new Set(matches.map(token => targetTokens[token]));
48
+ if (targets.size !== 1) return undefined;
49
+ for (const token of matches) {
50
+ if (targetTokens[token] === targets.values().next().value) return token;
51
+ }
52
+ return undefined;
53
+ }
54
+
55
+ function roleValues(role, sourceTokens, legacy = []) {
56
+ return new Set([
57
+ sourceTokens[role],
58
+ themeTokens("light")[role],
59
+ ...legacy.map(token => themeTokens("light")[token] ?? token)
60
+ ]);
61
+ }
62
+
63
+ function mapRoleValue(value, role, sourceTokens, targetTokens, legacy) {
64
+ if (Array.isArray(value)) {
65
+ let changed = false;
66
+ const next = value.map(item => {
67
+ const mapped = mapRoleValue(
68
+ item,
69
+ role,
70
+ sourceTokens,
71
+ targetTokens,
72
+ legacy
73
+ );
74
+ changed ||= mapped.changed;
75
+ return mapped.value;
76
+ });
77
+ return { changed, value: changed ? next : value };
78
+ }
79
+ if (!roleValues(role, sourceTokens, legacy).has(value)) {
80
+ return { changed: false, value };
81
+ }
82
+ return { changed: value !== targetTokens[role], value: targetTokens[role] };
83
+ }
84
+
85
+ function mapColorValue(value, sourceTokens, targetTokens) {
86
+ if (Array.isArray(value)) {
87
+ let changed = false;
88
+ const next = value.map(item => {
89
+ const mapped = mapColorValue(item, sourceTokens, targetTokens);
90
+ changed ||= mapped.changed;
91
+ return mapped.value;
92
+ });
93
+ return { changed, value: changed ? next : value };
94
+ }
95
+ const token = tokenFor(value, sourceTokens, targetTokens);
96
+ if (token === undefined) return { changed: false, value };
97
+ const next = targetTokens[token];
98
+ return { changed: next !== value, value: next };
99
+ }
100
+
101
+ function isFieldDrivenMark(program, id, property) {
102
+ if (!(["fill", "stroke"].includes(property))) return false;
103
+ const layer = findLayer(program, id);
104
+ return property === "stroke"
105
+ ? layer?.encoding?.stroke?.field !== undefined ||
106
+ (layer?.mark?.type === "line" && layer.encoding?.color?.field !== undefined)
107
+ : layer?.encoding?.color?.field !== undefined;
108
+ }
109
+
110
+ function isRegressionBand(program, id) {
111
+ return Object.values(program.markConfigs).some(
112
+ config => config.regression?.bandId === id
113
+ );
114
+ }
115
+
116
+ function referenceRole(program, id, property) {
117
+ let role;
118
+ for (const node of program.trace.children) {
119
+ const created = /^create(Point|Line|Area|Arc|Bar|Rect|Rule|Tick|Text)Mark$/u
120
+ .exec(node.op);
121
+ const createdId = created === null
122
+ ? undefined
123
+ : node.args.id ?? created[1].toLowerCase();
124
+ if (createdId === id ||
125
+ (node.op === "removeMark" && node.args.target === id)) {
126
+ role = undefined;
127
+ }
128
+ if (node.op === "createReferenceLine" &&
129
+ (node.args.id ?? "referenceLine") === id && property === "stroke") {
130
+ role = "referenceLine";
131
+ }
132
+ if (node.op === "createReferenceBand" &&
133
+ (node.args.id ?? "referenceBand") === id && property === "fill") {
134
+ role = "referenceBand";
135
+ }
136
+ }
137
+ return role;
138
+ }
139
+
140
+ function componentRole(program, id, property) {
141
+ for (const [owner, config] of Object.entries(program.markConfigs)) {
142
+ const box = config.boxPlot;
143
+ if (box !== undefined) {
144
+ if (id === box.medianId && property === "stroke") return "boxMedian";
145
+ if (id === box.outlierId && ["fill", "stroke"].includes(property)) {
146
+ return "boxLine";
147
+ }
148
+ if (id === box.whiskerId && property === "stroke") return "boxLine";
149
+ const whisker = program.markConfigs[box.whiskerId]?.errorBar;
150
+ if ([whisker?.lowerCapId, whisker?.upperCapId].includes(id) &&
151
+ property === "stroke") return "boxLine";
152
+ }
153
+ if (config.gradientPlot !== undefined &&
154
+ id === `${owner}Center` && property === "stroke") {
155
+ return "gradientCenter";
156
+ }
157
+ }
158
+ return referenceRole(program, id, property);
159
+ }
160
+
161
+ function graphicRole(program, id, property) {
162
+ if (property === "fontFamily") return "fontFamily";
163
+ if (id === "canvas" && property === "background") return "background";
164
+ if (id === `${program.compositionSpec?.id}-headers` && property === "fill") {
165
+ return "strongText";
166
+ }
167
+ const component = componentRole(program, id, property);
168
+ if (component !== undefined) return component;
169
+ const layer = findLayer(program, id);
170
+ if (layer !== undefined && ["fill", "stroke"].includes(property)) {
171
+ if (isFieldDrivenMark(program, id, property)) return undefined;
172
+ if (property === "fill" && isRegressionBand(program, id)) {
173
+ return "regressionBand";
174
+ }
175
+ return layer.mark?.type === "text" ? "text" : "mark";
176
+ }
177
+ if (/AxisLines?$/u.test(id) && property === "stroke") return "axis";
178
+ if (/AxisTicks$/u.test(id) && property === "stroke") return "mutedText";
179
+ if (/^parallelAxisLabels$/u.test(id) && property === "fill") return "axis";
180
+ if (/AxisLabels$/u.test(id) && property === "fill") return "text";
181
+ if (/AxisTitles?$/u.test(id) && property === "fill") return "axisTitle";
182
+ if (/Grid(?:Lines|Circles)?$/u.test(id) && property === "stroke") return "grid";
183
+ if (id === "chartTitle" && property === "fill") return "strongText";
184
+ if (id === "chartSubtitle" && property === "fill") return "mutedText";
185
+ if (/(?:Legend|Gradient)Labels$/u.test(id) && property === "fill") return "text";
186
+ if (/(?:Legend|Gradient)Title$/u.test(id) && property === "fill") return "strongText";
187
+ if (/(?:Legend|Gradient)Background$/u.test(id) && property === "stroke") return "border";
188
+ if (/GradientTicks$/u.test(id) && property === "stroke") return "mutedText";
189
+ if (/opacityLegendSymbols$/iu.test(id) && ["fill", "stroke"].includes(property)) {
190
+ return "mark";
191
+ }
192
+ if (/sizeLegendSymbols$/iu.test(id) && ["fill", "stroke"].includes(property)) {
193
+ return "sizeSymbol";
194
+ }
195
+ if (/strokeWidthLegendSymbols$/iu.test(id) && property === "stroke") {
196
+ return "mark";
197
+ }
198
+ return undefined;
199
+ }
200
+
201
+ function roleLegacy(role) {
202
+ if (role === "axis") return ["text"];
203
+ if (role === "axisTitle") return ["text", "strongText"];
204
+ if (role === "strongText") return ["text"];
205
+ if (role === "grid") return ["#d7e0ea"];
206
+ return [];
207
+ }
208
+
209
+ function isOverridden(overrides, key) {
210
+ return overrides.has(key);
211
+ }
212
+
213
+ function isFieldDrivenLegendSymbol(id) {
214
+ return /(?:color|series|stroke)(?:Legend|Gradient|Interval)(?:Symbol|Symbols|Strips|Swatches|Lines|Points)/iu
215
+ .test(id);
216
+ }
217
+
218
+ function mapGraphicProperties(
219
+ program,
220
+ id,
221
+ type,
222
+ properties,
223
+ sourceTokens,
224
+ targetTokens,
225
+ overrides,
226
+ overrideId = id
227
+ ) {
228
+ let changed = false;
229
+ const next = { ...properties };
230
+ for (const property of THEME_STYLE_PROPERTIES) {
231
+ if (!Object.hasOwn(properties, property)) continue;
232
+ if (isOverridden(overrides, `g:${overrideId}.${property}`) ||
233
+ isOverridden(overrides, `g:${id}.${property}`)) continue;
234
+ const role = graphicRole(program, id, property);
235
+ if (role === undefined || isFieldDrivenLegendSymbol(id) ||
236
+ isFieldDrivenMark(program, id, property)) continue;
237
+ const mapped = mapRoleValue(
238
+ properties[property],
239
+ role,
240
+ sourceTokens,
241
+ targetTokens,
242
+ roleLegacy(role)
243
+ );
244
+ if (mapped.changed) {
245
+ next[property] = mapped.value;
246
+ changed = true;
247
+ }
248
+ }
249
+ return { changed, properties: changed ? next : properties, type };
250
+ }
251
+
252
+ function recolorGraphic(
253
+ program,
254
+ id,
255
+ graphic,
256
+ sourceTokens,
257
+ targetTokens,
258
+ overrides
259
+ ) {
260
+ if (graphic.items === undefined) {
261
+ const mapped = mapGraphicProperties(
262
+ program,
263
+ id,
264
+ graphic.type,
265
+ graphic.properties,
266
+ sourceTokens,
267
+ targetTokens,
268
+ overrides,
269
+ id
270
+ );
271
+ if (!mapped.changed) return program;
272
+ let next = program;
273
+ for (const property of THEME_STYLE_PROPERTIES) {
274
+ if (mapped.properties[property] !== graphic.properties[property]) {
275
+ next = next.editGraphics({
276
+ target: id,
277
+ property,
278
+ value: mapped.properties[property]
279
+ });
280
+ }
281
+ }
282
+ return next;
283
+ }
284
+
285
+ let changed = false;
286
+ const items = graphic.items.map(item => {
287
+ const type = item.type ?? graphic.type;
288
+ const mapped = mapGraphicProperties(
289
+ program,
290
+ id,
291
+ type,
292
+ item.properties,
293
+ sourceTokens,
294
+ targetTokens,
295
+ overrides,
296
+ item.id
297
+ );
298
+ changed ||= mapped.changed;
299
+ return { type, properties: mapped.properties };
300
+ });
301
+ return changed
302
+ ? program.editGraphics({ target: id, property: "items", value: items })
303
+ : program;
304
+ }
305
+
306
+ function configRole(program, path) {
307
+ const property = path.at(-1);
308
+ if (property === "fontFamily") return "fontFamily";
309
+ if (path[0] === "facets" && path.includes("headers") &&
310
+ property === "color") {
311
+ return path[path.indexOf("headers") + 1] === "row"
312
+ ? "mutedText"
313
+ : "strongText";
314
+ }
315
+ if (path[0] === "marks" && ["fill", "stroke"].includes(property)) {
316
+ if (path.includes("boxPlot") && path.includes("median") &&
317
+ property === "stroke") return "boxMedian";
318
+ if (path.includes("gradientPlot") && path.includes("center") &&
319
+ property === "stroke") return "gradientCenter";
320
+ const component = componentRole(program, path[1], property);
321
+ if (component !== undefined) return component;
322
+ const layer = findLayer(program, path[1]);
323
+ if (isFieldDrivenMark(program, path[1], property)) return undefined;
324
+ if (property === "fill" && isRegressionBand(program, path[1])) {
325
+ return "regressionBand";
326
+ }
327
+ return layer?.mark?.type === "text" ? "text" : "mark";
328
+ }
329
+ if (path[0] === "guides" && path[1] === "axis") {
330
+ const component = path[3];
331
+ if (property !== "color") return undefined;
332
+ if (component === "line") return "axis";
333
+ if (component === "ticks") return "mutedText";
334
+ if (component === "labels") return "text";
335
+ if (component === "title") return "axisTitle";
336
+ }
337
+ if (path[0] === "guides" && path[1] === "grid" && property === "color") {
338
+ return "grid";
339
+ }
340
+ if (path[0] === "title" && property === "color") {
341
+ if (path.includes("titleStyle")) return "strongText";
342
+ if (path.includes("subtitleStyle")) return "mutedText";
343
+ }
344
+ return undefined;
345
+ }
346
+
347
+ function recolorConfig(
348
+ program,
349
+ value,
350
+ sourceTokens,
351
+ targetTokens,
352
+ overrides,
353
+ path = []
354
+ ) {
355
+ if (Array.isArray(value)) {
356
+ let changed = false;
357
+ const next = value.map((item, index) => {
358
+ const mapped = recolorConfig(
359
+ program,
360
+ item,
361
+ sourceTokens,
362
+ targetTokens,
363
+ overrides,
364
+ [...path, index]
365
+ );
366
+ changed ||= mapped.changed;
367
+ return mapped.value;
368
+ });
369
+ return { changed, value: changed ? next : value };
370
+ }
371
+ if (value === null || typeof value !== "object") {
372
+ const property = path.at(-1);
373
+ if (!["color", "fill", "stroke", "background", "fontFamily"]
374
+ .includes(property)) {
375
+ return { changed: false, value };
376
+ }
377
+ if (isOverridden(overrides, `c:${path.join(".")}`)) {
378
+ return { changed: false, value };
379
+ }
380
+ if (property === "background" && value === "transparent") {
381
+ return { changed: false, value };
382
+ }
383
+ const role = configRole(program, path);
384
+ return role === undefined
385
+ ? mapColorValue(value, sourceTokens, targetTokens)
386
+ : mapRoleValue(
387
+ value,
388
+ role,
389
+ sourceTokens,
390
+ targetTokens,
391
+ roleLegacy(role)
392
+ );
393
+ }
394
+ if (["theme", "highlights", "selections"].includes(path[0])) {
395
+ return { changed: false, value };
396
+ }
397
+ let changed = false;
398
+ const next = {};
399
+ for (const [key, item] of Object.entries(value)) {
400
+ const mapped = recolorConfig(
401
+ program,
402
+ item,
403
+ sourceTokens,
404
+ targetTokens,
405
+ overrides,
406
+ [...path, key]
407
+ );
408
+ changed ||= mapped.changed;
409
+ next[key] = mapped.value;
410
+ }
411
+ return { changed, value: changed ? next : value };
412
+ }
413
+
414
+ function addMarkOverrides(overrides, program, op, args) {
415
+ const match = /^(?:create|edit)(Point|Line|Area|Arc|Bar|Rect|Rule|Tick|Text)Mark$/u
416
+ .exec(op);
417
+ if (match === null) return;
418
+ const type = match[1].toLowerCase();
419
+ const candidates = program.semanticSpec.layers.filter(
420
+ layer => layer.mark?.type === type
421
+ );
422
+ const id = args.target ?? args.id ??
423
+ (op.startsWith("create") ? type : undefined) ??
424
+ (candidates.length === 1 ? candidates[0].id : program.context.currentMark);
425
+ if (typeof id !== "string") return;
426
+ for (const property of ["fill", "stroke", "fontFamily"]) {
427
+ if (!Object.hasOwn(args, property)) continue;
428
+ overrides.add(`c:marks.${id}.${property}`);
429
+ overrides.add(`g:${id}.${property}`);
430
+ }
431
+ }
432
+
433
+ function addFacadeMarkOverrides(overrides, op, args) {
434
+ const facades = {
435
+ createScatterPlot: ["scatterPlot", "point", ["fill", "stroke"]],
436
+ createLinePlot: ["linePlot", "line", ["stroke"]],
437
+ createPolarScatterPlot: ["polarScatterPlot", "point", ["fill", "stroke"]],
438
+ createPolarLinePlot: ["polarLinePlot", "line", ["stroke"]],
439
+ createRadarPlot: ["radarPlot", "line", ["stroke"]],
440
+ createRugPlot: ["rugPlot", "tick", ["stroke"]],
441
+ createStripPlot: ["stripPlot", "point", ["fill", "stroke"]],
442
+ createBeeswarmPlot: ["beeswarmPlot", "point", ["fill", "stroke"]],
443
+ createBarPlot: ["barPlot", "bar", ["fill", "stroke"]],
444
+ createAreaPlot: ["areaPlot", "area", ["fill", "stroke"]],
445
+ createPiePlot: ["piePlot", "arc", ["fill", "stroke"]],
446
+ createRosePlot: ["rosePlot", "arc", ["fill", "stroke"]],
447
+ createRadialBarPlot: ["radialBarPlot", "arc", ["fill", "stroke"]],
448
+ createDensityPlot: ["densityPlot", "area", ["fill", "stroke"]],
449
+ createParallelCoordinates: ["parallelCoordinates", "line", ["stroke"]],
450
+ createHistogram: ["histogram", "bar", ["fill", "stroke"]],
451
+ createHeatmap: ["heatmap", "rect", ["stroke"]],
452
+ createHorizonPlot: ["horizonPlot", "area", ["stroke"]],
453
+ editHorizon: ["horizonPlot", "area", ["stroke"]],
454
+ createViolinPlot: ["violinPlot", "area", ["fill", "stroke"]]
455
+ };
456
+ const facade = facades[op];
457
+ if (facade === undefined) return;
458
+ const [defaultId, option, properties] = facade;
459
+ const id = args.id ?? defaultId;
460
+ for (const property of properties) {
461
+ if (!Object.hasOwn(args[option] ?? {}, property)) continue;
462
+ overrides.add(`c:marks.${id}.${property}`);
463
+ overrides.add(`g:${id}.${property}`);
464
+ }
465
+ }
466
+
467
+ function findDescendant(node, op) {
468
+ for (const child of node?.children ?? []) {
469
+ if (child.op === op) return child;
470
+ const nested = findDescendant(child, op);
471
+ if (nested !== undefined) return nested;
472
+ }
473
+ return undefined;
474
+ }
475
+
476
+ function resolveConfiguredOwner(program, key, requested, defaultId) {
477
+ if (typeof requested === "string" && program.markConfigs[requested]?.[key]) {
478
+ return requested;
479
+ }
480
+ if (typeof defaultId === "string" && program.markConfigs[defaultId]?.[key]) {
481
+ return defaultId;
482
+ }
483
+ const candidates = Object.entries(program.markConfigs)
484
+ .filter(([, config]) => config[key] !== undefined)
485
+ .map(([id]) => id);
486
+ return candidates.length === 1 ? candidates[0] : undefined;
487
+ }
488
+
489
+ function addTextFacadeOverrides(overrides, node) {
490
+ if (!["createAnnotation", "createMarkLabels"].includes(node.op)) return;
491
+ const id = findDescendant(node, "createTextMark")?.args.id ?? node.args.id;
492
+ if (typeof id !== "string") return;
493
+ for (const property of ["fill", "fontFamily"]) {
494
+ if (!Object.hasOwn(node.args, property)) continue;
495
+ overrides.add(`c:marks.${id}.${property}`);
496
+ overrides.add(`g:${id}.${property}`);
497
+ }
498
+ }
499
+
500
+ function addErrorBarOverrides(overrides, program, node) {
501
+ if (!["createErrorBar", "editErrorBar"].includes(node.op) ||
502
+ !Object.hasOwn(node.args, "stroke")) return;
503
+ const id = resolveConfiguredOwner(
504
+ program,
505
+ "errorBar",
506
+ node.op === "createErrorBar" ? node.args.id : node.args.target,
507
+ node.op === "createErrorBar" ? "errorBar" : undefined
508
+ );
509
+ if (id === undefined) return;
510
+ const config = program.markConfigs[id].errorBar;
511
+ for (const target of [id, config.lowerCapId, config.upperCapId]) {
512
+ overrides.add(`c:marks.${target}.stroke`);
513
+ overrides.add(`g:${target}.stroke`);
514
+ }
515
+ }
516
+
517
+ function errorBandTargets(program, node) {
518
+ const id = resolveConfiguredOwner(
519
+ program,
520
+ "errorBand",
521
+ node.op === "createErrorBand" ? node.args.id : node.args.target,
522
+ node.op === "createErrorBand" ? "errorBand" : undefined
523
+ );
524
+ if (id === undefined) return undefined;
525
+ const config = program.markConfigs[id].errorBand;
526
+ return {
527
+ id,
528
+ lower: config.lowerBoundaryId,
529
+ upper: config.upperBoundaryId
530
+ };
531
+ }
532
+
533
+ function addErrorBandOverrides(overrides, program, node) {
534
+ if (!["createErrorBand", "editErrorBand", "editErrorBandBoundary"]
535
+ .includes(node.op)) return;
536
+ const targets = errorBandTargets(program, node);
537
+ if (targets === undefined) return;
538
+ if (Object.hasOwn(node.args, "fill") && node.args.fill !== false) {
539
+ overrides.add(`c:marks.${targets.id}.fill`);
540
+ overrides.add(`g:${targets.id}.fill`);
541
+ }
542
+ const boundary = node.op === "editErrorBandBoundary"
543
+ ? node.args
544
+ : node.args.boundaries;
545
+ if (!Object.hasOwn(boundary ?? {}, "stroke")) return;
546
+ const ids = node.op !== "editErrorBandBoundary" ||
547
+ (node.args.boundary ?? "both") === "both"
548
+ ? [targets.lower, targets.upper]
549
+ : [(node.args.boundary === "lower" ? targets.lower : targets.upper)];
550
+ for (const id of ids) {
551
+ overrides.add(`c:marks.${id}.stroke`);
552
+ overrides.add(`g:${id}.stroke`);
553
+ }
554
+ }
555
+
556
+ function addBoxPlotOverrides(overrides, op, args) {
557
+ if (!["createBoxPlot", "editBoxPlot"].includes(op)) return;
558
+ const id = args.target ?? args.id ?? "boxPlot";
559
+ for (const property of ["fill", "stroke"]) {
560
+ if (!Object.hasOwn(args.box ?? {}, property)) continue;
561
+ overrides.add(`c:marks.${id}.${property}`);
562
+ overrides.add(`c:marks.${id}.boxPlot.box.${property}`);
563
+ overrides.add(`g:${id}.${property}`);
564
+ }
565
+ if (Object.hasOwn(args.median ?? {}, "stroke")) {
566
+ overrides.add(`c:marks.${id}.boxPlot.median.stroke`);
567
+ overrides.add(`c:marks.${id}Median.stroke`);
568
+ overrides.add(`g:${id}Median.stroke`);
569
+ }
570
+ }
571
+
572
+ function addGradientPlotOverrides(overrides, op, args) {
573
+ if (!["createGradientPlot", "editGradientPlot"].includes(op)) return;
574
+ const id = args.target ?? args.id ?? "gradientPlot";
575
+ if (Object.hasOwn(args.center ?? {}, "stroke")) {
576
+ overrides.add(`c:marks.${id}.gradientPlot.center.stroke`);
577
+ overrides.add(`g:${id}Center.stroke`);
578
+ }
579
+ }
580
+
581
+ function addReferenceOverrides(overrides, op, args) {
582
+ const definitions = {
583
+ createReferenceLine: ["referenceLine", ["stroke"]],
584
+ createReferenceBand: ["referenceBand", ["fill", "stroke"]]
585
+ };
586
+ const definition = definitions[op];
587
+ if (definition === undefined) return;
588
+ const [defaultId, properties] = definition;
589
+ const id = args.id ?? defaultId;
590
+ for (const property of properties) {
591
+ if (!Object.hasOwn(args, property)) continue;
592
+ overrides.add(`c:marks.${id}.${property}`);
593
+ overrides.add(`g:${id}.${property}`);
594
+ }
595
+ }
596
+
597
+ function addRegressionOverrides(overrides, node) {
598
+ const style = node.op === "createRegression" || node.op === "editRegression"
599
+ ? node.args.band
600
+ : node.args;
601
+ if (!["createRegression", "editRegression", "createRegressionBand",
602
+ "editRegressionBand"].includes(node.op) || style === false) return;
603
+ const child = findDescendant(
604
+ node,
605
+ node.op.startsWith("create") ? "createRegressionBand" : "editRegressionBand"
606
+ );
607
+ const id = node.op === "createRegressionBand"
608
+ ? node.args.id
609
+ : node.op === "editRegressionBand"
610
+ ? node.args.target ?? child?.args.target
611
+ : child?.args.id ?? child?.args.target;
612
+ if (typeof id !== "string") return;
613
+ if (Object.hasOwn(style ?? {}, "color")) {
614
+ overrides.add(`c:marks.${id}.fill`);
615
+ overrides.add(`g:${id}.fill`);
616
+ }
617
+ if (Object.hasOwn(style ?? {}, "stroke")) {
618
+ overrides.add(`c:marks.${id}.stroke`);
619
+ overrides.add(`g:${id}.stroke`);
620
+ }
621
+ }
622
+
623
+ function clearPrefix(overrides, prefix) {
624
+ for (const key of overrides) {
625
+ if (key.startsWith(prefix)) overrides.delete(key);
626
+ }
627
+ }
628
+
629
+ function visitTrace(node, visitor) {
630
+ visitor(node);
631
+ for (const child of node.children ?? []) visitTrace(child, visitor);
632
+ }
633
+
634
+ function clearRemovedResources(overrides, node) {
635
+ visitTrace(node, current => {
636
+ if (current.op === "editGraphics" && current.args.remove === true &&
637
+ typeof current.args.target === "string") {
638
+ clearPrefix(overrides, `g:${current.args.target}.`);
639
+ }
640
+ if (current.op !== "editSemantic" || current.args.remove !== true ||
641
+ typeof current.args.property !== "string") return;
642
+ const mark = /^layer\[([^\]]+)\]$/u.exec(current.args.property);
643
+ if (mark !== null) {
644
+ clearPrefix(overrides, `c:marks.${mark[1]}.`);
645
+ clearPrefix(overrides, `g:${mark[1]}.`);
646
+ return;
647
+ }
648
+ const axis = /^guide\.axis\.(x|y|theta|radius)$/u
649
+ .exec(current.args.property);
650
+ if (axis !== null) {
651
+ const prefix = axis[1] === "radius" ? "radial" : axis[1];
652
+ clearPrefix(overrides, `c:guides.axis.${axis[1]}.`);
653
+ clearPrefix(overrides, `g:${prefix}Axis`);
654
+ return;
655
+ }
656
+ const grid = /^guide\.grid\.([^\.]+)$/u.exec(current.args.property);
657
+ if (grid !== null) {
658
+ clearPrefix(overrides, `c:guides.grid.${grid[1]}.`);
659
+ clearPrefix(overrides, `g:${grid[1]}Grid`);
660
+ return;
661
+ }
662
+ const legend = /^guide\.legend\.([^\.]+)$/u.exec(current.args.property);
663
+ if (legend !== null) {
664
+ clearPrefix(overrides, `c:guides.legend.${legend[1]}.`);
665
+ }
666
+ });
667
+ }
668
+
669
+ function clearLocalResets(overrides, program, node) {
670
+ if (node.op !== "editErrorBand") return;
671
+ const targets = errorBandTargets(program, node);
672
+ if (targets === undefined) return;
673
+ if (node.args.fill === false) {
674
+ overrides.delete(`c:marks.${targets.id}.fill`);
675
+ overrides.delete(`g:${targets.id}.fill`);
676
+ }
677
+ if (node.args.boundaries === false) {
678
+ for (const id of [targets.lower, targets.upper]) {
679
+ clearPrefix(overrides, `c:marks.${id}.`);
680
+ clearPrefix(overrides, `g:${id}.`);
681
+ }
682
+ }
683
+ }
684
+
685
+ function clearRecreatedOverrides(overrides, op, args) {
686
+ const created = /^create(Point|Line|Area|Arc|Bar|Rect|Rule|Tick|Text)Mark$/u
687
+ .exec(op);
688
+ if (created !== null) {
689
+ const id = args.id ?? created[1].toLowerCase();
690
+ clearPrefix(overrides, `c:marks.${id}.`);
691
+ clearPrefix(overrides, `g:${id}.`);
692
+ }
693
+ if (op === "removeMark" && typeof args.target === "string") {
694
+ clearPrefix(overrides, `c:marks.${args.target}.`);
695
+ clearPrefix(overrides, `g:${args.target}.`);
696
+ }
697
+ if (op === "createTitle") {
698
+ clearPrefix(overrides, "c:title.");
699
+ clearPrefix(overrides, "g:chartTitle.");
700
+ clearPrefix(overrides, "g:chartSubtitle.");
701
+ }
702
+ if (op === "removeTitle") {
703
+ clearPrefix(overrides, "c:title.");
704
+ clearPrefix(overrides, "g:chartTitle.");
705
+ clearPrefix(overrides, "g:chartSubtitle.");
706
+ }
707
+ }
708
+
709
+ function addAxisOverrides(overrides, op, args) {
710
+ const match = /^(?:create|edit)(X|Y|Theta|Radial)Axis(Line|Ticks|Labels|Title)$/u
711
+ .exec(op);
712
+ if (match === null) return;
713
+ const channel = {
714
+ X: "x",
715
+ Y: "y",
716
+ Theta: "theta",
717
+ Radial: "radius"
718
+ }[match[1]];
719
+ const component = match[2].toLowerCase();
720
+ const idPrefix = match[1] === "Radial" ? "radial" : channel;
721
+ if (Object.hasOwn(args, "color")) {
722
+ overrides.add(`c:guides.axis.${channel}.${component}.color`);
723
+ overrides.add(
724
+ `g:${idPrefix}Axis${match[2]}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
725
+ );
726
+ }
727
+ if (["labels", "title"].includes(component) &&
728
+ Object.hasOwn(args, "fontFamily")) {
729
+ overrides.add(`c:guides.axis.${channel}.${component}.fontFamily`);
730
+ overrides.add(`g:${idPrefix}Axis${match[2]}.fontFamily`);
731
+ }
732
+ }
733
+
734
+ function addCompleteAxisOverride(overrides, name, args) {
735
+ const channel = { X: "x", Y: "y", Theta: "theta", Radial: "radius" }[name];
736
+ const idPrefix = name === "Radial" ? "radial" : channel;
737
+ const components = {
738
+ line: args.line,
739
+ ticks: args.ticks,
740
+ labels: args.labels,
741
+ title: args.title
742
+ };
743
+ if (args.ticksAndLabels && typeof args.ticksAndLabels === "object") {
744
+ components.ticks = args.ticksAndLabels.ticks;
745
+ components.labels = args.ticksAndLabels.labels;
746
+ }
747
+ for (const [component, options] of Object.entries(components)) {
748
+ const suffix = `${component[0].toUpperCase()}${component.slice(1)}`;
749
+ if (Object.hasOwn(options ?? {}, "color")) {
750
+ overrides.add(`c:guides.axis.${channel}.${component}.color`);
751
+ overrides.add(
752
+ `g:${idPrefix}Axis${suffix}.${component === "line" || component === "ticks" ? "stroke" : "fill"}`
753
+ );
754
+ }
755
+ if (["labels", "title"].includes(component) &&
756
+ Object.hasOwn(options ?? {}, "fontFamily")) {
757
+ overrides.add(`c:guides.axis.${channel}.${component}.fontFamily`);
758
+ overrides.add(`g:${idPrefix}Axis${suffix}.fontFamily`);
759
+ }
760
+ }
761
+ }
762
+
763
+ function addCompleteAxisOverrides(overrides, op, args) {
764
+ const match = /^(?:create|edit)(X|Y|Theta|Radial)Axis$/u.exec(op);
765
+ if (match !== null) addCompleteAxisOverride(overrides, match[1], args);
766
+ const group = /^(?:create|edit)(X|Y)AxisTicksAndLabels$/u.exec(op);
767
+ if (group !== null) {
768
+ addCompleteAxisOverride(overrides, group[1], { ticksAndLabels: args });
769
+ }
770
+ if (op !== "createAxes") return;
771
+ for (const [option, name] of [
772
+ ["x", "X"],
773
+ ["y", "Y"],
774
+ ["theta", "Theta"],
775
+ ["radius", "Radial"]
776
+ ]) {
777
+ if (args[option] && typeof args[option] === "object") {
778
+ addCompleteAxisOverride(overrides, name, args[option]);
779
+ }
780
+ }
781
+ }
782
+
783
+ function addGridOverrides(overrides, op, args) {
784
+ const match = /^(?:create|edit)(Horizontal|Vertical|Theta|Radial)Grid$/u.exec(op);
785
+ if (match === null || !Object.hasOwn(args, "color")) return;
786
+ const direction = match[1].toLowerCase();
787
+ overrides.add(`c:guides.grid.${direction}.color`);
788
+ overrides.add(`g:${direction}Grid.stroke`);
789
+ }
790
+
791
+ function addTitleOverrides(overrides, args) {
792
+ for (const [option, id] of [
793
+ ["titleStyle", "chartTitle"],
794
+ ["subtitleStyle", "chartSubtitle"]
795
+ ]) {
796
+ if (Object.hasOwn(args[option] ?? {}, "color")) {
797
+ overrides.add(`c:title.${option}.color`);
798
+ overrides.add(`g:${id}.fill`);
799
+ }
800
+ if (Object.hasOwn(args[option] ?? {}, "fontFamily")) {
801
+ overrides.add(`c:title.${option}.fontFamily`);
802
+ overrides.add(`g:${id}.fontFamily`);
803
+ }
804
+ }
805
+ }
806
+
807
+ function addParallelOverrides(overrides, program, op, args) {
808
+ if (!["createParallelAxis", "editParallelAxis"].includes(op)) return;
809
+ const dimensions = program.guideConfigs.axis?.parallel?.axes?.dimensions;
810
+ const index = dimensions?.findIndex(dimension => dimension.field === args.field);
811
+ if (index === undefined || index < 0) return;
812
+ const components = {
813
+ line: args.line,
814
+ ticks: args.ticks,
815
+ labels: args.labels,
816
+ title: args.title
817
+ };
818
+ if (args.ticksAndLabels && typeof args.ticksAndLabels === "object") {
819
+ components.ticks = args.ticksAndLabels.ticks;
820
+ components.labels = args.ticksAndLabels.labels;
821
+ }
822
+ for (const [component, options] of Object.entries(components)) {
823
+ if (Object.hasOwn(options ?? {}, "color")) {
824
+ overrides.add(
825
+ `c:guides.axis.parallel.axes.dimensions.${index}.${component}.color`
826
+ );
827
+ }
828
+ if (["labels", "title"].includes(component) &&
829
+ Object.hasOwn(options ?? {}, "fontFamily")) {
830
+ overrides.add(
831
+ `c:guides.axis.parallel.axes.dimensions.${index}.${component}.fontFamily`
832
+ );
833
+ }
834
+ }
835
+ }
836
+
837
+ function addLegendOverrides(overrides, program, args) {
838
+ const legend = program.guideConfigs.legend ?? {};
839
+ const prefixes = {
840
+ series: "seriesLegend",
841
+ color: "colorLegend",
842
+ stroke: "strokeLegend",
843
+ interval: "colorLegend",
844
+ gradient: "colorGradient",
845
+ strokeInterval: "strokeInterval",
846
+ strokeGradient: "strokeGradient",
847
+ size: "sizeLegend",
848
+ strokeWidth: "strokeWidthLegend",
849
+ opacity: "opacityLegend"
850
+ };
851
+ for (const kind of Object.keys(legend)) {
852
+ if (Object.hasOwn(args.labels ?? {}, "color")) {
853
+ overrides.add(`c:guides.legend.${kind}.labels.color`);
854
+ if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Labels.fill`);
855
+ }
856
+ if (Object.hasOwn(args.labels ?? {}, "fontFamily")) {
857
+ overrides.add(`c:guides.legend.${kind}.labels.fontFamily`);
858
+ if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Labels.fontFamily`);
859
+ }
860
+ if (Object.hasOwn(args.titleStyle ?? {}, "color")) {
861
+ overrides.add(`c:guides.legend.${kind}.titleStyle.color`);
862
+ if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Title.fill`);
863
+ }
864
+ if (Object.hasOwn(args.titleStyle ?? {}, "fontFamily")) {
865
+ overrides.add(`c:guides.legend.${kind}.titleStyle.fontFamily`);
866
+ if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Title.fontFamily`);
867
+ }
868
+ if (Object.hasOwn(args.border ?? {}, "color")) {
869
+ overrides.add(`c:guides.legend.${kind}.border.color`);
870
+ if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Background.stroke`);
871
+ }
872
+ }
873
+ }
874
+
875
+ function legendPrefix(kind) {
876
+ return {
877
+ series: "seriesLegend",
878
+ color: "colorLegend",
879
+ stroke: "strokeLegend",
880
+ interval: "colorLegend",
881
+ gradient: "colorGradient",
882
+ strokeInterval: "strokeInterval",
883
+ strokeGradient: "strokeGradient",
884
+ size: "sizeLegend",
885
+ strokeWidth: "strokeWidthLegend",
886
+ opacity: "opacityLegend"
887
+ }[kind];
888
+ }
889
+
890
+ function addLegendBlockOverrides(overrides, program, args) {
891
+ if (typeof args.target !== "string" || typeof args.channel !== "string") return;
892
+ for (const [kind, config] of Object.entries(program.guideConfigs.legend ?? {})) {
893
+ if (config.target !== args.target) continue;
894
+ const key = Object.keys(config.blockOverrides ?? {}).find(value => {
895
+ try {
896
+ return JSON.parse(value).includes(args.channel);
897
+ } catch {
898
+ return false;
899
+ }
900
+ });
901
+ if (key === undefined) continue;
902
+ const prefix = `c:guides.legend.${kind}.blockOverrides.${key}`;
903
+ const graphic = legendPrefix(kind);
904
+ for (const [property, graphicProperty] of [
905
+ ["color", "fill"],
906
+ ["fontFamily", "fontFamily"]
907
+ ]) {
908
+ if (!Object.hasOwn(args.text ?? {}, property)) continue;
909
+ overrides.add(`${prefix}.text.${property}`);
910
+ if (graphic !== undefined) {
911
+ overrides.add(`g:${graphic}Labels.${graphicProperty}`);
912
+ }
913
+ }
914
+ for (const property of ["fill", "stroke"]) {
915
+ if (!Object.hasOwn(args.symbol ?? {}, property)) continue;
916
+ overrides.add(`${prefix}.symbol.${property}`);
917
+ if (graphic !== undefined) {
918
+ overrides.add(`g:${graphic}Symbols.${property}`);
919
+ }
920
+ }
921
+ }
922
+ }
923
+
924
+ function addFacetOverrides(overrides, program, args) {
925
+ const id = program.compositionSpec?.type === "facet"
926
+ ? program.compositionSpec.id
927
+ : undefined;
928
+ if (id === undefined) return;
929
+ const owner = args.role === undefined || args.role === "all"
930
+ ? "common"
931
+ : args.role;
932
+ for (const [property, graphicProperty] of [
933
+ ["color", "fill"],
934
+ ["fontFamily", "fontFamily"]
935
+ ]) {
936
+ if (!Object.hasOwn(args, property)) continue;
937
+ overrides.add(`c:facets.${id}.headers.${owner}.${property}`);
938
+ overrides.add(`g:${id}-headers.${graphicProperty}`);
939
+ }
940
+ }
941
+
942
+ function explicitOverrides(program, node) {
943
+ const { op, args } = node;
944
+ const overrides = new Set();
945
+ if (op === "editGraphics" &&
946
+ typeof args.target === "string" &&
947
+ ["background", "fill", "stroke", "fontFamily"].includes(args.property)) {
948
+ overrides.add(`g:${args.target}.${args.property}`);
949
+ }
950
+ if (["createCanvas", "editCanvas"].includes(op) &&
951
+ Object.hasOwn(args, "background")) {
952
+ overrides.add("g:canvas.background");
953
+ }
954
+ addMarkOverrides(overrides, program, op, args);
955
+ addFacadeMarkOverrides(overrides, op, args);
956
+ addTextFacadeOverrides(overrides, node);
957
+ addBoxPlotOverrides(overrides, op, args);
958
+ addGradientPlotOverrides(overrides, op, args);
959
+ addReferenceOverrides(overrides, op, args);
960
+ addErrorBarOverrides(overrides, program, node);
961
+ addErrorBandOverrides(overrides, program, node);
962
+ addRegressionOverrides(overrides, node);
963
+ addAxisOverrides(overrides, op, args);
964
+ addCompleteAxisOverrides(overrides, op, args);
965
+ addGridOverrides(overrides, op, args);
966
+ addParallelOverrides(overrides, program, op, args);
967
+ if (["createTitle", "editTitle"].includes(op)) {
968
+ addTitleOverrides(overrides, args);
969
+ }
970
+ if (["createLegend", "createCategoricalLegend", "editLegend"].includes(op)) {
971
+ addLegendOverrides(overrides, program, args);
972
+ }
973
+ if (op === "editLegendLabels") {
974
+ addLegendOverrides(overrides, program, { labels: args });
975
+ }
976
+ if (op === "editLegendTitle") {
977
+ addLegendOverrides(overrides, program, { titleStyle: args });
978
+ }
979
+ if (op === "editLegendBorder") {
980
+ addLegendOverrides(overrides, program, args);
981
+ }
982
+ if (op === "editLegendBlock") {
983
+ addLegendBlockOverrides(overrides, program, args);
984
+ }
985
+ if (op === "editFacetHeaders") {
986
+ addFacetOverrides(overrides, program, args);
987
+ }
988
+ return overrides;
989
+ }
990
+
991
+ function collectOverrides(program) {
992
+ const overrides = new Set();
993
+ for (const node of program.trace.children) {
994
+ if (["applyTheme", "removeTheme"].includes(node.op)) continue;
995
+ clearRecreatedOverrides(overrides, node.op, node.args);
996
+ clearRemovedResources(overrides, node);
997
+ clearLocalResets(overrides, program, node);
998
+ for (const key of explicitOverrides(program, node)) {
999
+ overrides.add(key);
1000
+ }
1001
+ }
1002
+ return overrides;
1003
+ }
1004
+
1005
+ function callExisting(program, graphicId, operation) {
1006
+ return program.graphicSpec.objects[graphicId] !== undefined &&
1007
+ typeof program[operation] === "function"
1008
+ ? program[operation]()
1009
+ : program;
1010
+ }
1011
+
1012
+ function rematerializeThemeTypography(program) {
1013
+ if (program.compositionSpec !== undefined) {
1014
+ return program.compositionSpec.type === "facet"
1015
+ ? program.materializeComposition()
1016
+ : program;
1017
+ }
1018
+
1019
+ let next = program;
1020
+ for (const layer of next.semanticSpec.layers) {
1021
+ if (layer.mark?.type === "text" &&
1022
+ next.graphicSpec.objects[layer.id] !== undefined &&
1023
+ typeof next.rematerializeTextMark === "function") {
1024
+ next = next.rematerializeTextMark({ id: layer.id });
1025
+ }
1026
+ }
1027
+ for (const [id, operation] of [
1028
+ ["xAxisLabels", "editXAxisLabels"],
1029
+ ["yAxisLabels", "editYAxisLabels"],
1030
+ ["xAxisTitle", "editXAxisTitle"],
1031
+ ["yAxisTitle", "editYAxisTitle"],
1032
+ ["thetaAxisLabels", "editThetaAxisLabels"],
1033
+ ["radialAxisLabels", "editRadialAxisLabels"],
1034
+ ["thetaAxisTitle", "editThetaAxisTitle"],
1035
+ ["radialAxisTitle", "editRadialAxisTitle"]
1036
+ ]) {
1037
+ next = callExisting(next, id, operation);
1038
+ }
1039
+ if (Object.keys(next.guideConfigs.legend ?? {}).length > 0 &&
1040
+ typeof next.rematerializeLegend === "function") {
1041
+ next = next.rematerializeLegend();
1042
+ }
1043
+ next = callExisting(next, "chartTitle", "rematerializeTitle");
1044
+ return next;
1045
+ }
1046
+
1047
+ export function reconcileProgramTheme(program, { source, metadata }) {
1048
+ const storedState = program.materializationConfigs.theme;
1049
+ if (storedState === undefined) return program;
1050
+ const composition = program.compositionSpec !== undefined;
1051
+ const state = normalizeThemeState(storedState, { composition });
1052
+ const sourceState = normalizeThemeState(
1053
+ source.materializationConfigs.theme,
1054
+ { composition }
1055
+ );
1056
+ const sourceRootTokens = resolveEffectiveThemeTokens(sourceState);
1057
+ const targetRootTokens = resolveEffectiveThemeTokens(state);
1058
+ const contentState = current => composition
1059
+ ? { frames: current.frames.filter(frame => frame.scope === "descendants") }
1060
+ : current;
1061
+ const sourceTokens = resolveEffectiveThemeTokens(contentState(sourceState));
1062
+ const targetTokens = resolveEffectiveThemeTokens(contentState(state));
1063
+ const overrides = collectOverrides(program);
1064
+
1065
+ let themed = program._withMaterializationConfig(
1066
+ ["theme"],
1067
+ setThemeStateOverrides(state, [...overrides])
1068
+ );
1069
+
1070
+ const configs = recolorConfig(
1071
+ themed,
1072
+ themed.materializationConfigs,
1073
+ sourceTokens,
1074
+ targetTokens,
1075
+ overrides
1076
+ );
1077
+ let next = configs.changed
1078
+ ? themed
1079
+ ._withMaterializationConfig(["marks"], configs.value.marks)
1080
+ ._withMaterializationConfig(["guides"], configs.value.guides)
1081
+ : themed;
1082
+ if (configs.changed && configs.value.title !== undefined) {
1083
+ next = next._withMaterializationConfig(["title"], configs.value.title);
1084
+ }
1085
+ if (configs.changed && configs.value.facets !== undefined) {
1086
+ next = next._withMaterializationConfig(["facets"], configs.value.facets);
1087
+ }
1088
+
1089
+ const facetsChanged = configs.value.facets !==
1090
+ themed.materializationConfigs.facets;
1091
+ if (facetsChanged && next.compositionSpec?.type === "facet") {
1092
+ next = next.materializeComposition();
1093
+ } else if (sourceTokens.fontFamily !== targetTokens.fontFamily) {
1094
+ next = rematerializeThemeTypography(next);
1095
+ }
1096
+
1097
+ const parallelChanged = configs.value.guides.axis?.parallel !==
1098
+ themed.guideConfigs.axis?.parallel;
1099
+ if (parallelChanged && typeof next.rematerializeParallelAxes === "function") {
1100
+ next = next.rematerializeParallelAxes();
1101
+ }
1102
+
1103
+ for (const [id, graphic] of Object.entries(next.graphicSpec.objects)) {
1104
+ if (parallelChanged && id.startsWith("parallelAxis")) continue;
1105
+ const graphicSourceTokens = id === "canvas"
1106
+ ? sourceRootTokens
1107
+ : sourceTokens;
1108
+ const graphicTargetTokens = id === "canvas"
1109
+ ? targetRootTokens
1110
+ : targetTokens;
1111
+ next = recolorGraphic(
1112
+ next,
1113
+ id,
1114
+ graphic,
1115
+ graphicSourceTokens,
1116
+ graphicTargetTokens,
1117
+ overrides
1118
+ );
1119
+ }
1120
+
1121
+ if (sourceTokens.highlight !== targetTokens.highlight) {
1122
+ next = rematerializeThemeHighlights(next);
1123
+ }
1124
+
1125
+ if (metadata.op === "removeTheme" && state.frames.length === 0 &&
1126
+ (state.descendantFrames?.length ?? 0) === 0) {
1127
+ next = next._withoutMaterializationConfig(["theme"]);
1128
+ }
1129
+ return next;
1130
+ }