ggaction 0.0.21 → 0.0.23

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 (265) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +83 -90
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/intent-taxonomy.json +1 -1
  6. package/knowledge/mcp-resources.json +1 -1
  7. package/package.json +11 -4
  8. package/src/BasicChartProgram.js +1 -1
  9. package/src/accessibility.js +1 -0
  10. package/src/actions/boxPlots/components.js +1 -96
  11. package/src/actions/boxPlots/create.js +6 -1
  12. package/src/actions/boxPlots/edit.js +8 -0
  13. package/src/actions/boxPlots/materialize.js +3 -0
  14. package/src/actions/boxPlots/options.js +1 -1
  15. package/src/actions/canvas/actions.js +2 -1
  16. package/src/actions/canvas/index.js +3 -0
  17. package/src/actions/canvas/plotBorder.js +2 -1
  18. package/src/actions/canvas/plotOrder.js +14 -0
  19. package/src/actions/charts/area.js +1 -1
  20. package/src/actions/charts/bar.js +4 -4
  21. package/src/actions/charts/density.js +1 -1
  22. package/src/actions/charts/ecdf.js +1 -1
  23. package/src/actions/charts/endpoints.js +54 -5
  24. package/src/actions/charts/heatmap.js +1 -1
  25. package/src/actions/charts/histogram.js +1 -1
  26. package/src/actions/charts/horizon.js +1 -1
  27. package/src/actions/charts/index.js +2 -1
  28. package/src/actions/charts/interval-regression.js +3 -3
  29. package/src/actions/charts/line.js +1 -1
  30. package/src/actions/charts/radar.js +63 -3
  31. package/src/actions/charts/raincloud.js +37 -6
  32. package/src/actions/charts/rug-strip.js +2 -2
  33. package/src/actions/charts/scatter.js +3 -3
  34. package/src/actions/charts/text.js +1 -1
  35. package/src/actions/composition/actions.js +10 -3
  36. package/src/actions/coordinates/actions.js +2 -0
  37. package/src/actions/data/basic.js +6 -0
  38. package/src/actions/data/derived.js +6 -2
  39. package/src/actions/data/edit.js +22 -36
  40. package/src/actions/data/filter.js +1 -376
  41. package/src/actions/data/flatten.js +19 -0
  42. package/src/actions/data/index.js +6 -0
  43. package/src/actions/data/lookup.js +50 -0
  44. package/src/actions/data/raincloudFilter.js +56 -0
  45. package/src/actions/data/shared.js +10 -0
  46. package/src/actions/data/timeUnit.js +1 -1
  47. package/src/actions/distributions/revision.js +3 -1
  48. package/src/actions/encodings/angle.js +2 -1
  49. package/src/actions/encodings/appearance.js +38 -13
  50. package/src/actions/encodings/channels.js +14 -3
  51. package/src/actions/encodings/color/continuous.js +26 -10
  52. package/src/actions/encodings/color/index.js +50 -20
  53. package/src/actions/encodings/color/policy.js +57 -4
  54. package/src/actions/encodings/parallel.js +4 -0
  55. package/src/actions/encodings/position/policies/bar.js +10 -6
  56. package/src/actions/encodings/position/policies/line.js +1 -152
  57. package/src/actions/encodings/position/resolve.js +4 -1
  58. package/src/actions/encodings/ranged.js +46 -0
  59. package/src/actions/encodings/remove.js +1 -1
  60. package/src/actions/encodings/ruleAppearance.js +7 -6
  61. package/src/actions/encodings/shared.js +14 -2
  62. package/src/actions/encodings/stroke.js +20 -14
  63. package/src/actions/encodings/strokeDash.js +9 -11
  64. package/src/actions/encodings/text.js +30 -2
  65. package/src/actions/errorBars/edit.js +24 -4
  66. package/src/actions/facets/actions.js +1 -793
  67. package/src/actions/facets/derive.js +34 -5
  68. package/src/actions/facets/replay.js +1 -38
  69. package/src/actions/gradientPlots/components.js +21 -9
  70. package/src/actions/gradientPlots/create.js +4 -2
  71. package/src/actions/gradientPlots/edit.js +18 -1
  72. package/src/actions/gradientPlots/materialize.js +35 -36
  73. package/src/actions/gradientPlots/options.js +1 -1
  74. package/src/actions/gradientPlots/paint.js +13 -7
  75. package/src/actions/guides/applicability.js +2 -0
  76. package/src/actions/guides/axes/axes.js +39 -0
  77. package/src/actions/guides/axes/axis.js +9 -5
  78. package/src/actions/guides/axes/edit.js +11 -8
  79. package/src/actions/guides/axes/labels.js +24 -8
  80. package/src/actions/guides/axes/parallel/policy.js +3 -5
  81. package/src/actions/guides/axes/parallel/resolve.js +4 -3
  82. package/src/actions/guides/axes/policy.js +22 -0
  83. package/src/actions/guides/axes/tickGroups.js +4 -2
  84. package/src/actions/guides/axes/ticks.js +22 -4
  85. package/src/actions/guides/axes/titles.js +19 -6
  86. package/src/actions/guides/facadeAxes.js +3 -0
  87. package/src/actions/guides/grids/grid.js +3 -1
  88. package/src/actions/guides/grids/resolve.js +7 -11
  89. package/src/actions/guides/guides.js +21 -1
  90. package/src/actions/guides/legends/categorical/actions.js +1 -515
  91. package/src/actions/guides/legends/categorical/components.js +1 -4
  92. package/src/actions/guides/legends/categorical/layout.js +5 -2
  93. package/src/actions/guides/legends/categorical/options.js +4 -0
  94. package/src/actions/guides/legends/categorical/resolve.js +8 -3
  95. package/src/actions/guides/legends/categorical/symbols.js +3 -3
  96. package/src/actions/guides/legends/colorSize.js +2 -0
  97. package/src/actions/guides/legends/continuous/common.js +1 -1
  98. package/src/actions/guides/legends/continuous/gradient.js +1 -360
  99. package/src/actions/guides/legends/continuous/interval.js +1 -1
  100. package/src/actions/guides/legends/continuous/opacity.js +15 -1
  101. package/src/actions/guides/legends/edit.js +1 -1
  102. package/src/actions/guides/legends/focused.js +1 -1
  103. package/src/actions/guides/legends/lane.js +1 -1
  104. package/src/actions/guides/legends/sampling.js +1 -140
  105. package/src/actions/guides/legends/strokeWidth.js +3 -2
  106. package/src/actions/guides/legends/transition.js +19 -2
  107. package/src/actions/guides/polar/axes/facade.js +5 -4
  108. package/src/actions/guides/polar/axes/labels.js +5 -4
  109. package/src/actions/guides/polar/axes/titles.js +5 -4
  110. package/src/actions/guides/polar/resolve.js +2 -1
  111. package/src/actions/guides/tickValues.js +7 -7
  112. package/src/actions/marks/arc/actions.js +5 -7
  113. package/src/actions/marks/area/actions.js +9 -3
  114. package/src/actions/marks/bar/create.js +4 -2
  115. package/src/actions/marks/bar/edit.js +9 -6
  116. package/src/actions/marks/bar/materialize.js +5 -3
  117. package/src/actions/marks/dependentReferences.js +87 -0
  118. package/src/actions/marks/index.js +2 -0
  119. package/src/actions/marks/lifecycle.js +2 -1
  120. package/src/actions/marks/line/actions.js +24 -6
  121. package/src/actions/marks/line/materialize.js +7 -3
  122. package/src/actions/marks/point/create.js +5 -4
  123. package/src/actions/marks/point/edit.js +6 -3
  124. package/src/actions/marks/point/jitter.js +6 -6
  125. package/src/actions/marks/point/materialize.js +36 -39
  126. package/src/actions/marks/rect/actions.js +16 -4
  127. package/src/actions/marks/references.js +183 -35
  128. package/src/actions/marks/rule/actions.js +20 -9
  129. package/src/actions/marks/shared.js +2 -1
  130. package/src/actions/marks/text/actions.js +10 -3
  131. package/src/actions/marks/tick/actions.js +11 -2
  132. package/src/actions/primitives/editGraphics.js +28 -3
  133. package/src/actions/primitives/index.js +9 -0
  134. package/src/actions/primitives/semanticAction.js +13 -7
  135. package/src/actions/primitives/semanticValidation/layer.js +20 -0
  136. package/src/actions/primitives/semanticValidation/scale.js +8 -1
  137. package/src/actions/regression/create.js +6 -1
  138. package/src/actions/regression/edit.js +10 -1
  139. package/src/actions/scales/channels.js +1 -1
  140. package/src/actions/scales/consumers/common.js +19 -1
  141. package/src/actions/scales/consumers/families.js +2 -2
  142. package/src/actions/scales/consumers/seriesLayout.js +4 -3
  143. package/src/actions/scales/edit.js +11 -0
  144. package/src/actions/scales/editPolicy.js +5 -3
  145. package/src/actions/scales/preview.js +2 -1
  146. package/src/actions/scales/quantitativeColor.js +2 -4
  147. package/src/actions/selection/actions.js +10 -1
  148. package/src/actions/theme/reconcile.js +37 -9
  149. package/src/core/compositionState.js +26 -7
  150. package/src/core/datasetReferences.js +17 -0
  151. package/src/core/facetAxisPolicy.js +30 -0
  152. package/src/core/immutable.js +1 -0
  153. package/src/core/resourceReferences.js +5 -3
  154. package/src/core/validation.js +4 -0
  155. package/src/core/vocabulary.js +24 -2
  156. package/src/grammar/aggregate.js +1 -0
  157. package/src/grammar/arcs.js +1 -1
  158. package/src/grammar/areaSeries.js +1 -1
  159. package/src/grammar/axisTextPlacement.js +1 -1
  160. package/src/grammar/bars/policy.js +2 -2
  161. package/src/grammar/curveCommands.js +35 -2
  162. package/src/grammar/datasetDependencies.js +97 -0
  163. package/src/grammar/datasetSchema.js +32 -2
  164. package/src/grammar/facetHeaders.js +1 -0
  165. package/src/grammar/facets/dependencies.js +6 -2
  166. package/src/grammar/facets/guides.js +7 -5
  167. package/src/grammar/facets/legendIntent.js +6 -2
  168. package/src/grammar/facets/scales.js +27 -7
  169. package/src/grammar/flatten.js +85 -0
  170. package/src/grammar/histogram.js +13 -1
  171. package/src/grammar/horizon.js +1 -494
  172. package/src/grammar/jitter.js +2 -2
  173. package/src/grammar/legendSpacing.js +45 -0
  174. package/src/grammar/lineMissing.js +90 -0
  175. package/src/grammar/lineSeries.js +1 -376
  176. package/src/grammar/lookup.js +97 -0
  177. package/src/grammar/markFilter.js +4 -2
  178. package/src/grammar/markLabels.js +8 -3
  179. package/src/grammar/numeric.js +1 -1
  180. package/src/grammar/pathCommands.js +1 -1
  181. package/src/grammar/pathSeries.js +1 -109
  182. package/src/grammar/pointPolygon.js +81 -0
  183. package/src/grammar/pointShapes.js +15 -15
  184. package/src/grammar/rects.js +1 -1
  185. package/src/grammar/regression/derive.js +1 -195
  186. package/src/grammar/regression/parameters.js +7 -0
  187. package/src/grammar/scales/appearance.js +1 -1
  188. package/src/grammar/scales/color.js +1 -1
  189. package/src/grammar/scales/colorConsumers.js +1 -63
  190. package/src/grammar/scales/continuous.js +58 -1
  191. package/src/grammar/scales/definition.js +1 -1
  192. package/src/grammar/scales/ordinal.js +5 -2
  193. package/src/grammar/scales/policies.js +3 -3
  194. package/src/grammar/schemas/graphicBounds.js +40 -12
  195. package/src/grammar/schemas/graphicClip.js +23 -0
  196. package/src/grammar/schemas/graphicTree.js +16 -0
  197. package/src/grammar/statisticalReference.js +22 -0
  198. package/src/grammar/text.js +65 -1
  199. package/src/grammar/ticks.js +92 -1
  200. package/src/grammar/timeUnit.js +9 -3
  201. package/src/grammar/transformTopology.js +3 -1
  202. package/src/grammar/transforms.js +24 -0
  203. package/src/inspection.js +1 -495
  204. package/src/inspectionDescriptors.js +1 -1
  205. package/src/layout/canvas.js +1 -1
  206. package/src/layout/facetContentFlow.js +101 -0
  207. package/src/layout/facetHeaderTitles.js +1 -0
  208. package/src/layout/facetTopLane.js +29 -0
  209. package/src/layout/facets.js +1 -1
  210. package/src/layout/guideCollisions.js +9 -3
  211. package/src/layout/legendGroup.js +1 -1
  212. package/src/layout/legendItems.js +26 -10
  213. package/src/layout/legendSpacing.js +66 -0
  214. package/src/layout/plotDrawOrder.js +17 -0
  215. package/src/materialization/bars/histogram.js +8 -2
  216. package/src/materialization/bars/resolve.js +1 -1
  217. package/src/materialization/composition.js +1 -0
  218. package/src/materialization/compositionSnapshot.js +22 -2
  219. package/src/materialization/dataProvenance.js +1 -1
  220. package/src/materialization/facetContentBounds.js +96 -0
  221. package/src/materialization/facetGuides/layout.js +1 -1
  222. package/src/materialization/facetGuides/placement.js +39 -11
  223. package/src/materialization/facetGuides/preparation.js +43 -24
  224. package/src/materialization/facetHeaders.js +1 -1
  225. package/src/materialization/facetTopLane.js +25 -0
  226. package/src/materialization/facets.js +1 -1
  227. package/src/materialization/graphicHierarchy.js +30 -1
  228. package/src/materialization/graphicTranslation.js +2 -0
  229. package/src/materialization/guides/layout.js +1 -1
  230. package/src/materialization/guides/resources.js +1 -1
  231. package/src/materialization/legends.js +1 -1
  232. package/src/materialization/markClip.js +26 -0
  233. package/src/materialization/marks/capabilities.js +3 -2
  234. package/src/materialization/marks/grouping.js +7 -5
  235. package/src/materialization/marks/index.js +4 -2
  236. package/src/materialization/marks/policies.js +4 -0
  237. package/src/materialization/plotDrawOrder.js +44 -0
  238. package/src/materialization/rect.js +11 -3
  239. package/src/materialization/scaleGuideDependencies.js +13 -5
  240. package/src/materialization/selection/items/common.js +1 -1
  241. package/src/materialization/selection/items/path.js +1 -0
  242. package/src/materialization/selection/policies/index.js +2 -1
  243. package/src/materialization/text.js +10 -5
  244. package/src/mcp/cli.js +0 -0
  245. package/src/persistence/validation.js +114 -18
  246. package/src/persistence.js +4 -2
  247. package/src/renderers/canvas/index.js +16 -0
  248. package/src/renderers/canvas/isolated.js +11 -1
  249. package/src/renderers/canvas/native.js +7 -0
  250. package/src/renderers/canvas/scene.js +66 -0
  251. package/src/renderers/canvas/text.js +3 -2
  252. package/src/renderers/svg.js +1 -617
  253. package/src/renderers/svgSerialization.js +643 -0
  254. package/src/renderers/transitionScene.js +67 -0
  255. package/src/scene.js +89 -0
  256. package/src/selectors/axes.js +1 -1
  257. package/src/selectors/datasets.js +1 -0
  258. package/src/selectors/grids.js +9 -0
  259. package/src/selectors/markClip.js +28 -0
  260. package/src/selectors/raincloud.js +7 -0
  261. package/src/version.js +1 -1
  262. package/types/basic.d.ts +7 -0
  263. package/types/index.d.ts +8 -0
  264. package/types/program.d.ts +286 -70
  265. package/types/scene.d.ts +35 -0
