ggaction 0.0.12 → 0.0.13

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 (281) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +2 -2
  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 +4 -2
  11. package/src/actions/basic.js +2 -0
  12. package/src/actions/boxPlots/materialize.js +2 -1
  13. package/src/actions/canvas/actions.js +3 -1
  14. package/src/actions/canvas/fitting.js +211 -0
  15. package/src/actions/canvas/index.js +2 -0
  16. package/src/actions/categoryOrder/index.js +3 -2
  17. package/src/actions/charts/area.js +47 -0
  18. package/src/actions/charts/bar.js +11 -5
  19. package/src/actions/charts/beeswarm.js +70 -0
  20. package/src/actions/charts/density.js +44 -0
  21. package/src/actions/charts/ecdf.js +183 -0
  22. package/src/actions/charts/endpoints.js +451 -0
  23. package/src/actions/charts/heatmap.js +2 -2
  24. package/src/actions/charts/histogram.js +1 -1
  25. package/src/actions/charts/horizon.js +71 -0
  26. package/src/actions/charts/index.js +37 -0
  27. package/src/actions/charts/interval-regression.js +169 -0
  28. package/src/actions/charts/line.js +5 -5
  29. package/src/actions/charts/parallel.js +1 -1
  30. package/src/actions/charts/pie.js +44 -0
  31. package/src/actions/charts/polar.js +123 -0
  32. package/src/actions/charts/radar.js +299 -0
  33. package/src/actions/charts/radial.js +42 -0
  34. package/src/actions/charts/raincloud.js +422 -0
  35. package/src/actions/charts/rug-strip.js +279 -0
  36. package/src/actions/charts/scatter.js +9 -3
  37. package/src/actions/charts/shared.js +69 -10
  38. package/src/actions/composition/actions.js +118 -1
  39. package/src/actions/data/bin.js +29 -0
  40. package/src/actions/data/computed.js +24 -0
  41. package/src/actions/data/derived.js +66 -0
  42. package/src/actions/data/ecdf.js +38 -0
  43. package/src/actions/data/filter.js +212 -28
  44. package/src/actions/data/fold.js +24 -0
  45. package/src/actions/data/index.js +24 -0
  46. package/src/actions/data/interval.js +2 -0
  47. package/src/actions/data/intervalEdit.js +86 -4
  48. package/src/actions/data/regression.js +5 -2
  49. package/src/actions/data/stack.js +26 -0
  50. package/src/actions/data/summary.js +26 -0
  51. package/src/actions/data/timeUnit.js +1 -1
  52. package/src/actions/encodings/appearance.js +32 -13
  53. package/src/actions/encodings/barWidth.js +8 -0
  54. package/src/actions/encodings/basic.js +2 -0
  55. package/src/actions/encodings/color/continuous.js +19 -59
  56. package/src/actions/encodings/color/index.js +28 -22
  57. package/src/actions/encodings/color/layout.js +3 -97
  58. package/src/actions/encodings/color/policy.js +5 -8
  59. package/src/actions/encodings/density/resolve.js +37 -5
  60. package/src/actions/encodings/density.js +120 -33
  61. package/src/actions/encodings/horizon/resolve.js +8 -4
  62. package/src/actions/encodings/horizon.js +18 -8
  63. package/src/actions/encodings/index.js +2 -0
  64. package/src/actions/encodings/offset.js +10 -16
  65. package/src/actions/encodings/parallel.js +2 -8
  66. package/src/actions/encodings/position/apply.js +10 -4
  67. package/src/actions/encodings/position/index.js +43 -5
  68. package/src/actions/encodings/position/policies/arc.js +14 -0
  69. package/src/actions/encodings/position/policies/area.js +4 -42
  70. package/src/actions/encodings/position/policies/bar.js +48 -13
  71. package/src/actions/encodings/position/resolve.js +67 -22
  72. package/src/actions/encodings/ranged.js +123 -97
  73. package/src/actions/encodings/remove.js +37 -32
  74. package/src/actions/encodings/ruleAppearance.js +23 -9
  75. package/src/actions/encodings/seriesLayout.js +104 -0
  76. package/src/actions/encodings/shared.js +25 -23
  77. package/src/actions/encodings/strokeDash.js +9 -6
  78. package/src/actions/encodings/temporal.js +7 -0
  79. package/src/actions/encodings/text.js +30 -15
  80. package/src/actions/errorBands/create.js +18 -5
  81. package/src/actions/errorBands/edit.js +341 -21
  82. package/src/actions/errorBars/create.js +21 -10
  83. package/src/actions/errorBars/edit.js +355 -19
  84. package/src/actions/facets/actions.js +410 -12
  85. package/src/actions/facets/derive.js +57 -8
  86. package/src/actions/gradientPlots/components.js +33 -11
  87. package/src/actions/gradientPlots/materialize.js +5 -7
  88. package/src/actions/guides/applicability.js +20 -17
  89. package/src/actions/guides/axes/axes.js +4 -3
  90. package/src/actions/guides/axes/axis.js +18 -18
  91. package/src/actions/guides/axes/components.js +43 -0
  92. package/src/actions/guides/axes/edit.js +16 -51
  93. package/src/actions/guides/axes/labels.js +129 -19
  94. package/src/actions/guides/axes/lines.js +7 -5
  95. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  96. package/src/actions/guides/axes/parallel/policy.js +137 -0
  97. package/src/actions/guides/axes/parallel/resolve.js +44 -23
  98. package/src/actions/guides/axes/parallel.js +93 -217
  99. package/src/actions/guides/axes/policy.js +21 -67
  100. package/src/actions/guides/axes/tickGroups.js +14 -8
  101. package/src/actions/guides/axes/ticks.js +5 -4
  102. package/src/actions/guides/axes/titles.js +21 -8
  103. package/src/actions/guides/basic.js +2 -0
  104. package/src/actions/guides/facade.js +106 -0
  105. package/src/actions/guides/facadeAxes.js +200 -0
  106. package/src/actions/guides/facadeLegend.js +63 -0
  107. package/src/actions/guides/grids/grid.js +37 -33
  108. package/src/actions/guides/grids/resolve.js +10 -2
  109. package/src/actions/guides/guides.js +35 -28
  110. package/src/actions/guides/legends/categorical/actions.js +193 -249
  111. package/src/actions/guides/legends/categorical/components.js +4 -5
  112. package/src/actions/guides/legends/categorical/layout.js +36 -349
  113. package/src/actions/guides/legends/categorical/options.js +30 -14
  114. package/src/actions/guides/legends/categorical/recipes.js +14 -0
  115. package/src/actions/guides/legends/categorical/resolve.js +21 -10
  116. package/src/actions/guides/legends/categorical/symbols.js +2 -3
  117. package/src/actions/guides/legends/continuous/common.js +69 -14
  118. package/src/actions/guides/legends/continuous/gradient.js +111 -83
  119. package/src/actions/guides/legends/continuous/index.js +6 -2
  120. package/src/actions/guides/legends/continuous/interval.js +71 -92
  121. package/src/actions/guides/legends/continuous/opacity.js +105 -84
  122. package/src/actions/guides/legends/creation.js +27 -0
  123. package/src/actions/guides/legends/edit.js +198 -66
  124. package/src/actions/guides/legends/focused.js +2 -2
  125. package/src/actions/guides/legends/lane.js +98 -86
  126. package/src/actions/guides/legends/lifecycle.js +132 -0
  127. package/src/actions/guides/legends/remove.js +31 -76
  128. package/src/actions/guides/legends/size.js +149 -105
  129. package/src/actions/guides/legends/strokeWidth.js +106 -64
  130. package/src/actions/guides/legends/target.js +12 -0
  131. package/src/actions/guides/legends/transition.js +43 -0
  132. package/src/actions/guides/polar/axes/facade.js +77 -62
  133. package/src/actions/guides/polar/axes/labels.js +5 -4
  134. package/src/actions/guides/polar/axes/lines.js +2 -1
  135. package/src/actions/guides/polar/axes/shared.js +6 -0
  136. package/src/actions/guides/polar/axes/ticks.js +5 -4
  137. package/src/actions/guides/polar/axes/titles.js +5 -4
  138. package/src/actions/guides/polar/grids.js +7 -7
  139. package/src/actions/guides/reuse.js +44 -0
  140. package/src/actions/guides/tickValues.js +19 -21
  141. package/src/actions/index.js +2 -0
  142. package/src/actions/intervals/resolve.js +35 -15
  143. package/src/actions/marks/arc/actions.js +1 -1
  144. package/src/actions/marks/area/actions.js +18 -4
  145. package/src/actions/marks/area/materialize.js +5 -2
  146. package/src/actions/marks/bar/materialize.js +11 -7
  147. package/src/actions/marks/index.js +5 -0
  148. package/src/actions/marks/line/actions.js +43 -108
  149. package/src/actions/marks/line/materialize.js +34 -52
  150. package/src/actions/marks/point/edit.js +11 -15
  151. package/src/actions/marks/point/index.js +1 -0
  152. package/src/actions/marks/point/jitter.js +3 -0
  153. package/src/actions/marks/point/materialize.js +106 -2
  154. package/src/actions/marks/point/packing.js +68 -0
  155. package/src/actions/marks/references.js +87 -0
  156. package/src/actions/marks/remove.js +17 -4
  157. package/src/actions/marks/rule/actions.js +34 -3
  158. package/src/actions/marks/rule/appearance.js +26 -0
  159. package/src/actions/marks/shared.js +12 -12
  160. package/src/actions/marks/text/actions.js +224 -6
  161. package/src/actions/marks/text/layout.js +2 -2
  162. package/src/actions/primitives/createGraphics.js +3 -1
  163. package/src/actions/primitives/semantic.js +1 -1
  164. package/src/actions/primitives/semanticAction.js +39 -2
  165. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  166. package/src/actions/primitives/semanticValidation/layer.js +18 -1
  167. package/src/actions/primitives/semanticValidation/scale.js +10 -1
  168. package/src/actions/regression/create.js +1 -1
  169. package/src/actions/regression/edit.js +19 -6
  170. package/src/actions/regression/resolve.js +1 -1
  171. package/src/actions/scales/consumers/common.js +20 -53
  172. package/src/actions/scales/consumers/families.js +27 -14
  173. package/src/actions/scales/consumers/index.js +23 -2
  174. package/src/actions/scales/consumers/seriesLayout.js +33 -2
  175. package/src/actions/scales/create.js +4 -4
  176. package/src/actions/scales/definitions.js +19 -3
  177. package/src/actions/scales/edit.js +46 -34
  178. package/src/actions/scales/editPolicy.js +21 -31
  179. package/src/actions/scales/materialize.js +10 -46
  180. package/src/actions/scales/patch.js +1 -1
  181. package/src/actions/scales/preview.js +39 -0
  182. package/src/actions/scales/quantitativeColor.js +9 -5
  183. package/src/actions/theme/actions.js +43 -0
  184. package/src/actions/theme/index.js +9 -0
  185. package/src/actions/theme/reconcile.js +882 -0
  186. package/src/actions/titles/actions.js +11 -10
  187. package/src/actions/titles/resolve.js +1 -40
  188. package/src/actions/violinPlots/create.js +86 -42
  189. package/src/actions/violinPlots/edit.js +191 -0
  190. package/src/actions/violinPlots/index.js +2 -0
  191. package/src/core/ChartProgram.js +7 -0
  192. package/src/core/action.js +22 -2
  193. package/src/core/compositionState.js +136 -8
  194. package/src/grammar/aggregate.js +66 -23
  195. package/src/grammar/arcs.js +58 -1
  196. package/src/grammar/areaEndpoints.js +46 -0
  197. package/src/grammar/areaSeries.js +87 -41
  198. package/src/grammar/bars/aggregate.js +33 -12
  199. package/src/grammar/bars/policy.js +9 -7
  200. package/src/grammar/bin.js +181 -0
  201. package/src/grammar/categoryOrder.js +34 -0
  202. package/src/grammar/computed.js +150 -0
  203. package/src/grammar/ecdf.js +198 -0
  204. package/src/grammar/facets/index.js +117 -6
  205. package/src/grammar/fold.js +123 -0
  206. package/src/grammar/horizon.js +6 -4
  207. package/src/grammar/interval.js +37 -20
  208. package/src/grammar/lineSeries.js +20 -63
  209. package/src/grammar/markFilter.js +22 -4
  210. package/src/grammar/markLabels.js +93 -0
  211. package/src/grammar/parallelAxisTitles.js +16 -0
  212. package/src/grammar/pathSeries.js +91 -0
  213. package/src/grammar/pointPacking.js +183 -0
  214. package/src/grammar/positionDatum.js +12 -0
  215. package/src/grammar/rects.js +9 -1
  216. package/src/grammar/regression/derive.js +4 -0
  217. package/src/grammar/regression/index.js +7 -2
  218. package/src/grammar/regression/models.js +22 -7
  219. package/src/grammar/regression/parameters.js +40 -13
  220. package/src/grammar/rotation.js +29 -0
  221. package/src/grammar/rules.js +7 -21
  222. package/src/grammar/scales/color.js +23 -1
  223. package/src/grammar/scales/colorConsumers.js +38 -0
  224. package/src/grammar/scales/definition.js +15 -1
  225. package/src/grammar/scales/discretized.js +11 -5
  226. package/src/grammar/scales/fields.js +38 -8
  227. package/src/grammar/scales/mapping.js +2 -0
  228. package/src/grammar/scales/radial.js +55 -0
  229. package/src/grammar/scales/types.js +1 -0
  230. package/src/grammar/schemas/semanticPath.js +17 -3
  231. package/src/grammar/stack.js +170 -0
  232. package/src/grammar/statistics/confidenceInterval.js +101 -0
  233. package/src/grammar/summary.js +183 -0
  234. package/src/grammar/text.js +22 -16
  235. package/src/grammar/timeUnit.js +21 -11
  236. package/src/grammar/transformTopology.js +6 -0
  237. package/src/grammar/transforms.js +36 -0
  238. package/src/grammar/valueFormat.js +112 -0
  239. package/src/grammar/window.js +13 -7
  240. package/src/layout/facets.js +41 -7
  241. package/src/layout/guideCollisions.js +12 -0
  242. package/src/layout/legend.js +2 -10
  243. package/src/layout/legendItems.js +102 -0
  244. package/src/layout/legendLane.js +100 -11
  245. package/src/materialization/bars/aggregate.js +38 -152
  246. package/src/materialization/bars/histogram.js +24 -0
  247. package/src/materialization/categorySlotOffset.js +40 -0
  248. package/src/materialization/compositionSnapshot.js +1 -1
  249. package/src/materialization/dependencies.js +57 -16
  250. package/src/materialization/encodings.js +1 -1
  251. package/src/materialization/facets.js +8 -1
  252. package/src/materialization/guides/layout.js +57 -0
  253. package/src/materialization/guides/resources.js +2 -2
  254. package/src/materialization/legends.js +10 -1
  255. package/src/materialization/marks/capabilities.js +20 -5
  256. package/src/materialization/marks/grouping.js +52 -0
  257. package/src/materialization/marks/index.js +58 -23
  258. package/src/materialization/marks/policies.js +2 -0
  259. package/src/materialization/rect.js +33 -33
  260. package/src/materialization/rowEncoding.js +13 -13
  261. package/src/materialization/scaleGuideDependencies.js +8 -5
  262. package/src/materialization/scales/map.js +1 -0
  263. package/src/materialization/scales/policies/arc.js +42 -0
  264. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  265. package/src/materialization/scales/policies/series.js +2 -1
  266. package/src/materialization/scales/resolve.js +46 -14
  267. package/src/materialization/selection/compatibility.js +11 -0
  268. package/src/materialization/selection/items/bar.js +12 -68
  269. package/src/materialization/selection/items/common.js +8 -2
  270. package/src/materialization/selection/items/path.js +3 -13
  271. package/src/materialization/selection/items/rect.js +7 -4
  272. package/src/materialization/text.js +64 -5
  273. package/src/mcp/adapter.js +2 -2
  274. package/src/selectors/datasets.js +13 -0
  275. package/src/selectors/layers.js +3 -2
  276. package/src/selectors/scales.js +6 -0
  277. package/src/theme/defaults.js +39 -0
  278. package/types/basic.d.ts +11 -2
  279. package/types/index.d.ts +126 -0
  280. package/types/program.d.ts +1320 -135
  281. package/src/materialization/bars/grouped.js +0 -144