@@ -1,152 +1 @@
1
- import {
2
- validateAggregate,
3
- validateAggregateFieldType
4
- } from "../../../../grammar/aggregate.js";
5
- import { emptyPositionPolicy, resolveBin } from "./common.js";
6
-
7
- function rejectStack(args, channel) {
8
- if (args.stack !== undefined) {
9
- throw new Error(`Line ${channel} encoding does not support stack.`);
10
- }
11
- }
12
-
13
- function rejectBin(args, channel) {
14
- if (args.bin !== undefined) {
15
- throw new Error(`Line ${channel} encoding does not support bin.`);
16
- }
17
- }
18
-
19
- export function resolveLinePositionPolicy({
20
- program,
21
- layer,
22
- dataset,
23
- channel,
24
- args,
25
- fieldType
26
- }) {
27
- const polar = ["theta", "radius"].includes(channel);
28
- const config = program.markConfigs[layer.id] ?? {};
29
- if (polar) {
30
- if (config.curve !== undefined && config.curve !== "linear") {
31
- throw new Error("Polar line position currently requires curve \"linear\".");
32
- }
33
- rejectBin(args, channel);
34
- rejectStack(args, channel);
35
- if (args.aggregate !== undefined) {
36
- throw new Error(`Line ${channel} encoding does not support aggregate.`);
37
- }
38
- return emptyPositionPolicy();
39
- }
40
- if (config.closed === true) {
41
- throw new Error("Line closed requires theta/radius Polar position encodings.");
42
- }
43
- const regression = dataset.transform?.some(item => item.type === "regression");
44
- const interval = dataset.transform?.some(item => item.type === "interval");
45
- const windowOutput = dataset.transform?.some(item =>
46
- item.type === "window" &&
47
- item.operations?.some(operation => operation.as === args.field)
48
- );
49
-
50
- if (channel === "x") {
51
- rejectStack(args, "x");
52
- if (args.aggregate !== undefined) {
53
- throw new Error("Line x encoding does not support aggregate.");
54
- }
55
- if (args.bin !== undefined) {
56
- if (fieldType !== "quantitative") {
57
- throw new Error("Binned line x encoding requires a quantitative field.");
58
- }
59
- if (
60
- layer.encoding?.y !== undefined &&
61
- layer.encoding.y.aggregate === undefined
62
- ) {
63
- throw new Error("Binned line x encoding requires an aggregate y encoding.");
64
- }
65
- return {
66
- bin: resolveBin(args.bin),
67
- aggregate: undefined,
68
- stack: undefined
69
- };
70
- }
71
- if (["nominal", "ordinal"].includes(fieldType)) {
72
- if (layer.encoding?.y !== undefined && layer.encoding.y.aggregate === undefined &&
73
- layer.encoding.y.fieldType !== "quantitative") {
74
- throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");
75
- }
76
- return emptyPositionPolicy();
77
- }
78
- const directPair =
79
- layer.encoding?.y?.aggregate === undefined &&
80
- ["quantitative", "temporal"].includes(layer.encoding?.y?.fieldType) &&
81
- fieldType === "quantitative";
82
- const pendingQuantitativePair =
83
- layer.encoding?.y === undefined && fieldType === "quantitative";
84
- if (
85
- fieldType !== "temporal" &&
86
- !(
87
- (regression || interval || directPair || pendingQuantitativePair) &&
88
- fieldType === "quantitative"
89
- )
90
- ) {
91
- throw new Error(
92
- "Line x encoding requires a categorical or temporal field, or a compatible quantitative field."
93
- );
94
- }
95
- return emptyPositionPolicy();
96
- }
97
-
98
- rejectBin(args, "y");
99
- rejectStack(args, "y");
100
- if (layer.encoding?.x?.bin !== undefined && args.aggregate === undefined) {
101
- throw new Error("Binned line x encoding requires an aggregate y encoding.");
102
- }
103
- if (["nominal", "ordinal"].includes(layer.encoding?.x?.fieldType) &&
104
- args.aggregate === undefined && fieldType !== "quantitative") {
105
- throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");
106
- }
107
- const continuousPair =
108
- fieldType === "quantitative" &&
109
- ["quantitative", "temporal", "nominal", "ordinal"].includes(layer.encoding?.x?.fieldType) &&
110
- layer.encoding.x.bin === undefined;
111
- const prospectiveDirect =
112
- args.aggregate === undefined &&
113
- (fieldType === "temporal" ||
114
- (fieldType === "quantitative" &&
115
- (layer.encoding?.x === undefined || continuousPair)));
116
- if (interval || (windowOutput && args.aggregate === undefined) || prospectiveDirect) {
117
- if (!["quantitative", "temporal"].includes(fieldType)) {
118
- throw new Error(
119
- "Direct line y encoding requires a quantitative or temporal field."
120
- );
121
- }
122
- if (args.aggregate !== undefined) {
123
- throw new Error("Direct line y encoding does not support aggregate.");
124
- }
125
- return emptyPositionPolicy();
126
- }
127
- if (regression) {
128
- if (fieldType !== "quantitative") {
129
- throw new Error("Regression line y encoding requires a quantitative field.");
130
- }
131
- if (args.aggregate !== undefined) {
132
- throw new Error("Regression line y encoding does not support aggregate.");
133
- }
134
- return emptyPositionPolicy();
135
- }
136
-
137
- if (layer.encoding?.x !== undefined) {
138
- const compatibleAggregateX =
139
- ["temporal", "nominal", "ordinal"].includes(layer.encoding.x.fieldType) ||
140
- (layer.encoding.x.fieldType === "quantitative" &&
141
- layer.encoding.x.bin !== undefined);
142
- if (!compatibleAggregateX) {
143
- throw new Error(
144
- "Aggregate line y encoding requires a temporal, categorical, or binned quantitative x encoding."
145
- );
146
- }
147
- }
148
-
149
- const aggregate = validateAggregate(args.aggregate);
150
- validateAggregateFieldType(aggregate, fieldType);
151
- return { bin: undefined, aggregate, stack: undefined };
152
- }
1
+ import{validateAggregate as E,validateAggregateFieldType as b}from"../../../../grammar/aggregate.js";import{emptyPositionPolicy as a,resolveBin as h}from"./common.js";import{findSemanticScale as P}from"../../../../selectors/scales.js";import{validateCategoricalColorConsumer as L}from"../../../../grammar/scales/colorConsumers.js";import{validateLineMissingScope as k}from"../../../../grammar/lineMissing.js";function u(d,e){if(d.stack!==void 0)throw new Error(`Line ${e} encoding does not support stack.`)}function p(d,e){if(d.bin!==void 0)throw new Error(`Line ${e} encoding does not support bin.`)}function $({program:d,layer:e,dataset:g,channel:o,args:n,fieldType:i}){k({...e,encoding:{...e.encoding,[o]:{...n,fieldType:i}}});for(const t of["color","strokeDash"]){const r=e.encoding?.[t],l=r===void 0?void 0:P(d,r.scale);l!==void 0&&L({...e,encoding:{...e.encoding,[o]:{...e.encoding[o],...n,fieldType:i}}},r,l,t)}const m=["theta","radius"].includes(o),c=d.markConfigs[e.id]??{};if(m){if(c.curve!==void 0&&c.curve!=="linear")throw new Error('Polar line position currently requires curve "linear".');if(p(n,o),u(n,o),n.aggregate!==void 0)throw new Error(`Line ${o} encoding does not support aggregate.`);return a()}if(c.closed===!0)throw new Error("Line closed requires theta/radius Polar position encodings.");const f=g.transform?.some(t=>t.type==="regression"),s=g.transform?.some(t=>t.type==="interval"),v=g.transform?.some(t=>t.type==="window"&&t.operations?.some(r=>r.as===n.field));if(o==="x"){if(u(n,"x"),n.aggregate!==void 0)throw new Error("Line x encoding does not support aggregate.");if(n.bin!==void 0){if(i!=="quantitative")throw new Error("Binned line x encoding requires a quantitative field.");if(e.encoding?.y!==void 0&&e.encoding.y.aggregate===void 0)throw new Error("Binned line x encoding requires an aggregate y encoding.");return{bin:h(n.bin),aggregate:void 0,stack:void 0}}if(["nominal","ordinal"].includes(i)){if(e.encoding?.y!==void 0&&e.encoding.y.aggregate===void 0&&e.encoding.y.fieldType!=="quantitative")throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");return a()}const t=e.encoding?.y?.aggregate===void 0&&["quantitative","temporal"].includes(e.encoding?.y?.fieldType)&&i==="quantitative",r=e.encoding?.y===void 0&&i==="quantitative";if(i!=="temporal"&&!((f||s||t||r)&&i==="quantitative"))throw new Error("Line x encoding requires a categorical or temporal field, or a compatible quantitative field.");return a()}if(p(n,"y"),u(n,"y"),e.encoding?.x?.bin!==void 0&&n.aggregate===void 0)throw new Error("Binned line x encoding requires an aggregate y encoding.");if(["nominal","ordinal"].includes(e.encoding?.x?.fieldType)&&n.aggregate===void 0&&i!=="quantitative")throw new Error("Categorical line x requires a quantitative value or aggregate y encoding.");const q=i==="quantitative"&&["quantitative","temporal","nominal","ordinal"].includes(e.encoding?.x?.fieldType)&&e.encoding.x.bin===void 0,x=n.aggregate===void 0&&(i==="temporal"||i==="quantitative"&&(e.encoding?.x===void 0||q));if(s||v&&n.aggregate===void 0||x){if(!["quantitative","temporal"].includes(i))throw new Error("Direct line y encoding requires a quantitative or temporal field.");if(n.aggregate!==void 0)throw new Error("Direct line y encoding does not support aggregate.");return a()}if(f){if(i!=="quantitative")throw new Error("Regression line y encoding requires a quantitative field.");if(n.aggregate!==void 0)throw new Error("Regression line y encoding does not support aggregate.");return a()}if(e.encoding?.x!==void 0&&!(["temporal","nominal","ordinal"].includes(e.encoding.x.fieldType)||e.encoding.x.fieldType==="quantitative"&&e.encoding.x.bin!==void 0))throw new Error("Aggregate line y encoding requires a temporal, categorical, or binned quantitative x encoding.");const w=E(n.aggregate);return b(w,i),{bin:void 0,aggregate:w,stack:void 0}}export{$ as resolveLinePositionPolicy};
@@ -1,6 +1,7 @@
1
1
  import { isSourceOwnedText } from "../../../grammar/text.js";
2
2
  import { findSemanticScale } from "../../../selectors/scales.js";
3
3
  import { readAreaEndpoint } from "../../../grammar/areaEndpoints.js";
4
+ import { readLinePosition } from "../../../grammar/lineMissing.js";
4
5
  import { validateUserId } from "../../../core/identifiers.js";
5
6
  import { getPositionCoordinateDefaults } from "../../../grammar/coordinates.js";
6
7
  import {
@@ -272,7 +273,9 @@ export function resolvePositionEncoding(program, channel, args, operation) {
272
273
  "Position scale unknown currently requires a row-owned point mark."
273
274
  );
274
275
  }
275
- if (layer.mark.type === "area" && fieldType === "quantitative") {
276
+ if (layer.mark.type === "line" && layer.mark.missing === "break") {
277
+ readLinePosition(dataset.values, { field, fieldType, temporalUnit });
278
+ } else if (layer.mark.type === "area" && fieldType === "quantitative") {
276
279
  readAreaEndpoint(dataset.values, { ...(hasDatum ? { datum } : { field }), fieldType }, layer.mark.missing);
277
280
  } else if (["rule", "rect", "text", "point", "tick"].includes(layer.mark.type) && hasDatum) {
278
281
  const mark = layer.mark.type[0].toUpperCase() + layer.mark.type.slice(1);
@@ -199,6 +199,39 @@ const encodeY2 = /* @__PURE__ */ action(
199
199
  }
200
200
  );
201
201
 
202
+ function normalizeDatumRangeBound(value, fieldType, operation) {
203
+ if (typeof value === "string") return { field: value };
204
+ validateOptions(value, ["datum"], `${operation} endpoint`);
205
+ if (!Object.hasOwn(value, "datum") || fieldType !== "quantitative") {
206
+ throw new TypeError(`${operation} datum endpoints require a quantitative datum object.`);
207
+ }
208
+ return { datum: normalizePositionDatum(value.datum, fieldType, operation, undefined, "Range") };
209
+ }
210
+
211
+ function prepareDatumRange(program, layer, dataset, channel, args, common, lower, upper) {
212
+ if (Object.hasOwn(upper, "field")) {
213
+ validateSecondaryField(applyRequestedItemMissingPolicy(layer, dataset, upper.field),
214
+ upper.field, common.fieldType, undefined);
215
+ }
216
+ const resolved = resolvePositionEncoding(program, channel, {
217
+ ...common, ...lower,
218
+ ...(args.coordinate === undefined ? {} : { coordinate: args.coordinate }),
219
+ ...(args.scale === undefined ? {} : { scale: args.scale })
220
+ }, channel === "x" ? "encodeXRange" : "encodeYRange");
221
+ const primary = { ...lower, fieldType: common.fieldType, scale: resolved.scale.id };
222
+ const secondary = { ...upper, fieldType: common.fieldType, scale: resolved.scale.id };
223
+ const updated = { ...layer, coordinate: resolved.coordinate.id,
224
+ encoding: { ...layer.encoding, [channel]: primary, [`${channel}2`]: secondary } };
225
+ const coordinates = program.semanticSpec.coordinates;
226
+ resolveScalePreview({ ...program, semanticSpec: { ...program.semanticSpec,
227
+ layers: program.semanticSpec.layers.map(item => item.id === layer.id ? updated : item),
228
+ coordinates: coordinates.some(item => item.id === resolved.coordinate.id)
229
+ ? coordinates : [...coordinates, resolved.coordinate],
230
+ scales: [...program.semanticSpec.scales.filter(item => item.id !== resolved.scale.id), resolved.scale]
231
+ } }, resolved.scale.id);
232
+ return secondary;
233
+ }
234
+
202
235
  function rangeAction(channel) {
203
236
  const primary = channel === "x" ? "encodeX" : "encodeY";
204
237
  const secondary = channel === "x" ? "encodeX2" : "encodeY2";
@@ -213,6 +246,19 @@ function rangeAction(channel) {
213
246
  const { layer, dataset } = resolveTarget(this, args.target, ["area", "bar", "rect", "rule"], "ranged mark");
214
247
  let next = this;
215
248
  let lower = { field: args.lower }, upper = { field: args.upper };
249
+ if (layer.mark.type === "rect" || layer.mark.type === "rule") {
250
+ lower = normalizeDatumRangeBound(args.lower, common.fieldType, op);
251
+ upper = normalizeDatumRangeBound(args.upper, common.fieldType, op);
252
+ if (Object.hasOwn(lower, "datum") || Object.hasOwn(upper, "datum")) {
253
+ common.target = layer.id;
254
+ const prepared = prepareDatumRange(this, layer, dataset, channel, args, common, lower, upper);
255
+ const secondaryChannel = `${channel}2`;
256
+ if (layer.encoding?.[secondaryChannel] !== undefined) {
257
+ next = next.editSemantic({ property: `layer[${layer.id}].encoding.${secondaryChannel}`, remove: true });
258
+ }
259
+ next = setEncodingProperties(next, layer.id, secondaryChannel, prepared);
260
+ }
261
+ }
216
262
  if (layer.mark.type === "bar") {
217
263
  if (layer.mark.orientation !== undefined && channel !== (layer.mark.orientation === "vertical" ? "y" : "x")) {
218
264
  throw new Error("Numeric bar endpoints must use the measure channel.");
@@ -33,7 +33,7 @@ const OPTIONS = Object.freeze(["target", "channel"]);
33
33
  const REMOVABLE_CHANNELS = Object.freeze([
34
34
  "x", "y", "x2", "y2", "xOffset", "yOffset", "theta", "radius",
35
35
  "color", "stroke", "strokeDash", "strokeWidth", "size", "shape", "group",
36
- "angle", "opacity", "text"
36
+ "angle", "opacity", "text", "dx", "dy"
37
37
  ]);
38
38
  const REMOVE_AXIS = Object.freeze({
39
39
  x: "removeXAxis",
@@ -1,3 +1,4 @@
1
+ import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
1
2
  import { action } from "../../core/action.js";
2
3
  import { validateRuleStrokeWidth } from "../../grammar/ruleAppearance.js";
3
4
  import { readQuantitativeField } from "../../grammar/scales/index.js";
@@ -24,7 +25,7 @@ const WIDTH_OPTIONS = Object.freeze([
24
25
  const encodeStrokeWidth = /* @__PURE__ */ action(
25
26
  {
26
27
  op: "encodeStrokeWidth",
27
- description: "Assign constant or field-driven line or rule stroke width."
28
+ description: "Assign constant or field-driven mark stroke width."
28
29
  },
29
30
  function (args = {}) {
30
31
  validateOptions(args, WIDTH_OPTIONS, "encodeStrokeWidth");
@@ -38,7 +39,7 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
38
39
  throw new Error("Constant stroke width does not accept fieldType or scale.");
39
40
  }
40
41
  validateRuleStrokeWidth(args.value);
41
- const { id, layer } = resolveTarget(this, args.target, ["rule", "line"], "rule or line mark");
42
+ const { id, layer } = resolveTarget(this, args.target, APPEARANCE_MARK_TYPES.encodeStrokeWidth, "rule, line or rect mark");
42
43
  assertEncodingSelectionCompatibility(this, id, ["strokeWidth"]);
43
44
  let next = this.guideConfigs.legend?.strokeWidth?.target === id
44
45
  ? this.removeLegend({ target: id, channels: ["strokeWidth"] })
@@ -53,7 +54,7 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
53
54
  ...next.markConfigs[id],
54
55
  strokeWidth: args.value
55
56
  });
56
- if (layer.mark.type === "line") {
57
+ if (["line", "rect"].includes(layer.mark.type)) {
57
58
  return rematerializeEncoding(next, id, "strokeWidth", undefined, layer);
58
59
  }
59
60
  next = next.rematerializeRuleMark({ id });
@@ -62,8 +63,8 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
62
63
  const { id, dataset: sourceDataset, layer } = resolveTarget(
63
64
  this,
64
65
  args.target,
65
- ["rule", "line"],
66
- "rule or line mark"
66
+ APPEARANCE_MARK_TYPES.encodeStrokeWidth,
67
+ "rule, line or rect mark"
67
68
  );
68
69
  const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
69
70
  const fieldType = args.fieldType ?? "quantitative";
@@ -78,7 +79,7 @@ const encodeStrokeWidth = /* @__PURE__ */ action(
78
79
  }
79
80
  validatePathSeriesAppearance(dataset.values, {
80
81
  ...layer, encoding: { ...layer.encoding, strokeWidth: { field: args.field, fieldType } }
81
- });
82
+ }, this.semanticSpec.scales);
82
83
  const previous = layer.encoding?.strokeWidth;
83
84
  const requestedScale = resolveReassignmentScaleOptions(
84
85
  previous,
@@ -1,3 +1,6 @@
1
+ import { editScaleForEncoding } from "../scales/edit.js";
2
+ import { collectResourceReferences } from "../../core/resourceReferences.js";
3
+ import { removeScale } from "../resources/remove.js";
1
4
  import { cartesianAxisOwners } from "../../selectors/axes.js";
2
5
  import { isSourceOwnedText } from "../../grammar/text.js";
3
6
  import { validateUserId } from "../../core/identifiers.js";
@@ -60,6 +63,15 @@ export function resolveReassignmentScaleOptions(encoding, options) {
60
63
  return { ...options, id: encoding.scale };
61
64
  }
62
65
 
66
+ // A fixed-to-field transition may reuse a detached named channel scale with
67
+ // changed options. Recreate it through the existing resource owner, preserving
68
+ // the resolved definition's omitted defaults and never touching live consumers.
69
+ export function releaseDetachedEncodingScale(program, id) {
70
+ if (findSemanticScale(program, id) === undefined ||
71
+ collectResourceReferences(program, { kind: "scale", id }).some(ref => ref.strength === "live")) return program;
72
+ return removeScale.call(program, { id });
73
+ }
74
+
63
75
  export function applyEncodingScale(
64
76
  program,
65
77
  definition,
@@ -71,7 +83,7 @@ export function applyEncodingScale(
71
83
  return program.createScale(definition);
72
84
  }
73
85
  if (existing.type !== definition.type) {
74
- if (reassignment && allowTypeChange) return program.editScale({ ...definition });
86
+ if (reassignment && allowTypeChange) return editScaleForEncoding(program, { ...definition });
75
87
  throw new Error(
76
88
  `Scale "${definition.id}" cannot change type from "${existing.type}" to "${definition.type}".`
77
89
  );
@@ -89,7 +101,7 @@ export function applyEncodingScale(
89
101
 
90
102
  return Object.keys(patch).length === 1 || !reassignment
91
103
  ? program.createScale(definition)
92
- : program.editScale(patch);
104
+ : editScaleForEncoding(program, patch);
93
105
  }
94
106
 
95
107
  export function rebindPositionGuides(
@@ -1,3 +1,4 @@
1
+ import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
1
2
  import { action } from "../../core/action.js";
2
3
  import {
3
4
  readNominalField,
@@ -7,9 +8,10 @@ import {
7
8
  resolveTemporalUnit,
8
9
  validateCategoricalFieldType
9
10
  } from "../../grammar/scales/index.js";
10
- import { validateContinuousColorConsumer } from
11
+ import { validateContinuousColorConsumer, validateTickColorChannels } from
11
12
  "../../grammar/scales/colorConsumers.js";
12
- import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
13
+ import { validatePathGroupAppearance } from "../../materialization/marks/grouping.js";
14
+ import { resolveColorReassignmentScaleOptions } from "./color/policy.js";
13
15
  import { validateRuleStroke } from "../../grammar/ruleAppearance.js";
14
16
  import { assertEncodingSelectionCompatibility } from
15
17
  "../../materialization/selection/compatibility.js";
@@ -20,8 +22,8 @@ import {
20
22
  import { applyTemporalUnit } from "./temporal.js";
21
23
  import {
22
24
  applyEncodingScale,
25
+ releaseDetachedEncodingScale,
23
26
  rematerializeEncoding,
24
- resolveReassignmentScaleOptions,
25
27
  resolveTarget,
26
28
  setEncodingProperties,
27
29
  validateOptions
@@ -32,9 +34,7 @@ import { applyRequestedItemMissingPolicy } from
32
34
  const OPTIONS = Object.freeze([
33
35
  "target", "value", "field", "fieldType", "temporalUnit", "scale"
34
36
  ]);
35
- const MARK_TYPES = Object.freeze([
36
- "point", "line", "area", "bar", "rect", "arc", "rule", "tick"
37
- ]);
37
+ const MARK_TYPES = APPEARANCE_MARK_TYPES.encodeStroke;
38
38
 
39
39
  function withoutConstantStroke(program, layer) {
40
40
  const config = { ...program.markConfigs[layer.id] };
@@ -79,17 +79,17 @@ function hasStrokeLegend(program, target) {
79
79
  );
80
80
  }
81
81
 
82
- function validateStrokeField(dataset, layer, args, scale, temporalUnit) {
82
+ function validateStrokeField(program, dataset, layer, args, scale, temporalUnit) {
83
83
  const { field, fieldType } = args;
84
84
  validateContinuousColorConsumer(layer, args, scale, { channel: "stroke" });
85
85
  if (["line", "area"].includes(layer.mark.type)) {
86
- validatePathSeriesAppearance(dataset.values, {
86
+ validatePathGroupAppearance(program, {
87
87
  ...layer,
88
88
  encoding: {
89
89
  ...layer.encoding,
90
90
  stroke: { field, fieldType, temporalUnit, scale: scale.id }
91
91
  }
92
- });
92
+ }, dataset);
93
93
  }
94
94
  if (Object.hasOwn(scale, "unknown")) {
95
95
  readScaleField(dataset.values, field, fieldType, {
@@ -130,6 +130,7 @@ export const encodeStroke = /* @__PURE__ */ action(
130
130
  MARK_TYPES,
131
131
  "stroke-capable mark"
132
132
  );
133
+ validateTickColorChannels(layer, "stroke");
133
134
 
134
135
  if (hasValue) {
135
136
  const value = validateRuleStroke(args.value);
@@ -160,8 +161,8 @@ export const encodeStroke = /* @__PURE__ */ action(
160
161
  const dataset = applyRequestedItemMissingPolicy(layer, sourceDataset, args.field);
161
162
  const previous = layer.encoding?.stroke;
162
163
  const temporalUnit = resolveTemporalUnit(args, fieldType, previous);
163
- const requestedScale = resolveReassignmentScaleOptions(
164
- previous,
164
+ const requestedScale = resolveColorReassignmentScaleOptions(
165
+ this, layer, "stroke", fieldType,
165
166
  args.scale ?? {}
166
167
  );
167
168
  let scale;
@@ -179,7 +180,7 @@ export const encodeStroke = /* @__PURE__ */ action(
179
180
  readNominalField(dataset.values, args.field);
180
181
  }
181
182
  if (["line", "area"].includes(layer.mark.type)) {
182
- validatePathSeriesAppearance(dataset.values, {
183
+ validatePathGroupAppearance(this, {
183
184
  ...layer,
184
185
  encoding: {
185
186
  ...layer.encoding,
@@ -189,7 +190,7 @@ export const encodeStroke = /* @__PURE__ */ action(
189
190
  scale: scale.id
190
191
  }
191
192
  }
192
- });
193
+ }, dataset);
193
194
  }
194
195
  } else if (["quantitative", "temporal"].includes(fieldType)) {
195
196
  scale = resolveQuantitativeColorScaleDefinition(
@@ -199,17 +200,22 @@ export const encodeStroke = /* @__PURE__ */ action(
199
200
  "stroke"
200
201
  );
201
202
  validateStrokeField(
203
+ this,
202
204
  dataset,
203
205
  layer,
204
206
  { ...args, fieldType },
205
207
  scale,
206
- temporalUnit
208
+ temporalUnit,
209
+ this.semanticSpec.scales
207
210
  );
208
211
  } else {
209
212
  throw new Error(`Unsupported stroke field type "${fieldType}".`);
210
213
  }
211
214
 
212
215
  let next = withoutConstantStroke(this, layer);
216
+ if (this.markConfigs[target]?.stroke !== undefined || this.markConfigs[target]?.barAppearance?.stroke !== undefined) {
217
+ next = releaseDetachedEncodingScale(next, scale.id);
218
+ }
213
219
  next = applyTemporalUnit(next, target, "stroke", temporalUnit, previous);
214
220
  next = setEncodingProperties(next, target, "stroke", {
215
221
  field: args.field,
@@ -1,8 +1,9 @@
1
+ import { APPEARANCE_MARK_TYPES } from "../../core/vocabulary.js";
1
2
  import { action } from "../../core/action.js";
2
3
  import {
3
4
  normalizeStrokeDashPattern,
4
5
  readNominalField,
5
- validateNominalFieldType
6
+ validateCategoricalFieldType
6
7
  } from "../../grammar/scales/index.js";
7
8
  import { resolveStrokeDashScaleDefinition } from "../scales/definitions.js";
8
9
  import {
@@ -14,6 +15,7 @@ import {
14
15
  validateOptions
15
16
  } from "./shared.js";
16
17
  import { findLayer } from "../../selectors/layers.js";
18
+ import { validateCategoricalColorConsumer } from "../../grammar/scales/colorConsumers.js";
17
19
  import { validatePathSeriesAppearance } from "../../grammar/pathSeries.js";
18
20
  import { applyRequestedItemMissingPolicy } from
19
21
  "../../grammar/itemMissing.js";
@@ -63,7 +65,7 @@ function reconcileLegendAfterDashRemoval(program, target) {
63
65
  const encodeStrokeDash = /* @__PURE__ */ action(
64
66
  {
65
67
  op: "encodeStrokeDash",
66
- description: "Encode a nominal field as line stroke dash."
68
+ description: "Encode a categorical field as line stroke dash."
67
69
  },
68
70
  function (args = {}) {
69
71
  validateOptions(args, STROKE_DASH_ENCODING_OPTIONS, "encodeStrokeDash");
@@ -78,7 +80,7 @@ const encodeStrokeDash = /* @__PURE__ */ action(
78
80
  const { id: target, dataset: sourceDataset, layer } = resolveTarget(
79
81
  this,
80
82
  args.target,
81
- ["line", "rule"],
83
+ APPEARANCE_MARK_TYPES.encodeStrokeDash,
82
84
  "line or rule mark"
83
85
  );
84
86
  const dataset = hasField
@@ -88,7 +90,7 @@ const encodeStrokeDash = /* @__PURE__ */ action(
88
90
  normalizeStrokeDashPattern(args.value);
89
91
  validatePathSeriesAppearance(dataset.values, {
90
92
  ...layer, encoding: { ...layer.encoding, strokeDash: { datum: args.value } }
91
- });
93
+ }, this.semanticSpec.scales);
92
94
  let next = layer.encoding?.strokeDash === undefined
93
95
  ? this
94
96
  : this.clearStrokeDashEncoding({ target });
@@ -100,20 +102,16 @@ const encodeStrokeDash = /* @__PURE__ */ action(
100
102
  return rematerializeEncoding(next, target, "strokeDash", undefined, layer);
101
103
  }
102
104
 
103
- const fieldType = validateNominalFieldType(args.fieldType ?? "nominal");
105
+ const fieldType = validateCategoricalFieldType(args.fieldType ?? "nominal");
104
106
  validatePathSeriesAppearance(dataset.values, {
105
107
  ...layer, encoding: { ...layer.encoding, strokeDash: { field: args.field, fieldType } }
106
- });
108
+ }, this.semanticSpec.scales);
107
109
  const previous = layer.encoding?.strokeDash;
108
110
  const requestedScale = previous?.field === args.field
109
111
  ? resolveReassignmentScaleOptions(previous, args.scale ?? {})
110
112
  : args.scale ?? {};
111
113
  const scale = resolveStrokeDashScaleDefinition(this, requestedScale);
112
- if (Object.hasOwn(scale, "unknown")) {
113
- throw new Error(
114
- "StrokeDash scale unknown is not supported for grouped line or rule paths."
115
- );
116
- }
114
+ validateCategoricalColorConsumer(layer, { field: args.field, fieldType }, scale, "strokeDash");
117
115
  readNominalField(dataset.values, args.field);
118
116
 
119
117
  let next = previous === undefined
@@ -1,10 +1,11 @@
1
1
  import { action } from "../../core/action.js";
2
2
  import { schemaField } from "../../grammar/datasetSchema.js";
3
- import { formatTextValue, validateTextFormat } from "../../grammar/text.js";
3
+ import { formatTextValue, validateTextFormat, isIndependentText, resolveTextOffsetDataset, normalizeTextOffsetAssignment } from "../../grammar/text.js";
4
+ import { validateUserId } from "../../core/identifiers.js";
4
5
  import { normalizeMarkLabelContent, resolveMarkLabelValues } from "../../grammar/markLabels.js";
5
6
  import { canMaterializeText } from "../../materialization/marks/index.js";
6
7
  import { resolveMarkItems } from "../../materialization/selection/policies/index.js";
7
- import { findLayer } from "../../selectors/layers.js";
8
+ import { findLayer, resolveEligibleLayer } from "../../selectors/layers.js";
8
9
  import {
9
10
  resolveTarget,
10
11
  setEncodingProperties,
@@ -90,6 +91,33 @@ export const encodeText = /* @__PURE__ */ action(
90
91
  }
91
92
  );
92
93
 
94
+ function createTextOffsetEncoding(channel) {
95
+ const operation = channel === "dx" ? "encodeDx" : "encodeDy";
96
+ return action({ op: operation, description: `Assign direct numeric ${channel} pixel offsets to independent Text.` }, function (args = {}) {
97
+ const properties = normalizeTextOffsetAssignment(args, channel);
98
+ const selected = resolveEligibleLayer(this, {
99
+ target: args.target === undefined ? undefined : validateUserId(args.target, "Text offset target"),
100
+ predicate: isIndependentText,
101
+ label: "Independent Text offset"
102
+ });
103
+ const { id: target, layer, dataset } = resolveTarget(this, selected.id, ["text"], "independent Text");
104
+ resolveTextOffsetDataset({ ...layer, encoding: { ...layer.encoding, [channel]: properties } }, dataset);
105
+ let next = this;
106
+ for (const property of ["field", "fieldType", "datum"]) {
107
+ if (Object.hasOwn(layer.encoding?.[channel] ?? {}, property)) {
108
+ next = next.editSemantic({ property: `layer[${target}].encoding.${channel}.${property}`, remove: true });
109
+ }
110
+ }
111
+ next = setEncodingProperties(next, target, channel, properties);
112
+ return canMaterializeText(next, findLayer(next, target)) ? next.rematerializeTextMark({ id: target }) : next;
113
+ });
114
+ }
115
+
116
+ export const encodeDx = /* @__PURE__ */ createTextOffsetEncoding("dx");
117
+ export const encodeDy = /* @__PURE__ */ createTextOffsetEncoding("dy");
118
+
93
119
  export function registerTextEncodingAction(ProgramClass) {
94
120
  ProgramClass.prototype.encodeText = encodeText;
121
+ ProgramClass.prototype.encodeDx = encodeDx;
122
+ ProgramClass.prototype.encodeDy = encodeDy;
95
123
  }
@@ -266,8 +266,8 @@ function updateErrorBarOffsets(program, ids, current, candidate) {
266
266
  return next;
267
267
  }
268
268
 
269
- function updateErrorBarRoles(program, owner, current, candidate, dataId, capIds) {
270
- const owned = [owner.id, ...capIds];
269
+ function updateErrorBarRoles(program, owner, current, candidate, dataId, capIds, centerIds) {
270
+ const owned = [owner.id, ...capIds, ...centerIds];
271
271
  let next = updateErrorBarOffsets(
272
272
  program, owned, current, { ...candidate, offset: undefined }
273
273
  );
@@ -301,6 +301,18 @@ function updateErrorBarRoles(program, owner, current, candidate, dataId, capIds)
301
301
  }
302
302
  });
303
303
  }
304
+ for (const id of centerIds) {
305
+ updated = setCartesianPosition(updated, id, categoryChannel, {
306
+ field: candidate.position.field,
307
+ fieldType: candidate.position.fieldType,
308
+ scale: category.scale
309
+ });
310
+ updated = setCartesianPosition(updated, id, measureChannel, {
311
+ field: candidate.fields.center,
312
+ fieldType: "quantitative",
313
+ scale: measure.scale
314
+ });
315
+ }
304
316
  return updated;
305
317
  }
306
318
  });
@@ -411,7 +423,12 @@ export const editErrorBar = /* @__PURE__ */ closedAction(
411
423
  const capIds = errorBarCaps(current)
412
424
  .map(([id]) => id)
413
425
  .filter(id => findLayer(this, id) !== undefined);
414
- const consumers = collectIntervalConsumers(this, [owner.id, ...capIds]);
426
+ // createIntervalPlot records the center's ownership explicitly. It must
427
+ // follow the interval revision just like caps and source-owned labels.
428
+ const centerIds = this.semanticSpec.layers.filter(layer =>
429
+ this.markConfigs[layer.id]?.intervalPlot?.intervalId === owner.id
430
+ ).map(layer => layer.id);
431
+ const consumers = collectIntervalConsumers(this, [owner.id, ...capIds, ...centerIds]);
415
432
  const interval = roleRequested
416
433
  ? planIntervalRoleData(this, {
417
434
  owner: owner.id,
@@ -442,9 +459,12 @@ export const editErrorBar = /* @__PURE__ */ closedAction(
442
459
  });
443
460
  next = roleRequested
444
461
  ? updateErrorBarRoles(
445
- next, owner, current, candidate, interval.dataId, capIds
462
+ next, owner, current, candidate, interval.dataId, capIds, centerIds
446
463
  )
447
464
  : next.rematerializeErrorBar({ id: owner.id });
465
+ if (interval.changed || roleRequested) {
466
+ for (const id of centerIds) next = next.rematerializePointMark({ id });
467
+ }
448
468
  return releaseIntervalRevision(next, interval);
449
469
  }
450
470
  );