@@ -1,4 +1,5 @@
1
- import { deriveBarAggregates } from "../../../grammar/bars/aggregate.js";
1
+ import { deriveSeriesBarCells } from "../../../grammar/bars/aggregate.js";
2
+ import { readSeriesIdentity } from "../../../grammar/pathSeries.js";
2
3
  import {
3
4
  BAR_GRAINS,
4
5
  resolveBarChannels,
@@ -7,7 +8,6 @@ import {
7
8
  resolveBarGrain
8
9
  } from "../../../grammar/bars/policy.js";
9
10
  import { numericExtent } from "../../../grammar/numeric.js";
10
- import { layoutSeriesPartition } from "../../../grammar/seriesLayout.js";
11
11
  import {
12
12
  readNominalField,
13
13
  readTemporalField
@@ -24,7 +24,7 @@ import {
24
24
 
25
25
  function categoryValues(rows, encoding) {
26
26
  return encoding.fieldType === "temporal"
27
- ? readTemporalField(rows, encoding.field)
27
+ ? readTemporalField(rows, encoding.field, encoding.temporalUnit)
28
28
  : readNominalField(rows, encoding.field);
29
29
  }
30
30
 
@@ -36,20 +36,17 @@ function aggregateMembers(rows, layer, cell, channels) {
36
36
  const colors = !["nominal", "ordinal"].includes(color?.fieldType)
37
37
  ? undefined
38
38
  : readNominalField(rows, color.field);
39
+ const identity = layer.encoding?.group === undefined ? undefined : readSeriesIdentity(rows, layer);
39
40
  return rows.filter((_, index) =>
40
41
  categories[index] === categoryValue &&
41
- (colors === undefined || colors[index] === cell.color)
42
+ (identity === undefined ? colors === undefined || colors[index] === cell.color : identity.values[index] === cell.series)
42
43
  );
43
44
  }
44
45
 
45
46
  function aggregateCellDefinitions(program, layer, dataset) {
46
47
  const channels = resolveBarChannels(layer);
47
- const derived = deriveBarAggregates(dataset.values, layer).values;
48
48
  const categoryEncoding = layer.encoding[channels.category];
49
49
  const categoryScale = program.resolvedScales[categoryEncoding.scale];
50
- const measureScale = program.resolvedScales[
51
- layer.encoding[channels.measure].scale
52
- ];
53
50
  const colorEncoding = layer.encoding?.color;
54
51
  const colorScale = program.resolvedScales[colorEncoding?.scale];
55
52
  const offsetChannel = resolveBarOffsetChannel(layer);
@@ -67,70 +64,17 @@ function aggregateCellDefinitions(program, layer, dataset) {
67
64
  ...(cell.color === undefined ? {} : { color: cell.color }),
68
65
  ...(layer.encoding?.[offsetChannel] === undefined
69
66
  ? {}
70
- : { [offsetChannel]: cell.color })
67
+ : { [offsetChannel]: cell.series ?? cell.color })
71
68
  },
72
69
  members
73
70
  };
74
71
  }
75
72
 
76
- if (colorEncoding?.fieldType === "quantitative") {
77
- const cells = new Map(derived.map(cell => [
78
- cell[channels.category],
79
- cell
80
- ]));
81
- const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
82
- ? categoryScale.domain
83
- : [...new Set(derived.map(cell => cell[channels.category]))]
84
- .sort((left, right) => left - right);
85
- return categories.flatMap(category => {
86
- const cell = cells.get(category);
87
- return cell === undefined
88
- ? []
89
- : [definition(cell, measureScale.domain[0], cell[channels.measure])];
90
- });
91
- }
92
-
93
- if (layout === "group" && offsetScale !== undefined) {
94
- const categoryIndex = new Map(
95
- categoryScale.domain.map((value, index) => [value, index])
96
- );
97
- const offsetIndex = new Map(
98
- offsetScale.domain.map((value, index) => [value, index])
99
- );
100
- const cells = [...derived].sort((left, right) =>
101
- categoryIndex.get(left[channels.category]) -
102
- categoryIndex.get(right[channels.category]) ||
103
- offsetIndex.get(left.color) - offsetIndex.get(right.color)
104
- );
105
- return cells.map(cell => definition(
106
- cell,
107
- measureScale.domain[0],
108
- cell[channels.measure]
109
- ));
110
- }
111
-
112
- const categories = ["ordinal", "band", "point"].includes(categoryScale.type)
113
- ? categoryScale.domain
114
- : [...new Set(derived.map(cell => cell[channels.category]))]
115
- .sort((left, right) => left - right);
116
- const series = colorScale?.domain ?? [undefined];
117
- const lookup = new Map(derived.map(cell => [
118
- JSON.stringify([cell[channels.category], cell.color]),
119
- cell
120
- ]));
121
- const baseline = layout === "overlay" ? measureScale.domain[0] : 0;
122
- return categories.flatMap(category => {
123
- const cells = series.map(color =>
124
- lookup.get(JSON.stringify([category, color]))
125
- );
126
- const values = cells.map(cell => cell?.[channels.measure] ?? 0);
127
- return layoutSeriesPartition(values, layout, { baseline }).flatMap(segment => {
128
- const cell = cells[segment.index];
129
- return cell === undefined
130
- ? []
131
- : [definition(cell, segment.start, segment.end)];
132
- });
133
- });
73
+ const categories = categoryScale.type === "time" ? undefined : categoryScale.domain;
74
+ const series = layout === "group" ? offsetScale?.domain
75
+ : layer.encoding.group === undefined && colorEncoding?.fieldType !== "quantitative" ? colorScale?.domain : undefined;
76
+ return deriveSeriesBarCells(dataset.values, layer, categories, series)
77
+ .map(({ cell, start, end }) => definition(cell, start, end));
134
78
  }
135
79
 
136
80
  function histogramDefinitions(program, layer, dataset) {
@@ -145,7 +89,7 @@ function histogramDefinitions(program, layer, dataset) {
145
89
  });
146
90
  const colorScale = program.resolvedScales[colorEncoding?.scale];
147
91
  return segments.map(segment => {
148
- const colorValue = colorScale?.domain[segment.category];
92
+ const colorValue = segment.colorValue ?? colorScale?.domain[segment.category];
149
93
  return {
150
94
  fields: uniqueFields(segment.members),
151
95
  channels: {
@@ -1,3 +1,4 @@
1
+ import { normalizeTemporalValue } from "../../../grammar/scales/fields.js";
1
2
  import { cloneAndFreeze, isPlainObject } from "../../../core/immutable.js";
2
3
  import {
3
4
  MARK_GRAPHIC_PROPERTIES,
@@ -16,8 +17,13 @@ function graphicId(layer, index) {
16
17
 
17
18
  function encodingValue(row, encoding) {
18
19
  if (encoding === undefined) return undefined;
19
- if (Object.hasOwn(encoding, "field")) return row?.[encoding.field];
20
- return encoding.datum;
20
+ const value = Object.hasOwn(encoding, "field") ? row?.[encoding.field] : encoding.datum;
21
+ if (encoding.fieldType !== "temporal") return value;
22
+ try {
23
+ return normalizeTemporalValue(value, encoding.field, 0, encoding.temporalUnit);
24
+ } catch {
25
+ return undefined;
26
+ }
21
27
  }
22
28
 
23
29
  export function channelMapFromRow(row, layer) {
@@ -25,20 +25,10 @@ function rowsForSeries(rows, key) {
25
25
 
26
26
  function seriesDefinitions(layer, rows, series) {
27
27
  return series.map(item => {
28
- const members = rowsForSeries(rows, item.key);
28
+ const members = item.sourceIndices === undefined ? rowsForSeries(rows, item.key)
29
+ : item.sourceIndices.map(index => rows[index]);
29
30
  const fields = { ...uniqueFields(members), ...item.key };
30
- const channels = Object.fromEntries(
31
- Object.entries(layer.encoding ?? {}).flatMap(([channel, encoding]) => {
32
- if (encoding.field === undefined) {
33
- return Object.hasOwn(encoding, "datum")
34
- ? [[channel, encoding.datum]]
35
- : [];
36
- }
37
- return Object.hasOwn(fields, encoding.field)
38
- ? [[channel, fields[encoding.field]]]
39
- : [];
40
- })
41
- );
31
+ const channels = channelMapFromRow(fields, layer);
42
32
  return { fields, channels, members };
43
33
  });
44
34
  }
@@ -1,10 +1,12 @@
1
+ import { rectUsesFields } from "../../../grammar/rects.js";
1
2
  import { resolveRectRows } from "../../rect.js";
2
3
  import {
3
4
  channelMapFromRow,
4
5
  concreteProperties,
5
6
  finalizeItems,
6
7
  itemKey,
7
- ownFields
8
+ ownFields,
9
+ uniqueFields
8
10
  } from "./common.js";
9
11
 
10
12
  function resolveGradientPlotItems(program, layer, dataset) {
@@ -33,12 +35,13 @@ export function resolveRectItems(program, layer, dataset) {
33
35
  return resolveGradientPlotItems(program, layer, dataset);
34
36
  }
35
37
  const rows = resolveRectRows(program, layer, dataset);
38
+ const hasField = rectUsesFields(layer);
36
39
  const definitions = rows.map((item, graphicIndex) => ({
37
40
  key: itemKey(layer, "rect", item.sourceIndex),
38
- fields: ownFields(item.row),
39
- channels: item.channels,
41
+ fields: hasField ? ownFields(item.row) : uniqueFields(dataset.values),
42
+ channels: channelMapFromRow(item.row, layer),
40
43
  properties: concreteProperties(graphic.items[graphicIndex]?.properties),
41
- members: [item.row]
44
+ members: hasField ? [item.row] : dataset.values
42
45
  }));
43
46
  return finalizeItems(program, layer, "rect", definitions, "rect");
44
47
  }
@@ -1,5 +1,8 @@
1
1
  import { resolveBarChannels } from "../grammar/bars/policy.js";
2
+ import { resolveMarkLabelValues } from "../grammar/markLabels.js";
2
3
  import { formatTextValue } from "../grammar/text.js";
4
+ import { normalizePositionDatum } from "../grammar/positionDatum.js";
5
+ import { mapOrdinalPositionValues } from "../grammar/scales/index.js";
3
6
  import { findDataset } from "../selectors/datasets.js";
4
7
  import { findLayer } from "../selectors/layers.js";
5
8
  import { resolveMarkItems } from "./selection/policies/index.js";
@@ -7,6 +10,14 @@ import { mapScaleConsumerValues } from "./scales/map.js";
7
10
  import { resolveRowEncodingValues } from "./rowEncoding.js";
8
11
 
9
12
  function sourceValue(item, source, field) {
13
+ const measure = source.mark.type === "bar" ? resolveBarChannels(source).measure
14
+ : source.mark.type === "arc" ? "radius" : undefined;
15
+ const encoding = source.encoding?.[measure];
16
+ if (encoding?.field === field && encoding.aggregate != null && item.channels[measure] !== undefined) {
17
+ return source.mark.type === "bar"
18
+ ? item.channels[`${measure}2`] ?? item.channels[measure]
19
+ : item.channels[measure];
20
+ }
10
21
  if (Object.hasOwn(item.fields, field)) return item.fields[field];
11
22
  for (const [channel, encoding] of Object.entries(source.encoding ?? {})) {
12
23
  if (encoding?.field !== field || item.channels[channel] === undefined) continue;
@@ -18,6 +29,10 @@ function sourceValue(item, source, field) {
18
29
  if (item.members.length === 1 && Object.hasOwn(item.members[0], field)) {
19
30
  return item.members[0][field];
20
31
  }
32
+ if (source.mark.type === "line" && item.members.length > 0) {
33
+ const endpoint = item.members.at(-1);
34
+ if (Object.hasOwn(endpoint, field)) return endpoint[field];
35
+ }
21
36
  return undefined;
22
37
  }
23
38
 
@@ -54,6 +69,17 @@ function sourceAnchor(program, source, item) {
54
69
  y: item.properties.y2
55
70
  };
56
71
  }
72
+ if (source.mark.type === "line") {
73
+ const graphicId = item.graphicIds?.[0];
74
+ const graphic = program.graphicSpec.objects[source.id]?.items?.find(
75
+ candidate => candidate.id === graphicId
76
+ );
77
+ const commands = graphic?.properties?.commands;
78
+ const endpoint = Array.isArray(commands)
79
+ ? [...commands].reverse().find(command => Number.isFinite(command.x) && Number.isFinite(command.y))
80
+ : undefined;
81
+ return { x: endpoint?.x, y: endpoint?.y };
82
+ }
57
83
  return { x: item.properties.x, y: item.properties.y };
58
84
  }
59
85
 
@@ -122,12 +148,14 @@ function resolveSourceTextItems(program, layer, config) {
122
148
  throw new Error(`Text mark "${layer.id}" requires source layer "${layer.source}".`);
123
149
  }
124
150
  const items = resolveMarkItems(program, source.id);
125
- return items.flatMap(item => {
151
+ const values = layer.encoding.text.content === undefined ? undefined
152
+ : resolveMarkLabelValues(source, items, layer.encoding.text);
153
+ return items.flatMap((item, index) => {
126
154
  const anchor = sourceAnchor(program, source, item);
127
155
  const concrete = concreteItem(
128
156
  sourceTextConfig(config, source, item),
129
157
  anchor,
130
- contentValue(layer.encoding.text, item, source),
158
+ values === undefined ? contentValue(layer.encoding.text, item, source) : values[index],
131
159
  layer.encoding.text.format
132
160
  );
133
161
  return concrete === undefined ? [] : [{ graphic: concrete, anchor }];
@@ -139,10 +167,41 @@ function resolveRowTextItems(program, layer, config) {
139
167
  if (dataset === undefined) {
140
168
  throw new Error(`Text mark "${layer.id}" requires an existing dataset.`);
141
169
  }
142
- const x = resolveRowEncodingValues(program, layer, dataset, "x");
143
- const y = resolveRowEncodingValues(program, layer, dataset, "y");
170
+ const encodings = [layer.encoding?.x, layer.encoding?.y, layer.encoding?.text];
171
+ const length = encodings.some(encoding => encoding !== undefined && Object.hasOwn(encoding, "field"))
172
+ ? dataset.values.length
173
+ : 1;
174
+ const positionValues = channel => {
175
+ const encoding = layer.encoding?.[channel];
176
+ if (encoding === undefined) return undefined;
177
+ if (Object.hasOwn(encoding, "field")) {
178
+ return resolveRowEncodingValues(program, layer, dataset, channel);
179
+ }
180
+ const scale = program.resolvedScales[encoding.scale];
181
+ if (scale === undefined) {
182
+ throw new Error(
183
+ `text mark "${layer.id}" requires resolved ${channel} scale "${encoding.scale}".`
184
+ );
185
+ }
186
+ const datum = normalizePositionDatum(
187
+ encoding.datum,
188
+ encoding.fieldType,
189
+ channel,
190
+ encoding.temporalUnit,
191
+ "Text"
192
+ );
193
+ const mapped = ["nominal", "ordinal"].includes(encoding.fieldType)
194
+ ? mapOrdinalPositionValues([datum], scale)
195
+ : mapScaleConsumerValues([datum], scale, channel);
196
+ return Array.from({ length }, () => mapped[0]);
197
+ };
198
+ const x = positionValues("x");
199
+ const y = positionValues("y");
144
200
  if (x === undefined || y === undefined) return [];
145
- return dataset.values.flatMap((row, index) => {
201
+ const rows = length === 1 && dataset.values.length === 0
202
+ ? [undefined]
203
+ : dataset.values.slice(0, length);
204
+ return rows.flatMap((row, index) => {
146
205
  const concrete = concreteItem(
147
206
  config,
148
207
  { x: x[index], y: y[index] },
@@ -203,8 +203,8 @@ export function readKnowledgeResource(uri, { allowedDocs = [] } = {}) {
203
203
  throw new KnowledgeResourceError(`Unknown knowledge resource URI: ${uri}`);
204
204
  }
205
205
 
206
- if (cardsArtifact.schemaVersion !== 2 || resourcesArtifact.schemaVersion !== 2) {
207
- throw new Error("MCP action cards and resources must use schemaVersion 2.");
206
+ if (cardsArtifact.schemaVersion !== 3 || resourcesArtifact.schemaVersion !== 2) {
207
+ throw new Error("MCP action cards must use schemaVersion 3 and resources must use schemaVersion 2.");
208
208
  }
209
209
  if (resourcesArtifact.packageVersion !== cardsArtifact.packageVersion) {
210
210
  throw new Error("MCP action cards and resources must use one packageVersion.");
@@ -15,3 +15,16 @@ export function requireDataset(program, id, label = `Dataset "${id}"`) {
15
15
  if (dataset === undefined) throw new Error(`${label} does not exist.`);
16
16
  return dataset;
17
17
  }
18
+
19
+ export function requireMaterializedDataset(program, id) {
20
+ const dataset = findDataset(program, id);
21
+ if (dataset === undefined) throw new Error(`Unknown dataset "${id}".`);
22
+ if (!Array.isArray(dataset.values)) {
23
+ throw new Error(
24
+ `Dataset "${id}" requires materialized values. ` +
25
+ "createDerivedData stores a definition only; use the corresponding " +
26
+ "value-producing data action before creating a chart or mark."
27
+ );
28
+ }
29
+ return dataset;
30
+ }
@@ -16,17 +16,18 @@ export function resolveEligibleLayer(program, {
16
16
  target,
17
17
  predicate,
18
18
  label,
19
+ targetOption = "target",
19
20
  current = program.context.currentMark
20
21
  }) {
21
22
  const candidates = program.semanticSpec.layers.filter(predicate);
22
23
  if (target !== undefined) {
23
24
  const selected = candidates.find(layer => layer.id === target);
24
- if (selected === undefined) throw new Error(`Unknown ${label} target "${target}".`);
25
+ if (selected === undefined) throw new Error(`Unknown ${label} ${targetOption} "${target}".`);
25
26
  return selected;
26
27
  }
27
28
  const selected = candidates.find(layer => layer.id === current);
28
29
  if (selected !== undefined) return selected;
29
30
  if (candidates.length === 1) return candidates[0];
30
31
  if (candidates.length === 0) throw new Error(`${label} requires an eligible layer.`);
31
- throw new Error(`${label} target is ambiguous; provide target.`);
32
+ throw new Error(`${label} ${targetOption} is ambiguous; provide ${targetOption}.`);
32
33
  }
@@ -19,3 +19,9 @@ export function requireResolvedScale(program, id, type) {
19
19
  }
20
20
  return scale;
21
21
  }
22
+
23
+ export function hasSemanticScaleReferences(spec, id) {
24
+ const referenced = value => value !== null && typeof value === "object" && Object.entries(value).some(([key, child]) =>
25
+ (key === "scale" && child === id) || (key === "scales" && Array.isArray(child) && child.includes(id)) || referenced(child));
26
+ return referenced(spec.layers) || referenced(spec.guides);
27
+ }
@@ -13,5 +13,44 @@ export const DEFAULT_COLORS = Object.freeze({
13
13
  border: "#cbd5e1",
14
14
  sizeSymbol: "#94a3b8",
15
15
  regressionBand: "#111111",
16
+ boxLine: "#111111",
17
+ boxMedian: "#1f2937",
18
+ referenceLine: "#64748b",
19
+ referenceBand: "#94a3b8",
20
+ gradientCenter: "#0f172a",
16
21
  highlight: "#dc2626"
17
22
  });
23
+
24
+ export const THEME_NAMES = Object.freeze(["light", "dark"]);
25
+
26
+ export const THEME_TOKENS = Object.freeze({
27
+ light: Object.freeze({
28
+ background: "white",
29
+ ...DEFAULT_COLORS,
30
+ fontFamily: DEFAULT_FONT_FAMILY
31
+ }),
32
+ dark: Object.freeze({
33
+ background: "#0f172a",
34
+ mark: "#60a5fa",
35
+ text: "#e2e8f0",
36
+ strongText: "#f8fafc",
37
+ mutedText: "#94a3b8",
38
+ axis: "#cbd5e1",
39
+ axisTitle: "#f1f5f9",
40
+ grid: "#334155",
41
+ border: "#475569",
42
+ sizeSymbol: "#94a3b8",
43
+ regressionBand: "#f8fafc",
44
+ boxLine: "#f8fafc",
45
+ boxMedian: "#f8fafc",
46
+ referenceLine: "#94a3b8",
47
+ referenceBand: "#94a3b8",
48
+ gradientCenter: "#f8fafc",
49
+ highlight: DEFAULT_COLORS.highlight,
50
+ fontFamily: DEFAULT_FONT_FAMILY
51
+ })
52
+ });
53
+
54
+ export function themeTokens(name = "light") {
55
+ return THEME_TOKENS[name];
56
+ }
package/types/basic.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ChartProgram } from "./program.js";
1
+ import type { ChartProgram, BasicSeriesLayoutOptions } from "./program.js";
2
2
 
3
3
  export type {
4
4
  AxisLabelOptions,
@@ -6,7 +6,9 @@ export type {
6
6
  AxisTickOptions,
7
7
  AxisTicksAndLabelsOptions,
8
8
  AxisTitleOptions,
9
+ ApplyThemeOptions,
9
10
  BarWidthOptions,
11
+ BasicSeriesLayoutOptions,
10
12
  Bin2DDataOptions,
11
13
  CanvasOptions,
12
14
  ColorEncodingOptions,
@@ -21,6 +23,7 @@ export type {
21
23
  CreateScatterPlotOptions,
22
24
  CreateScaleOptions,
23
25
  GraphicSpec,
26
+ GroupEncodingOptions,
24
27
  GridDirectionOptions,
25
28
  HistogramEncodingOptions,
26
29
  LegendOptions,
@@ -30,6 +33,8 @@ export type {
30
33
  SecondaryPositionEncodingOptions,
31
34
  SemanticSpec,
32
35
  StrokeDashEncodingOptions,
36
+ TemporalInputUnit,
37
+ ThemeName,
33
38
  TraceNode,
34
39
  XAxisPosition,
35
40
  XOffsetEncodingOptions,
@@ -47,6 +52,8 @@ type BasicStateKey =
47
52
 
48
53
  type BasicMethodKey =
49
54
  | "createCanvas"
55
+ | "applyTheme"
56
+ | "removeTheme"
50
57
  | "createData"
51
58
  | "createPointMark"
52
59
  | "createLineMark"
@@ -57,6 +64,7 @@ type BasicMethodKey =
57
64
  | "encodeX2"
58
65
  | "encodeY2"
59
66
  | "encodeGroup"
67
+ | "encodePointRadius"
60
68
  | "encodeColor"
61
69
  | "encodeStrokeDash"
62
70
  | "encodeSize"
@@ -97,7 +105,8 @@ type RebindMethod<T> = T extends (...args: infer Args) => ChartProgram
97
105
 
98
106
  export type BasicChartProgram =
99
107
  Pick<ChartProgram, BasicStateKey> &
100
- { readonly [Key in BasicMethodKey]: RebindMethod<ChartProgram[Key]> };
108
+ { readonly [Key in BasicMethodKey]: RebindMethod<ChartProgram[Key]> } &
109
+ { readonly layoutSeries: (options: BasicSeriesLayoutOptions) => BasicChartProgram };
101
110
 
102
111
  export function chart(): BasicChartProgram;
103
112
  export function render(